From e94755348e27a11775f95251c8c506c8d3f6e4bb Mon Sep 17 00:00:00 2001 From: Matt Snider Date: Sat, 30 Mar 2019 18:34:11 +0100 Subject: [PATCH] Add --map-project SRC=DST option to map/translate project names --- todoist_taskwarrior/cli.py | 19 ++++++++++++++++--- todoist_taskwarrior/utils.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/todoist_taskwarrior/cli.py b/todoist_taskwarrior/cli.py index ce5c160..a2bcc2c 100644 --- a/todoist_taskwarrior/cli.py +++ b/todoist_taskwarrior/cli.py @@ -27,7 +27,7 @@ def cli(): @cli.command() def synchronize(): """Update the local Todoist task cache. - + This command accesses Todoist via the API and updates a local cache before exiting. This can be useful to pre-load the tasks, and means `migrate` can be run without a network connection. @@ -67,8 +67,12 @@ def clean(): 'during the import.') @click.option('--sync/--no-sync', default=True, help='Enable/disable Todoist synchronization of the local task cache.') +@click.option('-p', '--map-project', metavar='SRC=DST', multiple=True, + callback=utils.validate_map_project, + help='Project names specified will be translated from SRC to DST. ' + 'If DST is omitted, the project will be unset when SRC matches.') @click.pass_context -def migrate(ctx, interactive, sync): +def migrate(ctx, interactive, sync, map_project): """Migrate tasks from Todoist to Taskwarrior. By default this command will synchronize with the Todoist servers @@ -81,6 +85,12 @@ def migrate(ctx, interactive, sync): whether to skip, rename, change the priority, or change the tags, before moving on to the next task. + Use --map-project to change or remove the project. For example, in the + following invocation, the project FOO will be changed to BAR and the + project property will be unset when it is BAZ: + \r + --map-project FOO=BAR --map-project BAZ= + This command can be run multiple times and will not duplicate tasks. This is tracked in Taskwarrior by setting and detecting the `todoist_id` property on the task. @@ -95,7 +105,10 @@ def migrate(ctx, interactive, sync): data = {} tid = data['tid'] = task['id'] name = data['name'] = task['content'] - data['project'] = todoist.projects.get_by_id(task['project_id'])['name'] + data['project'] = utils.try_map( + map_project, + todoist.projects.get_by_id(task['project_id'])['name'], + ) data['priority'] = utils.parse_priority(task['priority']) data['tags'] = [ todoist.labels.get_by_id(l_id)['name'] diff --git a/todoist_taskwarrior/utils.py b/todoist_taskwarrior/utils.py index 49a5fbb..72ab668 100644 --- a/todoist_taskwarrior/utils.py +++ b/todoist_taskwarrior/utils.py @@ -1,6 +1,33 @@ +import click import re from datetime import datetime +""" Validation """ + +def validate_map_project(ctx, param, value): + map_project = {} + for mapping in value: + try: + src, dst = mapping.split('=', 2) + except ValueError: + raise click.BadParameter('--map-project needs to be of the form SRC=DST') + + if dst == '': + dst = None + map_project[src] = dst + return map_project + + +""" Mappings """ + +def try_map(m, value): + """Maps/translates `value` if it is present in `m`. """ + if value in m: + return m[value] + else: + return value + + """ Priorities """ PRIORITY_MAP = {1: None, 2: 'L', 3: 'M', 4: 'H'} @@ -12,7 +39,7 @@ def parse_priority(priority): These values map very easily to eachother, as Todoist priority 1 indicates that no priority has been set. """ - return PRIORITY_MAP[priority] + return PRIORITY_MAP[int(priority)] """ Dates """