Skip to content

Commit

Permalink
version 2.6.3, added OPML support, updated database location, updated…
Browse files Browse the repository at this point in the history
… requirements, added anonymization support
  • Loading branch information
AlexanderWillner committed Nov 11, 2020
1 parent 6f74516 commit c395bc6
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-include appstore/Makefile

VERSION=2.6.2
VERSION=2.6.3
MAIN=things3_kanban
APP=things3_app
SERVER=things3_api
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ While everything should work out of the box, you might want to change some confi

```ini
[DATABASE]
THINGSDB=/Users/myname/Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac/Things.sqlite3
THINGSDB=/Users/myname/Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac/Things Database.thingsdatabase/main.sqlite
TAG_WAITING=Waiting
TAG_MIT=MIT
TAG_CLEANUP=Cleanup
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ flake8==3.7.9
python-coveralls==2.9.3
flask==1.1.2
argcomplete==1.11.1
pywebview==3.3.5
setuptools==45.0.0
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def package_files(directory):
AUTHOR_MAIL = "[email protected]"
DESCRIPTON = "A simple read-only CLI, API and Web Service for Things 3"
URL = "https://kanbanview.app"
VERSION = "2.6.2"
VERSION = "2.6.3"
DATA_FILES = package_files('resources')
OPTIONS = {
'argv_emulation': False,
Expand Down
42 changes: 37 additions & 5 deletions things3/things3.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
__copyright__ = "2020 Alexander Willner"
__credits__ = ["Alexander Willner"]
__license__ = "Apache License 2.0"
__version__ = "2.6.2"
__version__ = "2.6.3"
__maintainer__ = "Alexander Willner"
__email__ = "[email protected]"
__status__ = "Development"
Expand All @@ -30,7 +30,8 @@ class Things3():
# Database info
FILE_CONFIG = str(Path.home()) + '/.kanbanviewrc'
FILE_DB = '/Library/Group Containers/'\
'JLMPQHK86H.com.culturedcode.ThingsMac/Things.sqlite3'
'JLMPQHK86H.com.culturedcode.ThingsMac/'\
'Things Database.thingsdatabase/main.sqlite'
TABLE_TASK = "TMTask"
TABLE_AREA = "TMArea"
TABLE_TAG = "TMTag"
Expand Down Expand Up @@ -129,7 +130,7 @@ def __init__(self,

cfg = self.get_from_config(database, 'THINGSDB')
self.database = cfg if cfg else self.database
# Automated migration to new database location in Things 3.12.6
# Automated migration to new database location in Things 3.12.6/3.13.1
# --------------------------------
try:
with open(self.database) as f_d:
Expand Down Expand Up @@ -190,7 +191,8 @@ def anonymize_tasks(self, tasks):
if self.anonymize:
for task in tasks:
task['title'] = self.anonymize_string(task['title'])
task['context'] = self.anonymize_string(task['context'])
task['context'] = self.anonymize_string(
task['context']) if 'context' in task else ''
return tasks

def get_inbox(self):
Expand Down Expand Up @@ -230,6 +232,34 @@ def get_today(self):
"""
return self.get_rows(query)

def get_task(self, area=None, project=None):
"""Get tasks."""
afilter = f'AND TASK.area = "{area}"' \
if area is not None else ''
pfilter = f'AND TASK.project = "{project}"' \
if project is not None else ''
query = f"""
TASK.{self.IS_NOT_TRASHED} AND
TASK.{self.IS_TASK} AND
TASK.{self.IS_OPEN} AND
TASK.{self.IS_ANYTIME} AND
TASK.{self.IS_NOT_RECURRING} AND (
(
PROJECT.title IS NULL OR (
PROJECT.{self.IS_NOT_TRASHED}
)
) AND (
HEADPROJ.title IS NULL OR (
HEADPROJ.{self.IS_NOT_TRASHED}
)
)
)
{afilter}
{pfilter}
ORDER BY TASK.duedate DESC, TASK.{self.DATE_CREATE} DESC
"""
return self.get_rows(query)

def get_someday(self):
"""Get someday tasks."""
query = f"""
Expand Down Expand Up @@ -412,8 +442,9 @@ def get_trashed(self):
"""
return self.get_rows(query)

def get_projects(self):
def get_projects(self, area=None):
"""Get projects."""
afilter = f'AND TASK.area = "{area}"' if area is not None else ''
query = f"""
SELECT
TASK.uuid,
Expand All @@ -432,6 +463,7 @@ def get_projects(self):
TASK.{self.IS_NOT_TRASHED} AND
TASK.{self.IS_PROJECT} AND
TASK.{self.IS_OPEN}
{afilter}
ORDER BY TASK.title COLLATE NOCASE
"""
return self.execute_query(query)
Expand Down
2 changes: 1 addition & 1 deletion things3/things3_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
__copyright__ = "Copyright 2020 Alexander Willner"
__credits__ = ["Alexander Willner"]
__license__ = "Apache License 2.0"
__version__ = "2.6.2"
__version__ = "2.6.3"
__maintainer__ = "Alexander Willner"
__email__ = "[email protected]"
__status__ = "Development"
Expand Down
2 changes: 1 addition & 1 deletion things3/things3_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
__copyright__ = "Copyright 2020 Alexander Willner"
__credits__ = ["Luc Beaulieu", "Alexander Willner"]
__license__ = "Apache License 2.0"
__version__ = "2.6.2"
__version__ = "2.6.3"
__maintainer__ = "Alexander Willner"
__email__ = "[email protected]"
__status__ = "Development"
Expand Down
26 changes: 24 additions & 2 deletions things3/things3_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
__copyright__ = "2020 Alexander Willner"
__credits__ = ["Alexander Willner"]
__license__ = "Apache License 2.0"
__version__ = "2.6.2"
__version__ = "2.6.3"
__maintainer__ = "Alexander Willner"
__email__ = "[email protected]"
__status__ = "Development"
Expand All @@ -21,13 +21,16 @@
import webbrowser
import argcomplete # type: ignore
from things3.things3 import Things3
from things3.things3_opml import Things3OPML


class Things3CLI():
"""Simple read-only Thing 3 CLI."""

print_json = False
print_csv = False
print_opml = False
anonymize = False
things3 = None

def __init__(self, database=None):
Expand All @@ -37,6 +40,8 @@ def print_tasks(self, tasks):
"""Print a task."""
if self.print_json:
print(json.dumps(tasks))
elif self.print_opml:
Things3OPML().print_tasks(tasks)
elif self.print_csv:
fieldnames = ['uuid', 'title', 'context', 'context_uuid', 'size',
'type', 'due', 'created', 'modified', 'started',
Expand All @@ -48,7 +53,7 @@ def print_tasks(self, tasks):
else:
for task in tasks:
title = task['title']
context = task['context']
context = task['context'] if 'context' in task else ''
print(' - ', title, ' (', context, ')')

@classmethod
Expand Down Expand Up @@ -88,6 +93,10 @@ def get_parser(cls):
help='Shows all tasks')
subparsers.add_parser('csv',
help='Exports tasks as CSV')
subparsers.add_parser('areas',
help='Shows all areas')
subparsers.add_parser('opml',
help='Exports tasks as OPML')
subparsers.add_parser('due',
help='Shows tasks with due dates')
subparsers.add_parser('empty',
Expand Down Expand Up @@ -147,6 +156,14 @@ def get_parser(cls):
action="store_true", default=False,
help="output as CSV", dest="csv")

parser.add_argument("-o", "--opml",
action="store_true", default=False,
help="output as OPML", dest="opml")

parser.add_argument("-a", "--anonymize",
action="store_true", default=False,
help="anonymize output", dest="anonymize")

parser.add_argument(
"--version",
action="version",
Expand All @@ -165,10 +182,15 @@ def main(self, args=None):
command = args.command
self.print_json = args.json
self.print_csv = args.csv
self.print_opml = args.opml
self.anonymize = args.anonymize
self.things3.anonymize = self.anonymize

if command in self.things3.functions:
func = self.things3.functions[command]
self.print_tasks(func(self.things3))
elif command == "opml":
Things3OPML().print_all(self.things3)
elif command == "csv":
print("Deprecated: use --csv instead")
elif command == "feedback":
Expand Down
2 changes: 1 addition & 1 deletion things3/things3_kanban.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
__copyright__ = "Copyright 2020 Alexander Willner"
__credits__ = ["Luc Beaulieu", "Alexander Willner"]
__license__ = "Apache License 2.0"
__version__ = "2.6.2"
__version__ = "2.6.3"
__maintainer__ = "Alexander Willner"
__email__ = "[email protected]"
__status__ = "Development"
Expand Down
65 changes: 65 additions & 0 deletions things3/things3_opml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""OPML Plugin for the Thing 3 CLI."""

from __future__ import print_function

__author__ = "Alexander Willner"
__copyright__ = "2020 Alexander Willner"
__credits__ = ["Alexander Willner"]
__license__ = "Apache License 2.0"
__version__ = "2.6.3"
__maintainer__ = "Alexander Willner"
__email__ = "[email protected]"
__status__ = "Development"

import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element, SubElement
from xml.dom import minidom


class Things3OPML():
"""OPML Plugin for Thing 3 CLI."""

@staticmethod
def get_top():
"""Get first element."""
top = Element('opml')
head = SubElement(top, 'head')
title = SubElement(head, 'title')
title.text = 'Things 3 Database'
return top

@staticmethod
def print(top):
"""Print pretty XML."""
xmlstr = minidom.parseString(
ET.tostring(top)).toprettyxml(indent=" ")
print(xmlstr)

def print_tasks(self, tasks):
"""Print pretty XML of selected tasks."""
top = self.get_top()
body = SubElement(top, 'body')
for task in tasks:
SubElement(body, 'outline').set('text', task['title'])
self.print(top)

def print_all(self, things3):
"""Print."""
top = self.get_top()
body = SubElement(top, 'body')

for area in things3.get_areas():
area_element = SubElement(body, 'outline')
area_element.set('text', area['title'])
for task in things3.get_task(area['uuid']):
SubElement(area_element, 'outline').set('text', task['title'])
for project in things3.get_projects(area['uuid']):
project_element = SubElement(area_element, 'outline')
project_element.set('text', project['title'])
for task in things3.get_task(None, project['uuid']):
SubElement(project_element, 'outline').set(
'text', task['title'])
self.print(top)

0 comments on commit c395bc6

Please sign in to comment.