From 5baa671d83ab4c8491d0818bc2d4d4c02d2cd1d6 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 5 Sep 2015 15:56:09 +0100 Subject: [PATCH 1/8] Start v0.2-dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3954474..09ded72 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from distutils.core import setup setup( name='NextAction', - version='0.1', + version='0.2-dev', py_modules=['nextaction'], url='https://github.com/nikdoof/NextAction', license='MIT', From 2564f972d84027d9b1b538b2d2ec5459feaa95c7 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 5 Sep 2015 15:56:30 +0100 Subject: [PATCH 2/8] Remove Heroku support --- README.md | 16 +--------------- app.json | 37 ------------------------------------- nextaction.py | 21 ++++++++------------- 3 files changed, 9 insertions(+), 65 deletions(-) delete mode 100644 app.json diff --git a/README.md b/README.md index b41d241..5291b48 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,4 @@ Running NextAction NextAction will read your environment to retrieve your Todoist API key, so to run on a Linux/Mac OSX you can use the following commandline - TODOIST_API_KEY="XYZ" python nextaction.py - -Heroku Support --------------- - -[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) - -This package is ready to be pushed to a Heroku instance with minimal configuration values: - -* ```TODOIST_API_KEY``` - Your Todoist API Key -* ```TODOIST_NEXT_ACTION_LABEL``` - The label to use in Todoist for next actions (defaults to next_action) -* ```TODOIST_SYNC_DELAY``` - The number of seconds to wait between syncs. (defaults to 5) -* ```TODOIST_INBOX_HANDLING``` - What method to use for the Inbox, sequence or parallel (defaults to parallel) -* ```TODODIST_PARALLEL_SUFFIX``` - What sequence of characters to use to identify parallel processed projects (defaults to =) -* ```TODODIST_SERIAL_SUFFIX``` - What sequence of characters to use to identify serial processed projects (defaults to -) \ No newline at end of file + python nextaction.py -a diff --git a/app.json b/app.json deleted file mode 100644 index b96f697..0000000 --- a/app.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "NextAction", - "description": "Todoist API application to provide a auto-populated next action label", - "repository": "https://github.com/nikdoof/NextAction", - "keywords": ["python"], - "env": { - "TODOIST_API_KEY": { - "description": "Your Todoist API Key", - "required": true - }, - "TODOIST_NEXT_ACTION_LABEL": { - "description": "The Todoist label to use for next actions.", - "value": "next_action", - "required": false - }, - "TODOIST_SYNC_DELAY": { - "description": "The number of seconds to wait between syncs.", - "value": "5", - "required": false - }, - "TODOIST_INBOX_HANDLING": { - "description": "What method to use for the Inbox, sequence or parallel", - "value": "parallel", - "required": false - }, - "TODODIST_PARALLEL_SUFFIX": { - "description": "What sequence of characters to use to identify parallel processed projects", - "value": "=", - "required": false - }, - "TODODIST_SERIAL_SUFFIX": { - "description": "What sequence of characters to use to identify serial processed projects", - "value": "-", - "required": false - } - } -} \ No newline at end of file diff --git a/nextaction.py b/nextaction.py index f683d10..a1199bf 100755 --- a/nextaction.py +++ b/nextaction.py @@ -2,7 +2,6 @@ import time import logging -import os import sys import argparse from datetime import datetime @@ -34,24 +33,20 @@ def get_subitems(items, parent_item=None): def main(): parser = argparse.ArgumentParser() - parser.add_argument('-a', '--api_key', help='Todoist API Key', - default=os.environ.get('TODOIST_API_KEY', None)) - parser.add_argument('-l', '--label', help='The next action label to use', - default=os.environ.get('TODOIST_NEXT_ACTION_LABEL', 'next_action')) - parser.add_argument('-d', '--delay', help='Specify the delay in seconds between syncs', - default=int(os.environ.get('TODOIST_SYNC_DELAY', '5')), type=int) + parser.add_argument('-a', '--api_key', help='Todoist API Key') + parser.add_argument('-l', '--label', help='The next action label to use', default='next_action') + parser.add_argument('-d', '--delay', help='Specify the delay in seconds between syncs', default=5, type=int) parser.add_argument('--debug', help='Enable debugging', action='store_true') parser.add_argument('--inbox', help='The method the Inbox project should be processed', - default=os.environ.get('TODOIST_INBOX_HANDLING', 'parallel'), - choices=['parallel', 'serial']) - parser.add_argument('--parallel_suffix', default=os.environ.get('TODOIST_PARALLEL_SUFFIX', '=')) - parser.add_argument('--serial_suffix', default=os.environ.get('TODOIST_SERIAL_SUFFIX', '-')) + default='parallel', choices=['parallel', 'serial']) + parser.add_argument('--parallel_suffix', default='=') + parser.add_argument('--serial_suffix', default='-') parser.add_argument('--hide_future', help='Hide future dated next actions until the specified number of days', - default=int(os.environ.get('TODOIST_HIDE_FUTURE', '7')), type=int) + default=7, type=int) args = parser.parse_args() # Set debug - if args.debug or os.environ.get('TODOIST_DEBUG', None): + if args.debug: log_level = logging.DEBUG else: log_level = logging.INFO From 97bd4f7fb4582a79aad2a2917e6f3f86e29ebe20 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 12 Dec 2015 13:54:42 +0000 Subject: [PATCH 3/8] Trap problems using the Todoist API --- nextaction.py | 70 +++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/nextaction.py b/nextaction.py index a1199bf..b2d3b8a 100755 --- a/nextaction.py +++ b/nextaction.py @@ -84,47 +84,51 @@ def main(): # Main loop while True: - api.sync(resource_types=['projects', 'labels', 'items']) - for project in api.projects.all(): - project_type = get_project_type(project) - if project_type: - logging.debug('Project %s being processed as %s', project['name'], project_type) + try: + api.sync(resource_types=['projects', 'labels', 'items']) + except Exception as e: + logging.exception('Error trying to sync with Todoist API: %s' % str(e)) + else: + for project in api.projects.all(): + project_type = get_project_type(project) + if project_type: + logging.debug('Project %s being processed as %s', project['name'], project_type) - items = sorted(api.items.all(lambda x: x['project_id'] == project['id']), key=lambda x: x['item_order']) + items = sorted(api.items.all(lambda x: x['project_id'] == project['id']), key=lambda x: x['item_order']) - for item in items: - labels = item['labels'] + for item in items: + labels = item['labels'] - # If its too far in the future, remove the next_action tag and skip - if args.hide_future > 0 and 'due_date_utc' in item.data and item['due_date_utc'] is not None: - due_date = datetime.strptime(item['due_date_utc'], '%a %d %b %Y %H:%M:%S +0000') - future_diff = (due_date - datetime.utcnow()).total_seconds() - if future_diff >= (args.hide_future * 86400): - if label_id in labels: - labels.remove(label_id) - logging.debug('Updating %s without label as its too far in the future', item['content']) - item.update(labels=labels) - continue + # If its too far in the future, remove the next_action tag and skip + if args.hide_future > 0 and 'due_date_utc' in item.data and item['due_date_utc'] is not None: + due_date = datetime.strptime(item['due_date_utc'], '%a %d %b %Y %H:%M:%S +0000') + future_diff = (due_date - datetime.utcnow()).total_seconds() + if future_diff >= (args.hide_future * 86400): + if label_id in labels: + labels.remove(label_id) + logging.debug('Updating %s without label as its too far in the future', item['content']) + item.update(labels=labels) + continue - # Process item - if project_type == 'serial': - if item['item_order'] == 1: + # Process item + if project_type == 'serial': + if item['item_order'] == 1: + if label_id not in labels: + labels.append(label_id) + logging.debug('Updating %s with label', item['content']) + item.update(labels=labels) + else: + if label_id in labels: + labels.remove(label_id) + logging.debug('Updating %s without label', item['content']) + item.update(labels=labels) + elif project_type == 'parallel': if label_id not in labels: - labels.append(label_id) logging.debug('Updating %s with label', item['content']) + labels.append(label_id) item.update(labels=labels) - else: - if label_id in labels: - labels.remove(label_id) - logging.debug('Updating %s without label', item['content']) - item.update(labels=labels) - elif project_type == 'parallel': - if label_id not in labels: - logging.debug('Updating %s with label', item['content']) - labels.append(label_id) - item.update(labels=labels) - api.commit() + api.commit() logging.debug('Sleeping for %d seconds', args.delay) time.sleep(args.delay) From 130b2f8ccbaa6eabf1d11a3223ef8f7bf211763f Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 12 Dec 2015 14:07:48 +0000 Subject: [PATCH 4/8] Update to use setuptools --- nextaction.py | 8 +++++--- setup.py | 11 +++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/nextaction.py b/nextaction.py index b2d3b8a..3c8ca25 100755 --- a/nextaction.py +++ b/nextaction.py @@ -1,13 +1,15 @@ #!/usr/bin/env python -import time import logging -import sys import argparse -from datetime import datetime +# noinspection PyPackageRequirements from todoist.api import TodoistAPI +import time +import sys +from datetime import datetime + def get_subitems(items, parent_item=None): """Search a flat item list for child items""" diff --git a/setup.py b/setup.py index 09ded72..ea2049e 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from distutils.core import setup +from setuptools import setup setup( name='NextAction', @@ -10,8 +10,11 @@ setup( author_email='andy@tensixtyone.com', description='A more GTD-like workflow for Todoist. Uses the REST API to add and remove a @next_action label from tasks.', entry_points={ - "distutils.commands": [ - "nextaction = nextaction:main", + "console_scripts": [ + "nextaction=nextaction:main", ], - } + }, + install_requires=[ + 'todoist-python', + ] ) From bd3cbead8c87a7ce7d40219734e60b0e1ba02c18 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 12 Dec 2015 14:17:25 +0000 Subject: [PATCH 5/8] Change suffixes to working defaults --- nextaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nextaction.py b/nextaction.py index 3c8ca25..77d7a83 100755 --- a/nextaction.py +++ b/nextaction.py @@ -41,8 +41,8 @@ def main(): parser.add_argument('--debug', help='Enable debugging', action='store_true') parser.add_argument('--inbox', help='The method the Inbox project should be processed', default='parallel', choices=['parallel', 'serial']) - parser.add_argument('--parallel_suffix', default='=') - parser.add_argument('--serial_suffix', default='-') + parser.add_argument('--parallel_suffix', default='.') + parser.add_argument('--serial_suffix', default='_') parser.add_argument('--hide_future', help='Hide future dated next actions until the specified number of days', default=7, type=int) args = parser.parse_args() From f925615238286e58b065deb16b0fe14bfa6e1f36 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 12 Dec 2015 14:20:48 +0000 Subject: [PATCH 6/8] Cleanup README, remove Heroku Procfile --- Procfile | 1 - README.md | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 Procfile diff --git a/Procfile b/Procfile deleted file mode 100644 index 1b951d1..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -nextaction: python nextaction.py \ No newline at end of file diff --git a/README.md b/README.md index 5291b48..6f51124 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ NextAction A more GTD-like workflow for Todoist. Uses the REST API to add and remove a `@next_action` label from tasks. This program looks at every list in your Todoist account. -Any list that ends with `-` or `=` is treated specially, and processed by NextAction. +Any list that ends with `_` or `.` is treated specially, and processed by NextAction. Note that NextAction requires Todoist Premium to function properly, as labels are a premium feature. @@ -19,18 +19,18 @@ Activating NextAction Sequential list processing -------------------------- -If a list ends with `-`, the top level of tasks will be treated as a priority queue and the most important will be labeled `@next_action`. +If a list ends with `_`, the top level of tasks will be treated as a priority queue and the most important will be labeled `@next_action`. Importance is determined by order in the list Parallel list processing ------------------------ -If a list name ends with `=`, the top level of tasks will be treated as parallel `@next_action`s. -The waterfall processing will be applied the same way as sequential lists - every parent task will be treated as sequential. This can be overridden by appending `=` to the name of the parent task. +If a list name ends with `.`, the top level of tasks will be treated as parallel `@next_action`s. +The waterfall processing will be applied the same way as sequential lists - every parent task will be treated as sequential. This can be overridden by appending `_` to the name of the parent task. Executing NextAction ==================== -You can run NexAction from any system that supports Python, and also deploy to Heroku as a constant running service +You can run NexAction from any system that supports Python. Running NextAction ------------------ From 4c82e9465c1153b73c8ff81b74125e67077e5866 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 12 Dec 2015 14:21:45 +0000 Subject: [PATCH 7/8] Add copyright to LICENSE --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index 495c706..5b9d05f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2014 Adam Kramer +Copyright (c) 2015 Andrew Williams Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 3bf53c7436582541a66eda0d0637f66bc282c9eb Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 12 Dec 2015 14:22:14 +0000 Subject: [PATCH 8/8] Version 0.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ea2049e..7f3632b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup setup( name='NextAction', - version='0.2-dev', + version='0.2', py_modules=['nextaction'], url='https://github.com/nikdoof/NextAction', license='MIT',