Added option to enable/disable reminders (#77)

This commit is contained in:
CasVT
2025-04-24 21:10:28 +02:00
parent cefc1a9593
commit cd400bb559
12 changed files with 133 additions and 29 deletions

View File

@@ -242,6 +242,7 @@ class ReminderData(GeneralReminderData):
repeat_quantity: Union[str, None]
repeat_interval: Union[int, None]
_weekdays: Union[str, None]
enabled: bool
def __post_init__(self) -> None:
if self._weekdays is not None:

View File

@@ -57,7 +57,8 @@ class Reminder:
repeat_quantity: Union[None, RepeatQuantity] = None,
repeat_interval: Union[None, int] = None,
weekdays: Union[None, List[WEEKDAY_NUMBER]] = None,
color: Union[None, str] = None
color: Union[None, str] = None,
enabled: Union[None, bool] = None
) -> ReminderData:
"""Edit the reminder.
@@ -92,6 +93,10 @@ class Reminder:
of the reminder, which is shown in the web-ui.
Defaults to None.
enabled (Union[None, bool], optional): Whether the reminder should
be enabled.
Defaults to None.
Note about args:
Either repeat_quantity and repeat_interval are given, weekdays is
given or neither, but not both.
@@ -107,7 +112,8 @@ class Reminder:
LOGGER.info(
f'Updating notification service {self.id}: '
+ f'{title=}, {time=}, {notification_services=}, {text=}, '
+ f'{repeat_quantity=}, {repeat_interval=}, {weekdays=}, {color=}'
+ f'{repeat_quantity=}, {repeat_interval=}, {weekdays=}, {color=}, '
+ f"{enabled=}"
)
# Validate data
@@ -151,7 +157,8 @@ class Reminder:
lambda w: ",".join(map(str, sorted(w)))
),
'color': color,
'notification_services': notification_services
'notification_services': notification_services,
'enabled': enabled
}
for k, v in new_values.items():
if (
@@ -163,7 +170,7 @@ class Reminder:
if repeated_reminder:
next_time = find_next_time(
data["time"],
data["repeat_quantity"],
repeat_quantity,
data["repeat_interval"],
weekdays
)
@@ -177,7 +184,8 @@ class Reminder:
data["weekdays"],
data["time"],
data["color"],
data["notification_services"]
data["notification_services"],
data["enabled"]
)
else:
@@ -192,7 +200,8 @@ class Reminder:
data["weekdays"],
data["original_time"],
data["color"],
data["notification_services"]
data["notification_services"],
data["enabled"]
)
REMINDER_HANDLER.find_next_reminder(next_time)
@@ -281,7 +290,8 @@ class Reminders:
repeat_quantity: Union[None, RepeatQuantity] = None,
repeat_interval: Union[None, int] = None,
weekdays: Union[None, List[WEEKDAY_NUMBER]] = None,
color: Union[None, str] = None
color: Union[None, str] = None,
enabled: bool = True
) -> Reminder:
"""Add a reminder.
@@ -312,6 +322,10 @@ class Reminders:
reminder, which is shown in the web-ui.
Defaults to None.
enabled (Union[None, bool], optional): Whether the reminder should
be enabled.
Defaults to None.
Note about args:
Either repeat_quantity and repeat_interval are given,
weekdays is given or neither, but not both.
@@ -327,7 +341,8 @@ class Reminders:
"""
LOGGER.info(
f'Adding reminder with {title=}, {time=}, {notification_services=}, ' +
f'{text=}, {repeat_quantity=}, {repeat_interval=}, {weekdays=}, {color=}')
f'{text=}, {repeat_quantity=}, {repeat_interval=}, {weekdays=}, {color=}' +
f'{enabled=}')
# Validate data
if time < datetime.utcnow().timestamp():
@@ -377,7 +392,8 @@ class Reminders:
weekdays_str,
original_time,
color,
notification_services
notification_services,
enabled
)
REMINDER_HANDLER.find_next_reminder(time)

View File

@@ -314,6 +314,7 @@ def setup_db() -> None:
weekdays VARCHAR(13),
color VARCHAR(7),
enabled BOOL NOT NULL DEFAULT 1,
FOREIGN KEY (user_id) REFERENCES users(id)
);

View File

@@ -303,3 +303,18 @@ class MigrateUpdateManifest(DBMigrator):
# So the migration doesn't do anything anymore, and a function used
# doesn't exist anymore, so the whole migration is just removed.
return
class MigrateAddEnabled(DBMigrator):
start_version = 10
def run(self) -> None:
# V10 -> V11
from backend.internals.db import get_db
get_db().execute("""
ALTER TABLE reminders
ADD enabled BOOL NOT NULL DEFAULT 1;
""")
return

View File

@@ -547,7 +547,7 @@ class RemindersDB:
id, title, text, color,
time, original_time,
repeat_quantity, repeat_interval,
weekdays AS _weekdays
weekdays AS _weekdays, enabled
FROM reminders
WHERE user_id = :user_id
{id_filter};
@@ -576,7 +576,8 @@ class RemindersDB:
weekdays: Union[str, None],
original_time: Union[int, None],
color: Union[str, None],
notification_services: List[int]
notification_services: List[int],
enabled: bool
) -> int:
new_id = get_db().execute("""
INSERT INTO reminders(
@@ -586,7 +587,8 @@ class RemindersDB:
repeat_quantity, repeat_interval,
weekdays,
original_time,
color
color,
enabled
)
VALUES (
:user_id,
@@ -595,7 +597,8 @@ class RemindersDB:
:rq, :ri,
:wd,
:ot,
:color
:color,
:enabled
);
""",
{
@@ -607,7 +610,8 @@ class RemindersDB:
"ri": repeat_interval,
"wd": weekdays,
"ot": original_time,
"color": color
"color": color,
"enabled": enabled
}
).lastrowid
@@ -628,7 +632,8 @@ class RemindersDB:
weekdays: Union[str, None],
original_time: Union[int, None],
color: Union[str, None],
notification_services: List[int]
notification_services: List[int],
enabled: bool
) -> None:
get_db().execute("""
UPDATE reminders
@@ -640,7 +645,8 @@ class RemindersDB:
repeat_interval = :ri,
weekdays = :wd,
original_time = :ot,
color = :color
color = :color,
enabled = :enabled
WHERE id = :r_id;
""",
{
@@ -652,6 +658,7 @@ class RemindersDB:
"wd": weekdays,
"ot": original_time,
"color": color,
"enabled": enabled,
"r_id": reminder_id
}
)
@@ -701,7 +708,14 @@ class UserlessRemindersDB:
).exists() or -1
def get_soonest_time(self) -> Union[int, None]:
return get_db().execute("SELECT MIN(time) FROM reminders;").exists()
"""Get the earliest time a reminder goes off that is enabled.
Returns:
Union[int, None]: The time, or None if there are no reminders.
"""
return get_db().execute(
"SELECT MIN(time) FROM reminders WHERE enabled = 1;"
).exists()
def fetch(
self,
@@ -717,7 +731,7 @@ class UserlessRemindersDB:
title, text, color,
time, original_time,
repeat_quantity, repeat_interval,
weekdays AS _weekdays
weekdays AS _weekdays, enabled
FROM reminders
{time_filter};
""",
@@ -745,7 +759,8 @@ class UserlessRemindersDB:
weekdays: Union[str, None],
original_time: Union[int, None],
color: Union[str, None],
notification_services: List[int]
notification_services: List[int],
enabled: bool
) -> int:
new_id = get_db().execute("""
INSERT INTO reminders(
@@ -755,7 +770,8 @@ class UserlessRemindersDB:
repeat_quantity, repeat_interval,
weekdays,
original_time,
color
color,
enabled
)
VALUES (
:user_id,
@@ -764,7 +780,8 @@ class UserlessRemindersDB:
:rq, :ri,
:wd,
:ot,
:color
:color,
:enabled
);
""",
{
@@ -776,7 +793,8 @@ class UserlessRemindersDB:
"ri": repeat_interval,
"wd": weekdays,
"ot": original_time,
"color": color
"color": color,
"enabled": enabled
}
).lastrowid

