Skip to content

Commit 0283c2f

Browse files
added api.search - addressing #22
1 parent 6aea47b commit 0283c2f

File tree

6 files changed

+38
-0
lines changed

6 files changed

+38
-0
lines changed

tests/main.sqlite-shm

0 Bytes
Binary file not shown.

tests/main.sqlite-wal

-40.3 KB
Binary file not shown.

tests/test_things.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
class ThingsCase(unittest.TestCase):
1515
"""Class documentation goes here."""
1616

17+
def test_search(self):
18+
"""Test search."""
19+
tasks = things.search('wrong_query', **FILEPATH)
20+
self.assertEqual(0, len(tasks))
21+
tasks = things.search('To-Do % Heading', **FILEPATH)
22+
self.assertEqual(1, len(tasks))
23+
1724
def test_inbox(self):
1825
"""Test inbox."""
1926
tasks = things.inbox(**FILEPATH)

things/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
link,
1212
logbook,
1313
projects,
14+
search,
1415
show,
1516
someday,
1617
tags,

things/api.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ def tags(title=None, include_items=False, **kwargs):
314314
# Utility API functions derived from above
315315
# --------------------------------------------------
316316

317+
def search(querystr: str, **kwargs):
318+
return tasks(querystr=querystr, **kwargs)
319+
317320

318321
def get(uuid, default=None, **kwargs):
319322
"""

things/database.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def get_tasks(
123123
due_date=None,
124124
index="index",
125125
count_only=False,
126+
querystr=None
126127
):
127128
"""Get tasks. See `api.tasks` for details on parameters."""
128129

@@ -162,6 +163,7 @@ def get_tasks(
162163
AND TASK.{self.IS_NOT_RECURRING}
163164
AND (PROJECT.title IS NULL OR PROJECT.{self.IS_NOT_TRASHED})
164165
AND (HEADPROJ.title IS NULL OR HEADPROJ.{self.IS_NOT_TRASHED})
166+
{make_search(querystr)}
165167
{make_filter('TASK.uuid', uuid)}
166168
{make_filter("TASK.area", area)}
167169
{make_filter("TASK.project", project)}
@@ -397,6 +399,7 @@ def get_count(self, sql):
397399
sql = f"""SELECT COUNT(uuid) FROM ({sql})"""
398400
return self.execute_query(sql, row_factory=list_factory)[0]
399401

402+
# noqa todo: add type hinting for resutl (List[Tuple[str, Any]]?)
400403
def execute_query(self, sql, parameters=(), row_factory=None):
401404
"""Run the actual query"""
402405
if self.debug is True:
@@ -664,6 +667,30 @@ def make_filter(column, value):
664667
}.get(value, default)
665668

666669

670+
def make_search(value: str) -> str:
671+
query = ""
672+
# noqa todo 'TMChecklistItem.title'
673+
sources = ['TASK.title', 'TASK.notes', 'AREA.title']
674+
for source in sources:
675+
subsearch = make_sub_search(source, value)
676+
if subsearch:
677+
query = query + subsearch + ' OR '
678+
if query.endswith(' OR '):
679+
query = query[:-4]
680+
if query != "":
681+
query = 'AND (' + query + ')'
682+
return query
683+
684+
685+
def make_sub_search(column, value):
686+
default = f'{column} LIKE "%{value}%"'
687+
return {
688+
None: "",
689+
False: f"{column} IS NULL",
690+
True: f"{column} IS NOT NULL",
691+
}.get(value, default)
692+
693+
667694
def validate(parameter, argument, valid_arguments):
668695
"""
669696
For a given parameter, check if its argument type is valid.

0 commit comments

Comments
 (0)