mirror of
https://github.com/Casvt/MIND.git
synced 2026-04-03 03:00:22 -04:00
Fixes issue #6
This commit is contained in:
109
backend/db.py
109
backend/db.py
@@ -8,7 +8,7 @@ from typing import Union
|
||||
|
||||
from flask import g
|
||||
|
||||
__DATABASE_VERSION__ = 4
|
||||
__DATABASE_VERSION__ = 5
|
||||
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
@@ -100,6 +100,87 @@ def migrate_db(current_db_version: int) -> None:
|
||||
""")
|
||||
current_db_version = 4
|
||||
|
||||
if current_db_version == 4:
|
||||
# V4 -> V5
|
||||
cursor.executescript("""
|
||||
BEGIN TRANSACTION;
|
||||
PRAGMA defer_foreign_keys = ON;
|
||||
|
||||
-- Reminders
|
||||
INSERT INTO reminder_services(reminder_id, notification_service_id)
|
||||
SELECT id, notification_service
|
||||
FROM reminders;
|
||||
|
||||
CREATE TEMPORARY TABLE temp_reminders AS
|
||||
SELECT id, user_id, title, text, time, repeat_quantity, repeat_interval, original_time, color
|
||||
FROM reminders;
|
||||
DROP TABLE reminders;
|
||||
CREATE TABLE reminders(
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
text TEXT,
|
||||
time INTEGER NOT NULL,
|
||||
|
||||
repeat_quantity VARCHAR(15),
|
||||
repeat_interval INTEGER,
|
||||
original_time INTEGER,
|
||||
|
||||
color VARCHAR(7),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
INSERT INTO reminders
|
||||
SELECT * FROM temp_reminders;
|
||||
|
||||
-- Static reminders
|
||||
INSERT INTO reminder_services(static_reminder_id, notification_service_id)
|
||||
SELECT id, notification_service
|
||||
FROM static_reminders;
|
||||
|
||||
CREATE TEMPORARY TABLE temp_static_reminders AS
|
||||
SELECT id, user_id, title, text, color
|
||||
FROM static_reminders;
|
||||
DROP TABLE static_reminders;
|
||||
CREATE TABLE static_reminders(
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
text TEXT,
|
||||
|
||||
color VARCHAR(7),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
INSERT INTO static_reminders
|
||||
SELECT * FROM temp_static_reminders;
|
||||
|
||||
-- Templates
|
||||
INSERT INTO reminder_services(template_id, notification_service_id)
|
||||
SELECT id, notification_service
|
||||
FROM templates;
|
||||
|
||||
CREATE TEMPORARY TABLE temp_templates AS
|
||||
SELECT id, user_id, title, text, color
|
||||
FROM templates;
|
||||
DROP TABLE templates;
|
||||
CREATE TABLE templates(
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
text TEXT,
|
||||
|
||||
color VARCHAR(7),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
INSERT INTO templates
|
||||
SELECT * FROM temp_templates;
|
||||
|
||||
COMMIT;
|
||||
""")
|
||||
current_db_version = 5
|
||||
|
||||
return
|
||||
|
||||
def setup_db() -> None:
|
||||
@@ -128,7 +209,6 @@ def setup_db() -> None:
|
||||
title VARCHAR(255) NOT NULL,
|
||||
text TEXT,
|
||||
time INTEGER NOT NULL,
|
||||
notification_service INTEGER NOT NULL,
|
||||
|
||||
repeat_quantity VARCHAR(15),
|
||||
repeat_interval INTEGER,
|
||||
@@ -136,32 +216,41 @@ def setup_db() -> None:
|
||||
|
||||
color VARCHAR(7),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (notification_service) REFERENCES notification_services(id)
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS templates(
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
text TEXT,
|
||||
notification_service INTEGER NOT NULL,
|
||||
|
||||
color VARCHAR(7),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (notification_service) REFERENCES notification_services(id)
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS static_reminders(
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
text TEXT,
|
||||
notification_service INTEGER NOT NULL,
|
||||
|
||||
color VARCHAR(7),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (notification_service) REFERENCES notification_services(id)
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS reminder_services(
|
||||
reminder_id INTEGER,
|
||||
static_reminder_id INTEGER,
|
||||
template_id INTEGER,
|
||||
notification_service_id INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (reminder_id) REFERENCES reminders(id)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY (static_reminder_id) REFERENCES static_reminders(id)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES templates(id)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY (notification_service_id) REFERENCES notification_services(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS config(
|
||||
key VARCHAR(255) PRIMARY KEY,
|
||||
|
||||
@@ -82,24 +82,39 @@ class NotificationService:
|
||||
"""
|
||||
# Check if no reminders exist with this service
|
||||
cursor = get_db()
|
||||
cursor.execute(
|
||||
"SELECT 1 FROM reminders WHERE notification_service = ? LIMIT 1;",
|
||||
cursor.execute("""
|
||||
SELECT 1
|
||||
FROM reminder_services
|
||||
WHERE notification_service_id = ?
|
||||
AND reminder_id IS NOT NULL
|
||||
LIMIT 1;
|
||||
""",
|
||||
(self.id,)
|
||||
)
|
||||
if cursor.fetchone():
|
||||
raise NotificationServiceInUse('reminder')
|
||||
|
||||
# Check if no templates exist with this service
|
||||
cursor.execute(
|
||||
"SELECT 1 FROM templates WHERE notification_service = ? LIMIT 1;",
|
||||
cursor.execute("""
|
||||
SELECT 1
|
||||
FROM reminder_services
|
||||
WHERE notification_service_id = ?
|
||||
AND template_id IS NOT NULL
|
||||
LIMIT 1;
|
||||
""",
|
||||
(self.id,)
|
||||
)
|
||||
if cursor.fetchone():
|
||||
raise NotificationServiceInUse('template')
|
||||
|
||||
# Check if no static reminders exist with this service
|
||||
cursor.execute(
|
||||
"SELECT 1 FROM static_reminders WHERE notification_service = ? LIMIT 1;",
|
||||
cursor.execute("""
|
||||
SELECT 1
|
||||
FROM reminder_services
|
||||
WHERE notification_service_id = ?
|
||||
AND static_reminder_id IS NOT NULL
|
||||
LIMIT 1;
|
||||
""",
|
||||
(self.id,)
|
||||
)
|
||||
if cursor.fetchone():
|
||||
|
||||
@@ -56,25 +56,31 @@ class ReminderHandler:
|
||||
SELECT
|
||||
r.id,
|
||||
r.title, r.text,
|
||||
r.repeat_quantity, r.repeat_interval, r.original_time,
|
||||
ns.url
|
||||
r.repeat_quantity, r.repeat_interval, r.original_time
|
||||
FROM reminders r
|
||||
INNER JOIN notification_services ns
|
||||
ON r.notification_service = ns.id
|
||||
WHERE time = ?;
|
||||
""", (time,))
|
||||
reminders = list(map(dict, cursor))
|
||||
|
||||
for reminder in reminders:
|
||||
cursor.execute("""
|
||||
SELECT url
|
||||
FROM reminder_services rs
|
||||
INNER JOIN notification_services ns
|
||||
ON rs.notification_service_id = ns.id
|
||||
WHERE rs.reminder_id = ?;
|
||||
""", (reminder['id'],))
|
||||
|
||||
# Send of reminder
|
||||
a = Apprise()
|
||||
a.add(reminder['url'])
|
||||
for url in cursor:
|
||||
a.add(url['url'])
|
||||
a.notify(title=reminder["title"], body=reminder["text"])
|
||||
|
||||
if reminder['repeat_quantity'] is None:
|
||||
# Delete the reminder from the database
|
||||
cursor.execute(
|
||||
"DELETE FROM reminders WHERE id = ?",
|
||||
"DELETE FROM reminders WHERE id = ?;",
|
||||
(reminder['id'],)
|
||||
)
|
||||
else:
|
||||
@@ -155,30 +161,33 @@ class Reminder:
|
||||
"""
|
||||
reminder = get_db(dict).execute("""
|
||||
SELECT
|
||||
r.id,
|
||||
r.title, r.text,
|
||||
r.time,
|
||||
r.notification_service,
|
||||
ns.title AS notification_service_title,
|
||||
r.repeat_quantity,
|
||||
r.repeat_interval,
|
||||
r.color
|
||||
FROM reminders r
|
||||
INNER JOIN notification_services ns
|
||||
ON r.notification_service = ns.id
|
||||
WHERE r.id = ?
|
||||
id,
|
||||
title, text,
|
||||
time,
|
||||
repeat_quantity,
|
||||
repeat_interval,
|
||||
color
|
||||
FROM reminders
|
||||
WHERE id = ?
|
||||
LIMIT 1;
|
||||
""",
|
||||
(self.id,)
|
||||
).fetchone()
|
||||
reminder = dict(reminder)
|
||||
|
||||
return dict(reminder)
|
||||
reminder['notification_services'] = list(map(lambda r: r[0], get_db().execute("""
|
||||
SELECT notification_service_id
|
||||
FROM reminder_services
|
||||
WHERE reminder_id = ?;
|
||||
""", (self.id,))))
|
||||
|
||||
return reminder
|
||||
|
||||
def update(
|
||||
self,
|
||||
title: str = None,
|
||||
time: int = None,
|
||||
notification_service: int = None,
|
||||
notification_services: List[int] = None,
|
||||
text: str = None,
|
||||
repeat_quantity: Literal["years", "months", "weeks", "days", "hours", "minutes"] = None,
|
||||
repeat_interval: int = None,
|
||||
@@ -189,12 +198,15 @@ class Reminder:
|
||||
Args:
|
||||
title (str): The new title of the entry. Defaults to None.
|
||||
time (int): The new UTC epoch timestamp the the reminder should be send. Defaults to None.
|
||||
notification_service (int): The new id of the notification service to use to send the reminder. Defaults to None.
|
||||
notification_services (List[int]): The new list of id's of the notification services to use to send the reminder. Defaults to None.
|
||||
text (str, optional): The new body of the reminder. Defaults to None.
|
||||
repeat_quantity (Literal["years", "months", "weeks", "days", "hours", "minutes"], optional): The new quantity of the repeat specified for the reminder. Defaults to None.
|
||||
repeat_interval (int, optional): The new amount of repeat_quantity, like "5" (hours). Defaults to None.
|
||||
color (str, optional): The new hex code of the color of the reminder, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
Raises:
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
|
||||
Returns:
|
||||
dict: The new reminder info
|
||||
"""
|
||||
@@ -218,7 +230,6 @@ class Reminder:
|
||||
new_values = {
|
||||
'title': title,
|
||||
'time': time,
|
||||
'notification_service': notification_service,
|
||||
'text': text,
|
||||
'repeat_quantity': repeat_quantity,
|
||||
'repeat_interval': repeat_interval,
|
||||
@@ -229,53 +240,63 @@ class Reminder:
|
||||
data[k] = v
|
||||
|
||||
# Update database
|
||||
try:
|
||||
if not repeated_reminder:
|
||||
next_time = data["time"]
|
||||
cursor.execute("""
|
||||
UPDATE reminders
|
||||
SET
|
||||
title=?, text=?,
|
||||
time=?, notification_service=?,
|
||||
repeat_quantity=?, repeat_interval=?,
|
||||
color=?
|
||||
WHERE id = ?;
|
||||
""", (
|
||||
data["title"],
|
||||
data["text"],
|
||||
data["time"],
|
||||
data["notification_service"],
|
||||
data["repeat_quantity"],
|
||||
data["repeat_interval"],
|
||||
data["color"],
|
||||
self.id
|
||||
))
|
||||
else:
|
||||
next_time = _find_next_time(
|
||||
if not repeated_reminder:
|
||||
next_time = data["time"]
|
||||
cursor.execute("""
|
||||
UPDATE reminders
|
||||
SET
|
||||
title=?, text=?,
|
||||
time=?,
|
||||
repeat_quantity=?, repeat_interval=?,
|
||||
color=?
|
||||
WHERE id = ?;
|
||||
""", (
|
||||
data["title"],
|
||||
data["text"],
|
||||
data["time"],
|
||||
data["repeat_quantity"], data["repeat_interval"]
|
||||
data["repeat_quantity"],
|
||||
data["repeat_interval"],
|
||||
data["color"],
|
||||
self.id
|
||||
))
|
||||
else:
|
||||
next_time = _find_next_time(
|
||||
data["time"],
|
||||
data["repeat_quantity"], data["repeat_interval"]
|
||||
)
|
||||
cursor.execute("""
|
||||
UPDATE reminders
|
||||
SET
|
||||
title=?, text=?,
|
||||
time=?,
|
||||
repeat_quantity=?, repeat_interval=?, original_time=?,
|
||||
color=?
|
||||
WHERE id = ?;
|
||||
""", (
|
||||
data["title"],
|
||||
data["text"],
|
||||
next_time,
|
||||
data["repeat_quantity"],
|
||||
data["repeat_interval"],
|
||||
data["time"],
|
||||
data["color"],
|
||||
self.id
|
||||
))
|
||||
|
||||
if notification_services:
|
||||
cursor.connection.isolation_level = None
|
||||
cursor.execute("BEGIN TRANSACTION;")
|
||||
cursor.execute("DELETE FROM reminder_services WHERE reminder_id = ?", (self.id,))
|
||||
try:
|
||||
cursor.executemany(
|
||||
"INSERT INTO reminder_services(reminder_id, notification_service_id) VALUES (?,?)",
|
||||
((self.id, s) for s in notification_services)
|
||||
)
|
||||
cursor.execute("""
|
||||
UPDATE reminders
|
||||
SET
|
||||
title=?, text=?,
|
||||
time=?, notification_service=?,
|
||||
repeat_quantity=?, repeat_interval=?, original_time=?,
|
||||
color=?
|
||||
WHERE id = ?;
|
||||
""", (
|
||||
data["title"],
|
||||
data["text"],
|
||||
next_time,
|
||||
data["notification_service"],
|
||||
data["repeat_quantity"],
|
||||
data["repeat_interval"],
|
||||
data["time"],
|
||||
data["color"],
|
||||
self.id
|
||||
))
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
cursor.execute("COMMIT;")
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
cursor.connection.isolation_level = ""
|
||||
|
||||
reminder_handler.find_next_reminder(next_time)
|
||||
return self.get()
|
||||
|
||||
@@ -306,7 +327,7 @@ class Reminders:
|
||||
sort_by (Literal["time", "time_reversed", "title", "title_reversed"], optional): How to sort the result. Defaults to "time".
|
||||
|
||||
Returns:
|
||||
List[dict]: The id, title, text, time, notification_service, notification_service_title and color of each reminder
|
||||
List[dict]: The id, title, text, time and color of each reminder
|
||||
"""
|
||||
sort_function = self.sort_functions.get(
|
||||
sort_by,
|
||||
@@ -316,18 +337,14 @@ class Reminders:
|
||||
# Fetch all reminders
|
||||
reminders: list = list(map(dict, get_db(dict).execute("""
|
||||
SELECT
|
||||
r.id,
|
||||
r.title, r.text,
|
||||
r.time,
|
||||
r.notification_service,
|
||||
ns.title AS notification_service_title,
|
||||
r.repeat_quantity,
|
||||
r.repeat_interval,
|
||||
r.color
|
||||
FROM reminders r
|
||||
INNER JOIN notification_services ns
|
||||
ON r.notification_service = ns.id
|
||||
WHERE r.user_id = ?;
|
||||
id,
|
||||
title, text,
|
||||
time,
|
||||
repeat_quantity,
|
||||
repeat_interval,
|
||||
color
|
||||
FROM reminders
|
||||
WHERE user_id = ?;
|
||||
""",
|
||||
(self.user_id,)
|
||||
)))
|
||||
@@ -368,7 +385,7 @@ class Reminders:
|
||||
self,
|
||||
title: str,
|
||||
time: int,
|
||||
notification_service: int,
|
||||
notification_services: List[int],
|
||||
text: str = '',
|
||||
repeat_quantity: Literal["years", "months", "weeks", "days", "hours", "minutes"] = None,
|
||||
repeat_interval: int = None,
|
||||
@@ -379,12 +396,15 @@ class Reminders:
|
||||
Args:
|
||||
title (str): The title of the entry
|
||||
time (int): The UTC epoch timestamp the the reminder should be send.
|
||||
notification_service (int): The id of the notification service to use to send the reminder.
|
||||
notification_services (List[int]): The id's of the notification services to use to send the reminder.
|
||||
text (str, optional): The body of the reminder. Defaults to ''.
|
||||
repeat_quantity (Literal["years", "months", "weeks", "days", "hours", "minutes"], optional): The quantity of the repeat specified for the reminder. Defaults to None.
|
||||
repeat_interval (int, optional): The amount of repeat_quantity, like "5" (hours). Defaults to None.
|
||||
color (str, optional): The hex code of the color of the reminder, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
Raises:
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
|
||||
Returns:
|
||||
dict: The info about the reminder
|
||||
"""
|
||||
@@ -397,21 +417,28 @@ class Reminders:
|
||||
elif repeat_quantity is not None and repeat_interval is None:
|
||||
raise InvalidKeyValue('repeat_interval', repeat_interval)
|
||||
|
||||
cursor = get_db()
|
||||
if repeat_quantity is None and repeat_interval is None:
|
||||
id = cursor.execute("""
|
||||
INSERT INTO reminders(user_id, title, text, time, color)
|
||||
VALUES (?, ?, ?, ?, ?);
|
||||
""", (self.user_id, title, text, time, color)
|
||||
).lastrowid
|
||||
else:
|
||||
id = cursor.execute("""
|
||||
INSERT INTO reminders(user_id, title, text, time, repeat_quantity, repeat_interval, original_time, color)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
|
||||
""", (self.user_id, title, text, time, repeat_quantity, repeat_interval, time, color)
|
||||
).lastrowid
|
||||
|
||||
try:
|
||||
if repeat_quantity is None and repeat_interval is None:
|
||||
id = get_db().execute("""
|
||||
INSERT INTO reminders(user_id, title, text, time, notification_service, color)
|
||||
VALUES (?,?,?,?,?, ?);
|
||||
""", (self.user_id, title, text, time, notification_service, color)
|
||||
).lastrowid
|
||||
else:
|
||||
id = get_db().execute("""
|
||||
INSERT INTO reminders(user_id, title, text, time, notification_service, repeat_quantity, repeat_interval, original_time, color)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||
""", (self.user_id, title, text, time, notification_service, repeat_quantity, repeat_interval, time, color)
|
||||
).lastrowid
|
||||
cursor.executemany(
|
||||
"INSERT INTO reminder_services(reminder_id, notification_service_id) VALUES (?, ?);",
|
||||
((id, service) for service in notification_services)
|
||||
)
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
|
||||
reminder_handler.find_next_reminder(time)
|
||||
|
||||
# Return info
|
||||
@@ -419,7 +446,7 @@ class Reminders:
|
||||
|
||||
def test_reminder(
|
||||
title: str,
|
||||
notification_service: int,
|
||||
notification_services: List[int],
|
||||
text: str = ''
|
||||
) -> None:
|
||||
"""Test send a reminder draft
|
||||
@@ -430,12 +457,14 @@ def test_reminder(
|
||||
text (str, optional): The body of the reminder. Defaults to ''.
|
||||
"""
|
||||
a = Apprise()
|
||||
url = get_db(dict).execute(
|
||||
"SELECT url FROM notification_services WHERE id = ? LIMIT 1;",
|
||||
(notification_service,)
|
||||
).fetchone()
|
||||
if not url:
|
||||
raise NotificationServiceNotFound
|
||||
a.add(url[0])
|
||||
cursor = get_db(dict)
|
||||
for service in notification_services:
|
||||
url = cursor.execute(
|
||||
"SELECT url FROM notification_services WHERE id = ? LIMIT 1;",
|
||||
(service,)
|
||||
).fetchone()
|
||||
if not url:
|
||||
raise NotificationServiceNotFound
|
||||
a.add(url[0])
|
||||
a.notify(title=title, body=text)
|
||||
return
|
||||
|
||||
@@ -5,7 +5,8 @@ from typing import List
|
||||
|
||||
from apprise import Apprise
|
||||
|
||||
from backend.custom_exceptions import NotificationServiceNotFound, ReminderNotFound
|
||||
from backend.custom_exceptions import (NotificationServiceNotFound,
|
||||
ReminderNotFound)
|
||||
from backend.db import get_db
|
||||
|
||||
|
||||
@@ -30,26 +31,29 @@ class StaticReminder:
|
||||
"""
|
||||
reminder = get_db(dict).execute("""
|
||||
SELECT
|
||||
r.id,
|
||||
r.title, r.text,
|
||||
r.notification_service,
|
||||
ns.title AS notification_service_title,
|
||||
r.color
|
||||
FROM static_reminders r
|
||||
INNER JOIN notification_services ns
|
||||
ON r.notification_service = ns.id
|
||||
WHERE r.id = ?
|
||||
id,
|
||||
title, text,
|
||||
color
|
||||
FROM static_reminders
|
||||
WHERE id = ?
|
||||
LIMIT 1;
|
||||
""",
|
||||
(self.id,)
|
||||
).fetchone()
|
||||
reminder = dict(reminder)
|
||||
|
||||
return dict(reminder)
|
||||
reminder['notification_services'] = list(map(lambda r: r[0], get_db().execute("""
|
||||
SELECT notification_service_id
|
||||
FROM reminder_services
|
||||
WHERE static_reminder_id = ?;
|
||||
""", (self.id,))))
|
||||
|
||||
return reminder
|
||||
|
||||
def update(
|
||||
self,
|
||||
title: str = None,
|
||||
notification_service: int = None,
|
||||
notification_services: List[int] = None,
|
||||
text: str = None,
|
||||
color: str = None
|
||||
) -> dict:
|
||||
@@ -57,12 +61,12 @@ class StaticReminder:
|
||||
|
||||
Args:
|
||||
title (str, optional): The new title of the entry. Defaults to None.
|
||||
notification_service (int, optional): The new id of the notification service to use to send the reminder. Defaults to None.
|
||||
notification_services (List[int], optional): The new id's of the notification services to use to send the reminder. Defaults to None.
|
||||
text (str, optional): The new body of the reminder. Defaults to None.
|
||||
color (str, optional): The new hex code of the color of the reminder, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
Raises:
|
||||
NotificationServiceNotFound: The notification service with the given id was not found
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
|
||||
Returns:
|
||||
dict: The new static reminder info
|
||||
@@ -71,7 +75,6 @@ class StaticReminder:
|
||||
data = self.get()
|
||||
new_values = {
|
||||
'title': title,
|
||||
'notification_service': notification_service,
|
||||
'text': text,
|
||||
'color': color
|
||||
}
|
||||
@@ -80,22 +83,32 @@ class StaticReminder:
|
||||
data[k] = v
|
||||
|
||||
# Update database
|
||||
try:
|
||||
get_db().execute("""
|
||||
UPDATE static_reminders
|
||||
SET
|
||||
title = ?, text = ?,
|
||||
notification_service = ?,
|
||||
color = ?
|
||||
WHERE id = ?;
|
||||
""",
|
||||
(data['title'], data['text'],
|
||||
data['notification_service'],
|
||||
data['color'],
|
||||
self.id)
|
||||
)
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
cursor = get_db()
|
||||
cursor.execute("""
|
||||
UPDATE static_reminders
|
||||
SET
|
||||
title = ?, text = ?,
|
||||
color = ?
|
||||
WHERE id = ?;
|
||||
""",
|
||||
(data['title'], data['text'],
|
||||
data['color'],
|
||||
self.id)
|
||||
)
|
||||
|
||||
if notification_services:
|
||||
cursor.connection.isolation_level = None
|
||||
cursor.execute("BEGIN TRANSACTION;")
|
||||
cursor.execute("DELETE FROM reminder_services WHERE static_reminder_id = ?", (self.id,))
|
||||
try:
|
||||
cursor.executemany(
|
||||
"INSERT INTO reminder_services(static_reminder_id, notification_service_id) VALUES (?,?)",
|
||||
((self.id, s) for s in notification_services)
|
||||
)
|
||||
cursor.execute("COMMIT;")
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
cursor.connection.isolation_level = ""
|
||||
|
||||
return self.get()
|
||||
|
||||
@@ -116,22 +129,18 @@ class StaticReminders:
|
||||
"""Get all static reminders
|
||||
|
||||
Returns:
|
||||
List[dict]: The id, title, text, notification_service, notification_service_title and color of each static reminder
|
||||
List[dict]: The id, title, text and color of each static reminder
|
||||
"""
|
||||
reminders: list = list(map(
|
||||
dict,
|
||||
get_db(dict).execute("""
|
||||
SELECT
|
||||
r.id,
|
||||
r.title, r.text,
|
||||
r.notification_service,
|
||||
ns.title AS notification_service_title,
|
||||
r.color
|
||||
FROM static_reminders r
|
||||
INNER JOIN notification_services ns
|
||||
ON r.notification_service = ns.id
|
||||
WHERE r.user_id = ?
|
||||
ORDER BY r.title, r.id;
|
||||
id,
|
||||
title, text,
|
||||
color
|
||||
FROM static_reminders
|
||||
WHERE user_id = ?
|
||||
ORDER BY title, id;
|
||||
""",
|
||||
(self.user_id,)
|
||||
)
|
||||
@@ -153,7 +162,7 @@ class StaticReminders:
|
||||
def add(
|
||||
self,
|
||||
title: str,
|
||||
notification_service: int,
|
||||
notification_services: List[int],
|
||||
text: str = '',
|
||||
color: str = None
|
||||
) -> StaticReminder:
|
||||
@@ -161,23 +170,29 @@ class StaticReminders:
|
||||
|
||||
Args:
|
||||
title (str): The title of the entry
|
||||
notification_service (int): The id of the notification service to use to send the reminder.
|
||||
notification_services (List[int]): The id's of the notification services to use to send the reminder.
|
||||
text (str, optional): The body of the reminder. Defaults to ''.
|
||||
color (str, optional): The hex code of the color of the reminder, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
Raises:
|
||||
NotificationServiceNotFound: The notification service with the given id was not found
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
|
||||
Returns:
|
||||
StaticReminder: A StaticReminder instance representing the newly created static reminder
|
||||
"""
|
||||
cursor = get_db()
|
||||
id = cursor.execute("""
|
||||
INSERT INTO static_reminders(user_id, title, text, color)
|
||||
VALUES (?,?,?,?);
|
||||
""",
|
||||
(self.user_id, title, text, color)
|
||||
).lastrowid
|
||||
|
||||
try:
|
||||
id = get_db().execute("""
|
||||
INSERT INTO static_reminders(user_id, title, text, notification_service, color)
|
||||
VALUES (?,?,?,?,?);
|
||||
""",
|
||||
(self.user_id, title, text, notification_service, color)
|
||||
).lastrowid
|
||||
cursor.executemany(
|
||||
"INSERT INTO reminder_services(static_reminder_id, notification_service_id) VALUES (?, ?);",
|
||||
((id, service) for service in notification_services)
|
||||
)
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
|
||||
@@ -192,18 +207,26 @@ class StaticReminders:
|
||||
Raises:
|
||||
ReminderNotFound: The static reminder with the given id was not found
|
||||
"""
|
||||
reminder = get_db(dict).execute("""
|
||||
SELECT r.title, r.text, ns.url
|
||||
FROM static_reminders r
|
||||
INNER JOIN notification_services ns
|
||||
ON r.notification_service = ns.id
|
||||
WHERE r.id = ?;
|
||||
cursor = get_db(dict)
|
||||
reminder = cursor.execute("""
|
||||
SELECT title, text
|
||||
FROM static_reminders
|
||||
WHERE id = ?
|
||||
LIMIT 1;
|
||||
""", (id,)).fetchone()
|
||||
if not reminder:
|
||||
raise ReminderNotFound
|
||||
reminder = dict(reminder)
|
||||
|
||||
a = Apprise()
|
||||
a.add(reminder['url'])
|
||||
cursor.execute("""
|
||||
SELECT url
|
||||
FROM reminder_services rs
|
||||
INNER JOIN notification_services ns
|
||||
ON rs.notification_service_id = ns.id
|
||||
WHERE rs.static_reminder_id = ?;
|
||||
""", (id,))
|
||||
for url in cursor:
|
||||
a.add(url['url'])
|
||||
a.notify(title=reminder['title'], body=reminder['text'])
|
||||
return
|
||||
|
||||
@@ -31,7 +31,6 @@ class Template:
|
||||
SELECT
|
||||
id,
|
||||
title, text,
|
||||
notification_service,
|
||||
color
|
||||
FROM templates
|
||||
WHERE id = ?
|
||||
@@ -39,12 +38,19 @@ class Template:
|
||||
""",
|
||||
(self.id,)
|
||||
).fetchone()
|
||||
template = dict(template)
|
||||
|
||||
return dict(template)
|
||||
template['notification_services'] = list(map(lambda r: r[0], get_db().execute("""
|
||||
SELECT notification_service_id
|
||||
FROM reminder_services
|
||||
WHERE template_id = ?;
|
||||
""", (self.id,))))
|
||||
|
||||
return template
|
||||
|
||||
def update(self,
|
||||
title: str = None,
|
||||
notification_service: int = None,
|
||||
notification_services: List[int] = None,
|
||||
text: str = None,
|
||||
color: str = None
|
||||
) -> dict:
|
||||
@@ -52,7 +58,7 @@ class Template:
|
||||
|
||||
Args:
|
||||
title (str): The new title of the entry. Defaults to None.
|
||||
notification_service (int): The new id of the notification service to use to send the reminder. Defaults to None.
|
||||
notification_services (List[int]): The new id's of the notification services to use to send the reminder. Defaults to None.
|
||||
text (str, optional): The new body of the template. Defaults to None.
|
||||
color (str, optional): The new hex code of the color of the template, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
@@ -64,7 +70,6 @@ class Template:
|
||||
data = self.get()
|
||||
new_values = {
|
||||
'title': title,
|
||||
'notification_service': notification_service,
|
||||
'text': text,
|
||||
'color': color
|
||||
}
|
||||
@@ -72,20 +77,30 @@ class Template:
|
||||
if k in ('color',) or v is not None:
|
||||
data[k] = v
|
||||
|
||||
try:
|
||||
cursor.execute("""
|
||||
UPDATE templates
|
||||
SET title=?, notification_service=?, text=?, color=?
|
||||
WHERE id = ?;
|
||||
""", (
|
||||
data['title'],
|
||||
data['notification_service'],
|
||||
data['text'],
|
||||
data['color'],
|
||||
self.id
|
||||
))
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
cursor.execute("""
|
||||
UPDATE templates
|
||||
SET title=?, text=?, color=?
|
||||
WHERE id = ?;
|
||||
""", (
|
||||
data['title'],
|
||||
data['text'],
|
||||
data['color'],
|
||||
self.id
|
||||
))
|
||||
|
||||
if notification_services:
|
||||
cursor.connection.isolation_level = None
|
||||
cursor.execute("BEGIN TRANSACTION;")
|
||||
cursor.execute("DELETE FROM reminder_services WHERE template_id = ?", (self.id,))
|
||||
try:
|
||||
cursor.executemany(
|
||||
"INSERT INTO reminder_services(template_id, notification_service_id) VALUES (?,?)",
|
||||
((self.id, s) for s in notification_services)
|
||||
)
|
||||
cursor.execute("COMMIT;")
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
cursor.connection.isolation_level = ""
|
||||
|
||||
return self.get()
|
||||
|
||||
@@ -105,13 +120,12 @@ class Templates:
|
||||
"""Get all templates
|
||||
|
||||
Returns:
|
||||
List[dict]: The id, title, text, notification_service and color
|
||||
List[dict]: The id, title, text and color
|
||||
"""
|
||||
templates: list = list(map(dict, get_db(dict).execute("""
|
||||
SELECT
|
||||
id,
|
||||
title, text,
|
||||
notification_service,
|
||||
color
|
||||
FROM templates
|
||||
WHERE user_id = ?
|
||||
@@ -136,7 +150,7 @@ class Templates:
|
||||
def add(
|
||||
self,
|
||||
title: str,
|
||||
notification_service: int,
|
||||
notification_services: List[int],
|
||||
text: str = '',
|
||||
color: str = None
|
||||
) -> Template:
|
||||
@@ -144,20 +158,26 @@ class Templates:
|
||||
|
||||
Args:
|
||||
title (str): The title of the entry
|
||||
notification_service (int): The id of the notification service to use to send the reminder.
|
||||
notification_services (List[int]): The id's of the notification services to use to send the reminder.
|
||||
text (str, optional): The body of the reminder. Defaults to ''.
|
||||
color (str, optional): The hex code of the color of the template, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Template: The info about the template
|
||||
"""
|
||||
cursor = get_db()
|
||||
id = cursor.execute("""
|
||||
INSERT INTO templates(user_id, title, text, color)
|
||||
VALUES (?,?,?,?);
|
||||
""",
|
||||
(self.user_id, title, text, color)
|
||||
).lastrowid
|
||||
|
||||
try:
|
||||
id = get_db().execute("""
|
||||
INSERT INTO templates(user_id, title, text, notification_service, color)
|
||||
VALUES (?,?,?,?,?);
|
||||
""",
|
||||
(self.user_id, title, text, notification_service, color)
|
||||
).lastrowid
|
||||
cursor.executemany(
|
||||
"INSERT INTO reminder_services(template_id, notification_service_id) VALUES (?, ?);",
|
||||
((id, service) for service in notification_services)
|
||||
)
|
||||
except IntegrityError:
|
||||
raise NotificationServiceNotFound
|
||||
|
||||
|
||||
@@ -97,6 +97,8 @@ class User:
|
||||
"""
|
||||
cursor = get_db()
|
||||
cursor.execute("DELETE FROM reminders WHERE user_id = ?", (self.user_id,))
|
||||
cursor.execute("DELETE FROM templates WHERE user_id = ?", (self.user_id,))
|
||||
cursor.execute("DELETE FROM static_reminders WHERE user_id = ?", (self.user_id,))
|
||||
cursor.execute("DELETE FROM notification_services WHERE user_id = ?", (self.user_id,))
|
||||
cursor.execute("DELETE FROM users WHERE id = ?", (self.user_id,))
|
||||
return
|
||||
|
||||
@@ -81,7 +81,7 @@ def extract_key(values: dict, key: str, check_existence: bool=True) -> Any:
|
||||
|
||||
if value is not None:
|
||||
# Check value and optionally convert
|
||||
if key in ('time', 'notification_service'):
|
||||
if key == 'time':
|
||||
try:
|
||||
value = int(value)
|
||||
except (ValueError, TypeError):
|
||||
@@ -112,6 +112,15 @@ def extract_key(values: dict, key: str, check_existence: bool=True) -> Any:
|
||||
if not color_regex.search(value):
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
elif key == 'notification_services':
|
||||
if not value:
|
||||
raise KeyNotFound(key)
|
||||
if not isinstance(value, list):
|
||||
raise InvalidKeyValue(key, value)
|
||||
for v in value:
|
||||
if not isinstance(v, int):
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
else:
|
||||
if key == 'sort_by':
|
||||
value = 'time'
|
||||
@@ -403,13 +412,13 @@ def api_reminders_list():
|
||||
sort_by: how to sort the result. Allowed values are 'title', 'title_reversed', 'time' and 'time_reversed'
|
||||
Returns:
|
||||
200:
|
||||
The id, title, text, time, notification_service, notification_service_title, repeat_quantity, repeat_interval and color of each reminder
|
||||
The id, title, text, time, repeat_quantity, repeat_interval and color of each reminder
|
||||
POST:
|
||||
Description: Add a reminder
|
||||
Parameters (body):
|
||||
title (required): the title of the reminder
|
||||
time (required): the UTC epoch timestamp that the reminder should be sent at
|
||||
notification_service (required): the id of the notification service to use to send the notification
|
||||
notification_services (required): array of the id's of the notification services to use to send the notification
|
||||
text: the body of the reminder
|
||||
repeat_quantity ('years', 'months', 'weeks', 'days', 'hours', 'minutes'): The quantity of the repeat_interval
|
||||
repeat_interval: The number of the interval
|
||||
@@ -419,6 +428,8 @@ def api_reminders_list():
|
||||
The info about the new reminder entry
|
||||
400:
|
||||
KeyNotFound: One of the required parameters was not given
|
||||
404:
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
"""
|
||||
reminders: Reminders = g.user_data.reminders
|
||||
|
||||
@@ -431,7 +442,7 @@ def api_reminders_list():
|
||||
data = request.get_json()
|
||||
title = extract_key(data, 'title')
|
||||
time = extract_key(data, 'time')
|
||||
notification_service = extract_key(data, 'notification_service')
|
||||
notification_services = extract_key(data, 'notification_services')
|
||||
text = extract_key(data, 'text', check_existence=False)
|
||||
repeat_quantity = extract_key(data, 'repeat_quantity', check_existence=False)
|
||||
repeat_interval = extract_key(data, 'repeat_interval', check_existence=False)
|
||||
@@ -439,7 +450,7 @@ def api_reminders_list():
|
||||
|
||||
result = reminders.add(title=title,
|
||||
time=time,
|
||||
notification_service=notification_service,
|
||||
notification_services=notification_services,
|
||||
text=text,
|
||||
repeat_quantity=repeat_quantity,
|
||||
repeat_interval=repeat_interval,
|
||||
@@ -493,10 +504,10 @@ def api_test_reminder():
|
||||
"""
|
||||
data = request.get_json()
|
||||
title = extract_key(data, 'title')
|
||||
notification_service = extract_key(data, 'notification_service')
|
||||
notification_services = extract_key(data, 'notification_services')
|
||||
text = extract_key(data, 'text', check_existence=False)
|
||||
|
||||
test_reminder(title, notification_service, text)
|
||||
test_reminder(title, notification_services, text)
|
||||
return return_api({}, code=201)
|
||||
|
||||
@api.route('/reminders/<int:r_id>', methods=['GET', 'PUT', 'DELETE'])
|
||||
@@ -522,7 +533,7 @@ def api_get_reminder(r_id: int):
|
||||
Parameters (body):
|
||||
title: The new title of the entry.
|
||||
time: The new UTC epoch timestamp the the reminder should be send.
|
||||
notification_service: The new id of the notification service to use to send the reminder.
|
||||
notification_services: Array of the new id's of the notification services to use to send the reminder.
|
||||
text: The new body of the reminder.
|
||||
repeat_quantity ('years', 'months', 'weeks', 'days', 'hours', 'minutes'): The new quantity of the repeat_interval.
|
||||
repeat_interval: The new number of the interval.
|
||||
@@ -531,7 +542,8 @@ def api_get_reminder(r_id: int):
|
||||
200:
|
||||
Reminder updated successfully
|
||||
404:
|
||||
No reminder found with the given id
|
||||
ReminderNotFound: No reminder found with the given id
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
DELETE:
|
||||
Description: Delete the reminder
|
||||
Returns:
|
||||
@@ -549,15 +561,15 @@ def api_get_reminder(r_id: int):
|
||||
data = request.get_json()
|
||||
title = extract_key(data, 'title', check_existence=False)
|
||||
time = extract_key(data, 'time', check_existence=False)
|
||||
notification_service = extract_key(data, 'notification_service', check_existence=False)
|
||||
notification_services = extract_key(data, 'notification_services', check_existence=False)
|
||||
text = extract_key(data, 'text', check_existence=False)
|
||||
repeat_quantity = extract_key(data, 'repeat_quantity', check_existence=False)
|
||||
repeat_interval = extract_key(data, 'repeat_interval', check_existence=False)
|
||||
color = extract_key(data, 'color', check_existence=False)
|
||||
|
||||
|
||||
result = reminders.fetchone(r_id).update(title=title,
|
||||
time=time,
|
||||
notification_service=notification_service,
|
||||
notification_services=notification_services,
|
||||
text=text,
|
||||
repeat_quantity=repeat_quantity,
|
||||
repeat_interval=repeat_interval,
|
||||
@@ -585,12 +597,12 @@ def api_get_templates():
|
||||
Description: Get a list of all templates
|
||||
Returns:
|
||||
200:
|
||||
The id, title, notification_service, text and color of every template
|
||||
The id, title, text and color of every template
|
||||
POST:
|
||||
Description: Add a template
|
||||
Parameters (body):
|
||||
title (required): the title of the template
|
||||
notification_service (required): the id of the notification service to use to send the notification
|
||||
notification_services (required): array of the id's of the notification services to use to send the notification
|
||||
text: the body of the template
|
||||
color: the hex code of the color of the template, which is shown in the web-ui
|
||||
Returns:
|
||||
@@ -598,6 +610,8 @@ def api_get_templates():
|
||||
The info about the new template entry
|
||||
400:
|
||||
KeyNotFound: One of the required parameters was not given
|
||||
404:
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
"""
|
||||
templates: Templates = g.user_data.templates
|
||||
|
||||
@@ -608,12 +622,12 @@ def api_get_templates():
|
||||
elif request.method == 'POST':
|
||||
data = request.get_json()
|
||||
title = extract_key(data, 'title')
|
||||
notification_service = extract_key(data, 'notification_service')
|
||||
notification_services = extract_key(data, 'notification_services')
|
||||
text = extract_key(data, 'text', check_existence=False)
|
||||
color = extract_key(data, 'color', check_existence=False)
|
||||
|
||||
result = templates.add(title=title,
|
||||
notification_service=notification_service,
|
||||
notification_services=notification_services,
|
||||
text=text,
|
||||
color=color)
|
||||
return return_api(result.get(), code=201)
|
||||
@@ -640,14 +654,15 @@ def api_get_template(t_id: int):
|
||||
Description: Edit the template
|
||||
Parameters (body):
|
||||
title: The new title of the entry.
|
||||
notification_service: The new id of the notification service to use to send the reminder.
|
||||
notification_services: The new array of id's of the notification services to use to send the reminder.
|
||||
text: The new body of the template.
|
||||
color: The new hex code of the color of the template.
|
||||
Returns:
|
||||
200:
|
||||
Template updated successfully
|
||||
404:
|
||||
No template found with the given id
|
||||
TemplateNotFound: No template found with the given id
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
DELETE:
|
||||
Description: Delete the template
|
||||
Returns:
|
||||
@@ -665,12 +680,12 @@ def api_get_template(t_id: int):
|
||||
elif request.method == 'PUT':
|
||||
data = request.get_json()
|
||||
title = extract_key(data, 'title', check_existence=False)
|
||||
notification_service = extract_key(data, 'notification_service', check_existence=False)
|
||||
notification_services = extract_key(data, 'notification_services', check_existence=False)
|
||||
text = extract_key(data, 'text', check_existence=False)
|
||||
color = extract_key(data, 'color', check_existence=False)
|
||||
|
||||
result = template.update(title=title,
|
||||
notification_service=notification_service,
|
||||
notification_services=notification_services,
|
||||
text=text,
|
||||
color=color)
|
||||
return return_api(result)
|
||||
@@ -696,12 +711,12 @@ def api_static_reminders_list():
|
||||
Description: Get a list of all static reminders
|
||||
Returns:
|
||||
200:
|
||||
The id, title, text, notification_service, notification_service_title and color of each static reminder
|
||||
The id, title, text and color of each static reminder
|
||||
POST:
|
||||
Description: Add a static reminder
|
||||
Parameters (body):
|
||||
title (required): the title of the static reminder
|
||||
notification_service (required): the id of the notification service to use to send the notification
|
||||
notification_services (required): array of the id's of the notification services to use to send the notification
|
||||
text: the body of the static reminder
|
||||
color: The hex code of the color of the static reminder, which is shown in the web-ui
|
||||
Returns:
|
||||
@@ -709,6 +724,8 @@ def api_static_reminders_list():
|
||||
The info about the new static reminder entry
|
||||
400:
|
||||
KeyNotFound: One of the required parameters was not given
|
||||
404:
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
"""
|
||||
reminders: StaticReminders = g.user_data.static_reminders
|
||||
|
||||
@@ -719,12 +736,12 @@ def api_static_reminders_list():
|
||||
elif request.method == 'POST':
|
||||
data = request.get_json()
|
||||
title = extract_key(data, 'title')
|
||||
notification_service = extract_key(data, 'notification_service')
|
||||
notification_services = extract_key(data, 'notification_services')
|
||||
text = extract_key(data, 'text', check_existence=False)
|
||||
color = extract_key(data, 'color', check_existence=False)
|
||||
|
||||
result = reminders.add(title=title,
|
||||
notification_service=notification_service,
|
||||
notification_services=notification_services,
|
||||
text=text,
|
||||
color=color)
|
||||
return return_api(result.get(), code=201)
|
||||
@@ -756,14 +773,15 @@ def api_get_static_reminder(r_id: int):
|
||||
Description: Edit the static reminder
|
||||
Parameters (body):
|
||||
title: The new title of the static reminder.
|
||||
notification_service: The new id of the notification service to use to send the reminder.
|
||||
notification_services: The new array of id's of the notification services to use to send the reminder.
|
||||
text: The new body of the static reminder.
|
||||
color: The new hex code of the color of the static reminder, which is shown in the web-ui.
|
||||
Returns:
|
||||
200:
|
||||
Static reminder updated successfully
|
||||
404:
|
||||
No static reminder found with the given id
|
||||
ReminderNotFound: No static reminder found with the given id
|
||||
NotificationServiceNotFound: One of the notification services was not found
|
||||
DELETE:
|
||||
Description: Delete the static reminder
|
||||
Returns:
|
||||
@@ -784,12 +802,12 @@ def api_get_static_reminder(r_id: int):
|
||||
elif request.method == 'PUT':
|
||||
data = request.get_json()
|
||||
title = extract_key(data, 'title', check_existence=False)
|
||||
notification_service = extract_key(data, 'notification_service', check_existence=False)
|
||||
notification_services = extract_key(data, 'notification_services', check_existence=False)
|
||||
text = extract_key(data, 'text', check_existence=False)
|
||||
color = extract_key(data, 'color', check_existence=False)
|
||||
|
||||
result = reminders.fetchone(r_id).update(title=title,
|
||||
notification_service=notification_service,
|
||||
notification_services=notification_services,
|
||||
text=text,
|
||||
color=color)
|
||||
return return_api(result)
|
||||
|
||||
@@ -81,6 +81,34 @@
|
||||
border-color: var(--color-white);
|
||||
}
|
||||
|
||||
.notification-service-list {
|
||||
width: 100%;
|
||||
max-height: 10rem;
|
||||
overflow-y: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
border: 2px solid var(--color-gray);
|
||||
border-radius: 4px;
|
||||
|
||||
box-shadow: var(--default-shadow);
|
||||
}
|
||||
|
||||
.notification-service-list > div {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: .5rem .75rem;
|
||||
}
|
||||
|
||||
.notification-service-list > div:not(:first-child) {
|
||||
border-top: 1px solid var(--color-gray);
|
||||
}
|
||||
|
||||
.notification-service-list > div > input {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.repeat-bar,
|
||||
.repeat-edit-bar {
|
||||
display: flex;
|
||||
@@ -188,7 +216,7 @@ div.options > button {
|
||||
|
||||
#info.show-add-static-reminder #template-selection,
|
||||
#info.show-add-static-reminder #color-toggle,
|
||||
#info.show-add-static-reminder #notification-service-input {
|
||||
#info.show-add-static-reminder #toggle-notification-service-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -207,7 +235,7 @@ div.options > button {
|
||||
}
|
||||
|
||||
#info.show-add-template #color-toggle,
|
||||
#info.show-add-template #notification-service-input {
|
||||
#info.show-add-template #toggle-notification-service-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -236,7 +264,7 @@ div.options > button {
|
||||
}
|
||||
|
||||
#info.show-edit-static-reminder #color-toggle,
|
||||
#info.show-edit-static-reminder #notification-service-input {
|
||||
#info.show-edit-static-reminder #toggle-notification-service-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -254,6 +282,6 @@ div.options > button {
|
||||
}
|
||||
|
||||
#info.show-edit-template #color-toggle,
|
||||
#info.show-edit-template #notification-service-input {
|
||||
#info.show-edit-template #toggle-notification-service-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -14,15 +14,22 @@ function fillNotificationSelection() {
|
||||
if (json.result.length) {
|
||||
document.getElementById('add-reminder').classList.remove('error', 'error-icon');
|
||||
|
||||
const options = document.getElementById('notification-service-input');
|
||||
options.innerHTML = '';
|
||||
inputs.notification_service.innerHTML = '';
|
||||
json.result.forEach(service => {
|
||||
const entry = document.createElement('option');
|
||||
entry.value = service.id;
|
||||
entry.innerText = service.title;
|
||||
options.appendChild(entry);
|
||||
const entry = document.createElement('div');
|
||||
|
||||
const select = document.createElement('input');
|
||||
select.dataset.id = service.id;
|
||||
select.type = 'checkbox';
|
||||
entry.appendChild(select);
|
||||
|
||||
const title = document.createElement('p');
|
||||
title.innerText = service.title;
|
||||
entry.appendChild(title);
|
||||
|
||||
inputs.notification_service.appendChild(entry);
|
||||
});
|
||||
options.querySelector(':nth-child(1)').setAttribute('selected', '');
|
||||
inputs.notification_service.querySelector(':first-child input').checked = true;
|
||||
|
||||
const table = document.getElementById('services-list');
|
||||
table.querySelectorAll('tr:not(#add-row)').forEach(e => e.remove());
|
||||
|
||||
@@ -3,7 +3,8 @@ function showAdd(type) {
|
||||
inputs.title.value = '';
|
||||
inputs.text.value = '';
|
||||
inputs.time.value = '';
|
||||
inputs.notification_service.value = document.querySelector('#notification-service-input option[selected]').value;
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]').forEach(c => c.checked = false);
|
||||
inputs.notification_service.querySelector('input[type="checkbox"]:first-child').checked = true;
|
||||
toggleNormal();
|
||||
toggleColor(true);
|
||||
document.getElementById('test-reminder').classList.remove('show-sent');
|
||||
@@ -71,7 +72,9 @@ function showEdit(id, type) {
|
||||
);
|
||||
inputs.time.value = trigger_date.toLocaleString('en-CA').slice(0,10) + 'T' + trigger_date.toTimeString().slice(0,5);
|
||||
};
|
||||
inputs.notification_service.value = json.result.notification_service;
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]').forEach(
|
||||
c => c.checked = json.result.notification_services.includes(parseInt(c.dataset.id))
|
||||
);
|
||||
|
||||
if (type == types.reminder) {
|
||||
if (json.result.repeat_interval === null) {
|
||||
|
||||
@@ -26,7 +26,7 @@ function loadTemplateSelection() {
|
||||
function applyTemplate() {
|
||||
if (inputs.template.value === '0') {
|
||||
inputs.title.value = '';
|
||||
inputs.notification_service.value = document.querySelector('#notification-service-input option[selected]').value;
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]:checked').forEach(c => c.checked = false)
|
||||
inputs.text.value = '';
|
||||
toggleColor(true);
|
||||
} else {
|
||||
@@ -37,7 +37,9 @@ function applyTemplate() {
|
||||
})
|
||||
.then(json => {
|
||||
inputs.title.value = json.result.title;
|
||||
inputs.notification_service.value = json.result.notification_service;
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]').forEach(
|
||||
c => c.checked = json.result.notification_services.includes(parseInt(c.dataset.id))
|
||||
);
|
||||
inputs.text.value = json.result.text;
|
||||
if (json.result.color !== null) {
|
||||
if (inputs.color.classList.contains('hidden'))
|
||||
|
||||
@@ -4,7 +4,7 @@ const inputs = {
|
||||
'template': document.getElementById('template-selection'),
|
||||
'title': document.getElementById('title-input'),
|
||||
'time': document.getElementById('time-input'),
|
||||
'notification_service': document.getElementById('notification-service-input'),
|
||||
'notification_service': document.querySelector('.notification-service-list'),
|
||||
'text': document.getElementById('text-input'),
|
||||
'color': document.querySelector('.color-list')
|
||||
};
|
||||
@@ -44,6 +44,13 @@ function toggleColor(hide=false) {
|
||||
inputs.color.classList.add('hidden');
|
||||
};
|
||||
|
||||
function toggleNotificationService(hide=false) {
|
||||
if (!hide)
|
||||
inputs.notification_service.classList.toggle('hidden');
|
||||
else
|
||||
inputs.notification_service.classList.add('hidden');
|
||||
};
|
||||
|
||||
function toggleNormal() {
|
||||
type_buttons.normal_button.dataset.selected = 'true';
|
||||
type_buttons.repeat_button.dataset.selected = 'false';
|
||||
@@ -83,9 +90,22 @@ function testReminder() {
|
||||
input.classList.remove('error-input');
|
||||
input.removeAttribute('title');
|
||||
};
|
||||
|
||||
const ns = [...
|
||||
document.querySelectorAll('.notification-service-list input[type="checkbox"]:checked')
|
||||
].map(c => parseInt(c.dataset.id))
|
||||
if (!ns) {
|
||||
input.classList.add('error-input');
|
||||
input.title = 'No notification service set';
|
||||
return
|
||||
} else {
|
||||
input.classList.remove('error-input');
|
||||
input.removeAttribute('title');
|
||||
};
|
||||
|
||||
const data = {
|
||||
'title': inputs.title.value,
|
||||
'notification_service': inputs.notification_service.value,
|
||||
'notification_services': ns,
|
||||
'text': inputs.text.value
|
||||
};
|
||||
headers.body = JSON.stringify(data);
|
||||
@@ -140,19 +160,30 @@ function deleteInfo() {
|
||||
function submitInfo() {
|
||||
inputs.time.classList.remove('error-input');
|
||||
inputs.time.removeAttribute('title');
|
||||
inputs.notification_service.classList.remove('error-input');
|
||||
inputs.notification_service.removeAttribute('title');
|
||||
let fetch_data = {
|
||||
url: null,
|
||||
method: null
|
||||
method: null,
|
||||
call_back: null
|
||||
};
|
||||
const data = {
|
||||
'title': inputs.title.value,
|
||||
'notification_service': inputs.notification_service.value,
|
||||
'notification_services': [...
|
||||
document.querySelectorAll('.notification-service-list input[type="checkbox"]:checked')
|
||||
].map(c => parseInt(c.dataset.id)),
|
||||
'text': inputs.text.value,
|
||||
'color': null
|
||||
};
|
||||
if (!inputs.color.classList.contains('hidden')) {
|
||||
data['color'] = inputs.color.querySelector('button[data-selected="true"]').dataset.color;
|
||||
};
|
||||
|
||||
if (data.notification_services.length === 0) {
|
||||
inputs.notification_service.classList.add('error-input');
|
||||
inputs.notification_service.title = 'No notification service set';
|
||||
return
|
||||
};
|
||||
|
||||
const e_id = document.getElementById('info').dataset.id;
|
||||
const cl = document.getElementById('info').classList;
|
||||
@@ -165,16 +196,22 @@ function submitInfo() {
|
||||
};
|
||||
fetch_data.url = `${url_prefix}/api/reminders?api_key=${api_key}`;
|
||||
fetch_data.method = 'POST';
|
||||
fetch_data.call_back = fillReminders;
|
||||
|
||||
} else if (cl.contains('show-add-template')) {
|
||||
// Add template
|
||||
fetch_data.url = `${url_prefix}/api/templates?api_key=${api_key}`;
|
||||
fetch_data.method = 'POST';
|
||||
fetch_data.call_back = () => {
|
||||
loadTemplateSelection();
|
||||
fillTemplates();
|
||||
};
|
||||
|
||||
} else if (cl.contains('show-add-static-reminder')) {
|
||||
// Add static reminder
|
||||
fetch_data.url = `${url_prefix}/api/staticreminders?api_key=${api_key}`;
|
||||
fetch_data.method = 'POST';
|
||||
fetch_data.call_back = fillStaticReminders;
|
||||
|
||||
} else if (cl.contains('show-edit-reminder')) {
|
||||
// Edit reminder
|
||||
@@ -185,16 +222,22 @@ function submitInfo() {
|
||||
};
|
||||
fetch_data.url = `${url_prefix}/api/reminders/${e_id}?api_key=${api_key}`;
|
||||
fetch_data.method = 'PUT';
|
||||
fetch_data.call_back = fillReminders;
|
||||
|
||||
} else if (cl.contains('show-edit-template')) {
|
||||
// Edit template
|
||||
fetch_data.url = `${url_prefix}/api/templates/${e_id}?api_key=${api_key}`;
|
||||
fetch_data.method = 'PUT';
|
||||
fetch_data.call_back = () => {
|
||||
loadTemplateSelection();
|
||||
fillTemplates();
|
||||
};
|
||||
|
||||
} else if (cl.contains('show-edit-static-reminder')) {
|
||||
// Edit a static reminder
|
||||
fetch_data.url = `${url_prefix}/api/staticreminders/${e_id}?api_key=${api_key}`;
|
||||
fetch_data.method = 'PUT';
|
||||
fetch_data.call_back = fillStaticReminders;
|
||||
|
||||
} else return;
|
||||
|
||||
@@ -206,9 +249,7 @@ function submitInfo() {
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
|
||||
fillReminders();
|
||||
fillStaticReminders();
|
||||
fillTemplates();
|
||||
fetch_data.call_back()
|
||||
hideWindow();
|
||||
})
|
||||
.catch(e => {
|
||||
@@ -228,6 +269,7 @@ loadColor();
|
||||
|
||||
document.getElementById('template-selection').addEventListener('change', e => applyTemplate());
|
||||
document.getElementById('color-toggle').addEventListener('click', e => toggleColor());
|
||||
document.getElementById('toggle-notification-service-list').addEventListener('click', e => toggleNotificationService());
|
||||
document.getElementById('normal-button').addEventListener('click', e => toggleNormal());
|
||||
document.getElementById('repeat-button').addEventListener('click', e => toggleRepeated());
|
||||
document.getElementById('close-info').addEventListener('click', e => hideWindow());
|
||||
|
||||
@@ -163,8 +163,9 @@
|
||||
<input type="text" id="title-input" placeholder="Title" required>
|
||||
<div class="sub-inputs">
|
||||
<input type="datetime-local" id="time-input" required>
|
||||
<select id="notification-service-input" required></select>
|
||||
<button type="button" id="toggle-notification-service-list">Notification Services</button>
|
||||
</div>
|
||||
<div class="notification-service-list hidden"></div>
|
||||
<div class="sub-inputs">
|
||||
<button type="button" id="normal-button" data-selected="true">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 507.506 507.506" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import unittest
|
||||
from threading import Thread
|
||||
|
||||
from backend.reminders import filter_function, ReminderHandler
|
||||
|
||||
@@ -9,13 +8,6 @@ class Test_Reminder_Handler(unittest.TestCase):
|
||||
instance = ReminderHandler(context)
|
||||
self.assertIs(context, instance.context)
|
||||
|
||||
self.assertIsInstance(instance.thread, Thread)
|
||||
|
||||
self.assertFalse(instance.stop)
|
||||
with self.assertRaises(RuntimeError):
|
||||
instance.stop_handling()
|
||||
self.assertTrue(instance.stop)
|
||||
|
||||
def test_filter_function(self):
|
||||
p = {
|
||||
'title': 'TITLE',
|
||||
|
||||
Reference in New Issue
Block a user