diff --git a/README.md b/README.md index 6f51124..4f502f9 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,12 @@ 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 project or task ends with `_`, the child 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. +If a project or task name ends with `.`, the child 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 diff --git a/nextaction.py b/nextaction.py index 77d7a83..5854830 100755 --- a/nextaction.py +++ b/nextaction.py @@ -27,6 +27,8 @@ def get_subitems(items, parent_item=None): found = True if item['indent'] == parent_item['indent'] and item['id'] != parent_item['id']: return result_items + elif item['indent'] == required_indent and found: + result_items.append(item) elif item['indent'] == required_indent: result_items.append(item) return result_items @@ -45,6 +47,7 @@ def main(): 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) + parser.add_argument('--onetime', help='Update Todoist once and exit', action='store_true') args = parser.parse_args() # Set debug @@ -84,6 +87,28 @@ def main(): elif name[-1] == args.serial_suffix: return 'serial' + def get_item_type(item): + """Identifies how a item with sub items should be handled""" + name = item['content'].strip() + if name[-1] == args.parallel_suffix: + return 'parallel' + elif name[-1] == args.serial_suffix: + return 'serial' + + def add_label(item, label): + if label not in item['labels']: + labels = item['labels'] + logging.debug('Updating %s with label', item['content']) + labels.append(label) + api.items.update(item['id'], labels=labels) + + def remove_label(item, label): + if label in item['labels']: + labels = item['labels'] + logging.debug('Updating %s without label', item['content']) + labels.remove(label) + api.items.update(item['id'], labels=labels) + # Main loop while True: try: @@ -99,38 +124,53 @@ def main(): 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'] # 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) + remove_label(item, label_id) continue - # 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: - logging.debug('Updating %s with label', item['content']) - labels.append(label_id) - item.update(labels=labels) + item_type = get_item_type(item) + child_items = get_subitems(items, item) + if item_type: + logging.debug('Identified %s as %s type', item['content'], item_type) - api.commit() + if item_type or len(child_items) > 0: + # Process serial tagged items + if item_type == 'serial': + for idx, child_item in enumerate(child_items): + if idx == 0: + add_label(child_item, label_id) + else: + remove_label(child_item, label_id) + # Process parallel tagged items or untagged parents + else: + for child_item in child_items: + add_label(child_item, label_id) + + # Remove the label from the parent + remove_label(item, label_id) + + # Process items as per project type on indent 1 if untagged + else: + if item['indent'] == 1: + if project_type == 'serial': + if item['item_order'] == 1: + add_label(item, label_id) + else: + remove_label(item, label_id) + elif project_type == 'parallel': + add_label(item, label_id) + + logging.debug('%d changes queued for sync... commiting if needed', len(api.queue)) + if len(api.queue): + api.commit() + + if args.onetime: + break logging.debug('Sleeping for %d seconds', args.delay) time.sleep(args.delay) diff --git a/setup.py b/setup.py index 7f3632b..7d8ad34 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup setup( name='NextAction', - version='0.2', + version='0.3', py_modules=['nextaction'], url='https://github.com/nikdoof/NextAction', license='MIT',