View File

@@ -306,7 +306,8 @@ def api_reminders_list(inputs: Dict[str, Any]):
repeat_quantity=inputs['repeat_quantity'],
repeat_interval=inputs['repeat_interval'],
weekdays=inputs['weekdays'],
color=inputs['color']
color=inputs['color'],
enabled=inputs['enabled']
)
return return_api(result.get().todict(), code=201)
@@ -344,6 +345,7 @@ def api_get_reminder(inputs: Dict[str, Any], r_id: int):
return return_api(result.todict())
elif request.method == 'PUT':
print(inputs)
result = reminders.fetchone(r_id).update(
title=inputs['title'],
time=inputs['time'],
@@ -352,7 +354,8 @@ def api_get_reminder(inputs: Dict[str, Any], r_id: int):
repeat_quantity=inputs['repeat_quantity'],
repeat_interval=inputs['repeat_interval'],
weekdays=inputs['weekdays'],
color=inputs['color']
color=inputs['color'],
enabled=inputs['enabled']
)
return return_api(result.todict())
@@ -453,7 +456,7 @@ def api_static_reminders_query(inputs: Dict[str, Any]):
return return_api([r.todict() for r in result])
@api.route('staticreminders/<int:s_id>', StaticReminderData)
@api.route('/staticreminders/<int:s_id>', StaticReminderData)
@endpoint_wrapper
def api_get_static_reminder(inputs: Dict[str, Any], s_id: int):
reminders = StaticReminders(

View File

@@ -306,6 +306,23 @@ class ColorVariable(NonRequiredInputVariable):
)
class EnabledVariable(NonRequiredInputVariable):
name = "enabled"
description = "Whether the reminder should be enabled"
data_type = [DataType.BOOL]
default = True
def validate(self) -> bool:
return isinstance(self.value, bool)
class EditEnabledVariable(EnabledVariable):
default = None
def validate(self) -> bool:
return self.value is None or super().validate()
class QueryVariable(InputVariable):
name = "query"
description = "The search term"
@@ -538,7 +555,8 @@ class RemindersData(EndpointData):
RepeatQuantityVariable,
RepeatIntervalVariable,
WeekDaysVariable,
ColorVariable
ColorVariable,
EnabledVariable
]
)
)
@@ -570,7 +588,8 @@ class ReminderData(EndpointData):
RepeatQuantityVariable,
RepeatIntervalVariable,
WeekDaysVariable,
ColorVariable
ColorVariable,
EditEnabledVariable
]
),
delete=(

View File

@@ -21,7 +21,7 @@
--gap: 1rem;
display: flex;
justify-content: center;
align-items: center;
align-items: stretch;
flex-wrap: wrap;
gap: var(--gap);
}
@@ -29,7 +29,7 @@
.sub-inputs > :where(
input, select, button, label
) {
width: calc(50% - (var(--gap) / 2));
flex: 1 0;
}
.form-container > form > button,
@@ -54,6 +54,19 @@
opacity: 0;
}
#enabled-container {
height: 4rem;
flex: 1 0;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
gap: 1rem;
padding: 1rem;
}
#color-button {
--color: var(--color-dark);
background-color: var(--color);
@@ -254,6 +267,7 @@ div.options > button {
display: none;
}
#info.show-add-static-reminder #enabled-container,
#info.show-add-static-reminder #time-input,
#info.show-add-static-reminder #normal-button,
#info.show-add-static-reminder #repeat-button,
@@ -274,6 +288,7 @@ div.options > button {
width: 100%;
}
#info.show-add-template #enabled-container,
#info.show-add-template #template-selection,
#info.show-add-template #time-input,
#info.show-add-template #normal-button,
@@ -307,6 +322,7 @@ div.options > button {
width: 100%;
}
#info.show-edit-static-reminder #enabled-container,
#info.show-edit-static-reminder #template-selection,
#info.show-edit-static-reminder #time-input,
#info.show-edit-static-reminder #normal-button,
@@ -326,6 +342,7 @@ div.options > button {
width: 100%;
}
#info.show-edit-template #enabled-container,
#info.show-edit-template #template-selection,
#info.show-edit-template #time-input,
#info.show-edit-template #normal-button,

