Passed
Push — master ( 5fd921...68583f )
by Alexander
01:12
created

things3   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 3
eloc 72
dl 0
loc 126
rs 10
c 0
b 0
f 0

3 Functions

Rating   Name   Duplication   Size   Complexity  
A get_rows() 0 34 1
A get_inbox() 0 3 1
A get_today() 0 3 1
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
4
"""Simple read-only API for Things 3."""
5
6
from __future__ import print_function
7
8
__author__ = "Alexander Willner"
9
__copyright__ = "2020 Alexander Willner"
10
__credits__ = ["Alexander Willner"]
11
__license__ = "Apache"
12
__version__ = "0.0.1"
13
__maintainer__ = "Alexander Willner"
14
__email__ = "[email protected]"
15
__status__ = "Development"
16
17
import sqlite3
18
from os.path import expanduser
19
from os import environ
20
21
# Basic config
22
FILE_SQLITE = '~/Library/Containers/'\
23
              'com.culturedcode.ThingsMac.beta/Data/Library/'\
24
              'Application Support/Cultured Code/Things/Things.sqlite3'\
25
    if not environ.get('THINGSDB') else environ.get('THINGSDB')
26
TAG_WAITING = "Waiting" if not environ.get('TAG_WAITING') \
27
    else environ.get('TAG_WAITING')
28
TAG_MIT = "MIT" if not environ.get('TAG_MIT') \
29
    else environ.get('TAG_MIT')
30
31
# Basic variables
32
FILE_SQLITE = expanduser(FILE_SQLITE)
33
CURSOR = sqlite3.connect(FILE_SQLITE).cursor()
34
35
# Database layout info
36
TASKTABLE = "TMTask"
37
AREATABLE = "TMArea"
38
TAGTABLE = "TMTag"
39
TASKTAGTABLE = "TMTaskTag"
40
ISNOTTRASHED = "TASK.trashed = 0"
41
ISTRASHED = "TASK.trashed = 1"
42
ISOPEN = "TASK.status = 0"
43
ISNOTSTARTED = "TASK.start = 0"
44
ISCANCELLED = "TASK.status = 2"
45
ISCOMPLETED = "TASK.status = 3"
46
ISSTARTED = "TASK.start = 1"
47
ISPOSTPONED = "TASK.start = 2"
48
ISTASK = "TASK.type = 0"
49
ISPROJECT = "TASK.type = 1"
50
ISHEADING = "TASK.type = 2"
51
ISOPENTASK = ISTASK + " AND " + ISNOTTRASHED + " AND " + ISOPEN
52
53
# Queries
54
LIST_SOMEDAY = ISOPENTASK + " AND " + ISPOSTPONED + \
55
    " AND TASK.startdate IS NULL AND TASK.recurrenceRule IS NULL" + \
56
    " ORDER BY TASK.duedate DESC, TASK.creationdate DESC"
57
LIST_INBOX = ISOPENTASK + " AND " + ISNOTSTARTED + \
58
    " ORDER BY TASK.duedate DESC , TASK.todayIndex"
59
LIST_ANYTIME = ISOPENTASK + " AND " + ISSTARTED + \
60
    " AND TASK.startdate is NULL" + \
61
    " AND (TASK.area NOT NULL OR TASK.project in (SELECT uuid FROM " + \
62
    TASKTABLE + \
63
    " WHERE uuid=TASK.project AND start=1" + \
64
    " AND trashed=0))" + \
65
    " ORDER BY TASK.duedate DESC , TASK.todayIndex"
66
LIST_TODAY = ISOPENTASK + " AND " + ISSTARTED + \
67
    " AND TASK.startdate is NOT NULL" + \
68
    " ORDER BY TASK.duedate DESC , TASK.todayIndex"
69
LIST_UPCOMING = ISOPENTASK + " AND " + ISPOSTPONED + \
70
    " AND (TASK.startDate NOT NULL OR TASK.recurrenceRule NOT NULL)" + \
71
    " ORDER BY TASK.startdate, TASK.todayIndex"
72
LIST_WAITING = ISOPENTASK + \
73
    " AND TAGS.tags=(SELECT uuid FROM " + TAGTABLE + \
74
    " WHERE title='" + TAG_WAITING + "')" + \
75
    " ORDER BY TASK.duedate DESC , TASK.todayIndex"
76
LIST_MIT = ISOPENTASK + " AND " + ISSTARTED + " AND PROJECT.status = 0 " \
77
    " AND TAGS.tags=(SELECT uuid FROM " + TAGTABLE + \
78
    " WHERE title='" + TAG_MIT + "')" + \
79
    " ORDER BY TASK.duedate DESC , TASK.todayIndex"
80
81
82
def get_inbox():
83
    """Get all tasks from the inbox."""
84
    return get_rows(LIST_INBOX)
85
86
87
def get_today():
88
    """Get all tasks from the today list."""
89
    return get_rows(LIST_TODAY)
90
91
92
def get_rows(sql):
93
    """Query Things database."""
94
95
    sql = """
96
        SELECT DISTINCT
97
            TASK.uuid,
98
            TASK.title,
99
            CASE
100
                WHEN AREA.title IS NOT NULL THEN AREA.title
101
                WHEN PROJECT.title IS NOT NULL THEN PROJECT.title
102
                WHEN HEADING.title IS NOT NULL THEN HEADING.title
103
            END,
104
            CASE
105
                WHEN AREA.uuid IS NOT NULL THEN AREA.uuid
106
                WHEN PROJECT.uuid IS NOT NULL THEN PROJECT.uuid
107
            END,
108
            CASE
109
                WHEN TASK.recurrenceRule IS NULL
110
                THEN date(TASK.dueDate,"unixepoch")
111
            ELSE NULL
112
            END
113
        FROM
114
            TMTask AS TASK
115
        LEFT JOIN
116
            TMTaskTag TAGS ON TAGS.tasks = TASK.uuid
117
        LEFT OUTER JOIN
118
            TMTask PROJECT ON TASK.project = PROJECT.uuid
119
        LEFT OUTER JOIN
120
            TMArea AREA ON TASK.area = AREA.uuid
121
        LEFT OUTER JOIN
122
            TMTask HEADING ON TASK.actionGroup = HEADING.uuid
123
        WHERE """ + sql
124
    CURSOR.execute(sql)
125
    return CURSOR.fetchall()
126