Skip to content

Adding Reminder time #133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified tests/main.sqlite
Binary file not shown.
Binary file modified tests/main.sqlite-shm
Binary file not shown.
Binary file modified tests/main.sqlite-wal
Binary file not shown.
4 changes: 4 additions & 0 deletions tests/test_things.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,10 @@ def test_thingsdate(self):
self.assertEqual("AND stopDate IS NULL", sqlfilter)


def test_thingstime(self):
test_task = things.tasks("7F4vqUNiTvGKaCUfv5pqYG")['reminder_time']
self.assertEqual(test_task, "12:34:00")

if __name__ == "__main__":
unittest.main()
ThingsCase() # For Vulture
52 changes: 51 additions & 1 deletion things/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"heading_title",
"project",
"project_title",
"reminder_time",
"trashed",
"tags",
)
Expand Down Expand Up @@ -107,6 +108,8 @@
# See `convert_isodate_sql_expression_to_thingsdate` for details.
DATE_DEADLINE = "deadline" # INTEGER: YYYYYYYYYYYMMMMDDDDD0000000, in binary
DATE_START = "startDate" # INTEGER: YYYYYYYYYYYMMMMDDDDD0000000, in binary
# See 'convert_isodate_sql_expresstion_to_thingstime' for details.
REMINDER_TIME = "reminderTime" # INTEGER: hhhhhmmmmmm00000000000000000000, in binary

# --------------------------------------------------
# Various filters
Expand Down Expand Up @@ -528,6 +531,9 @@ def make_tasks_sql_query(where_predicate=None, order_predicate=None):
deadline_expression = convert_thingsdate_sql_expression_to_isodate(
f"TASK.{DATE_DEADLINE}"
)
reminder_time_expression = convert_thingstime_sql_expression_to_isotime(
f"TASK.{REMINDER_TIME}"
)

return f"""
SELECT DISTINCT
Expand Down Expand Up @@ -578,6 +584,7 @@ def make_tasks_sql_query(where_predicate=None, order_predicate=None):
END AS checklist,
date({start_date_expression}) AS start_date,
date({deadline_expression}) AS deadline,
time({reminder_time_expression}) AS "reminder_time",
datetime(TASK.{DATE_STOP}, "unixepoch", "localtime") AS "stop_date",
datetime(TASK.{DATE_CREATED}, "unixepoch", "localtime") AS created,
datetime(TASK.{DATE_MODIFIED}, "unixepoch", "localtime") AS modified,
Expand Down Expand Up @@ -703,6 +710,49 @@ def convert_thingsdate_sql_expression_to_isodate(sql_expression):
return f"CASE WHEN {thingsdate} THEN {isodate} ELSE {thingsdate} END"


def convert_thingstime_sql_expression_to_isotime(sql_expression: str) -> str:
"""
Return SQL Expression that decodes a Things time as a string.

A _Things time_ is an integer where the binary digits are
hhhhhmmmmmm00000000000000000000; h is hours, m is minutes.
Seconds are not encoded in a Things time.

For example, the ISO 8601 time '12:34:00' corresponds to the Things
time 840957952 as integer; in binary that is:
1100100010000000000000000000000
hhhhhmmmmmm00000000000000000000
12 34 00

Parameters
----------
sql_expression : str
A sql expression pointing to a "Things time" integer in
format hhhhhmmmmmm00000000000000000000, in binary.



Example
-------
>>> convert_thingstime_sql_expression_to_isotime(TASK.reminderTime)
"time(CASE WHEN TASK.reminderTime THEN \
printf('%02d:%02d', (TASK.reminderTime & 2080374784) >> 26, \
(TASK.reminderTime & 66060288) >> 20) \
ELSE TASK.reminderTime END) AS "reminder_time"",
"""

h_mask = 0b1111100000000000000000000000000
m_mask = 0b0000011111100000000000000000000

thingstime = sql_expression
hours = f"({thingstime} & {h_mask}) >> 26"
minutes = f"({thingstime} & {m_mask}) >> 20"

isotime = f"printf('%02d:%02d', {hours}, {minutes})"
# when thingstime is NULL, return thingstime as-is
return f"CASE WHEN {thingstime} THEN {isotime} ELSE {thingstime} END"


def dict_factory(cursor, row):
"""
Convert SQL result into a dictionary.
Expand Down Expand Up @@ -1192,4 +1242,4 @@ def validate_offset(parameter, argument):
f"Please specify a string of the format 'X[d/w/y]' "
"where X is a non-negative integer followed by 'd', 'w', or 'y' "
"that indicates days, weeks, or years."
)
)