View File

@@ -81,6 +81,9 @@ function fillTable(table, results) {
} else if (r.weekdays !== null)
formatted_date += ` (each ${r.weekdays.map(d => week_days[d]).join(', ')})`;
if (!r.enabled)
formatted_date += ' (Disabled)';
time.innerText = formatted_date;
entry.appendChild(time);
};

View File

@@ -1,5 +1,6 @@
function showAdd(type) {
const default_service = getLocalStorage('default_service')['default_service'];
inputs.enabled.checked = true;
inputs.template.value = '0';
inputs.title.value = '';
inputs.text.value = '';
@@ -69,6 +70,7 @@ function showEdit(id, type) {
inputs.title.value = json.result.title;
if (type === Types.reminder) {
inputs.enabled.checked = json.result.enabled;
var trigger_date = new Date(
(json.result.time
+ new Date(json.result.time * 1000).getTimezoneOffset()

View File

@@ -1,6 +1,7 @@
const colors = ["#3c3c3c", "#49191e", "#171a42", "#083b06", "#3b3506", "#300e40"];
const inputs = {
'enabled': document.querySelector('#enabled-input'),
'template': document.querySelector('#template-selection'),
'color_toggle': document.querySelector('#color-toggle'),
'color_button': document.querySelector('#color-button'),
@@ -219,6 +220,8 @@ function submitInfo() {
const cl = document.getElementById('info').classList;
if (cl.contains('show-add-reminder')) {
// Add reminder
data['enabled'] = inputs.enabled.checked;
data['time'] =
(new Date(inputs.time.value) / 1000)
+ (new Date(inputs.time.value).getTimezoneOffset() * 60);
@@ -262,6 +265,8 @@ function submitInfo() {
} else if (cl.contains('show-edit-reminder')) {
// Edit reminder
data['enabled'] = inputs.enabled.checked;
data['time'] =
(new Date(inputs.time.value) / 1000)
+ (new Date(inputs.time.value).getTimezoneOffset() * 60);

View File

@@ -228,6 +228,10 @@
<select id="template-selection">
<option value="0" selected>No template</option>
</select>
<div id="enabled-container" class="as-button">
<input type="checkbox" id="enabled-input" checked>
<label for="enabled-input">Enabled</label>
</div>
<label for="color-toggle" id="color-button" class="as-button">Color</label>
</div>
<div class="color-list"></div>