todoist-taskwarrior/todoist_taskwarrior/cli.py

236 lines
5.6 KiB
Python
Raw Normal View History

2018-11-23 22:36:32 +01:00
import click
import os
import sys
from taskw import TaskWarrior
from todoist.api import TodoistAPI
from . import utils
todoist = None
taskwarrior = None
""" CLI Commands """
@click.group()
def cli():
pass
@cli.command()
def synchronize():
"""Sync the local task database and then exit. """
important('Syncing tasks with todoist... ', nl=False)
todoist.sync()
success('OK')
2018-11-23 22:36:32 +01:00
@cli.command()
@click.option('-i', '--interactive', is_flag=True, default=False)
@click.option('--sync/--no-sync', default=True)
@click.pass_context
def migrate(ctx, interactive, sync):
if sync:
ctx.invoke(synchronize)
2018-11-23 22:36:32 +01:00
tasks = todoist.items.all()
important(f'Starting migration of {len(tasks)}...\n')
for idx, task in enumerate(tasks):
data = {}
tid = data['tid'] = task['id']
name = data['name'] = task['content']
data['project'] = todoist.projects.get_by_id(task['project_id'])['name']
data['priority'] = utils.parse_priority(task['priority'])
data['tags'] = [
2018-11-23 22:36:32 +01:00
todoist.labels.get_by_id(l_id)['name']
for l_id in task['labels']
]
data['entry'] = utils.parse_date(task['date_added'])
data['due'] = utils.parse_date(task['due_date_utc'])
data['recur'] = utils.parse_recur(task['date_string'])
2018-11-23 22:36:32 +01:00
important(f'Task {idx + 1} of {len(tasks)}: {name}\n')
if check_task_exists(tid):
info(f'Already exists (todoist_id={tid})\n')
elif not interactive:
add_task(**data)
else:
task_prompt(**data)
def check_task_exists(tid):
""" Given a Taskwarrior ID, check if the task exists """
taskwarrior_id, _ = taskwarrior.get_task(todoist_id=tid)
return taskwarrior_id is not None
2018-11-23 22:36:32 +01:00
def add_task(tid, name, project, tags, priority, entry, due, recur):
"""Add a taskwarrior task from todoist task
Returns the taskwarrior task.
"""
info(f"Importing '{name}' ({project}) - ", nl=False)
try:
tw_task = taskwarrior.task_add(
name,
project=project,
tags=tags,
priority=priority,
entry=entry,
due=due,
recur=recur,
todoist_id=tid,
)
2018-11-23 22:36:32 +01:00
except:
error('FAILED')
else:
success('OK')
return tw_task
def task_prompt(**task_data):
"""Interactively add tasks
y - add task
n - skip task
r - rename task
p - change priority
t - change tags
q - quit immediately
? - print help
"""
callbacks = {
'y': lambda: task_data,
'n': lambda: task_data,
# Rename
'r': lambda: {
**task_data,
'name': name_prompt(task_data['name']),
},
# Edit tags
't': lambda: {
**task_data,
'tags': tags_prompt(task_data['tags']),
},
# Edit priority
'p': lambda: {
**task_data,
'priority': priority_prompt(task_data['priority']),
},
# Quit
'q': lambda: exit(1),
# Help message
'?': lambda: task_prompt_help() or task_data,
}
response = None
while response not in ('y', 'n'):
prompt_text = (
stringify_task(**task_data)
+ important_msg(f"\nImport this task?")
)
response = click.prompt(
prompt_text,
type=click.Choice(callbacks.keys()),
show_choices=True,
)
# Execute operation
task_data = callbacks[response]()
if response == 'n':
error('Skipping task\n')
return
return add_task(**task_data)
def task_prompt_help():
lines = [
x.strip() for x in
task_prompt.__doc__.split('\n')
]
error('\n'.join(lines))
def tags_prompt(tags):
return click.prompt(
important_msg('Set tags'),
default=' '.join(tags),
show_default=False,
value_proc=lambda x: x.split(' ')
)
def priority_prompt(priority):
return click.prompt(
important_msg('Set priority'),
default='',
type=click.Choice([None, 'L', 'M', 'H']),
value_proc=lambda x: None if '' else x,
show_default=False,
)
def name_prompt(name):
return click.prompt(
important_msg('Set name'),
default=name,
value_proc=lambda x: x.strip()
)
""" Output Utils """
2018-11-23 22:36:32 +01:00
def important(msg, **kwargs):
click.echo(important_msg(msg), **kwargs)
def important_msg(msg):
return click.style(msg, fg='blue', bold=True)
2018-11-23 22:36:32 +01:00
def info(msg, **kwargs):
click.echo(msg, **kwargs)
def success(msg, **kwargs):
click.echo(click.style(msg, fg='green', bold=True))
def error(msg, **kwargs):
click.echo(click.style(msg, fg='red', bold=True))
def stringify_task(**task_data):
string = ''
for key, value in task_data.items():
key = click.style(key, underline=True)
if isinstance(value, list):
value = ' '.join(value)
elif value is None:
value = ''
string += f'{key}: {value}\n'
return string
2018-11-23 22:36:32 +01:00
""" Entrypoint """
if __name__ == '__main__':
is_help_cmd = '-h' in sys.argv or '--help' in sys.argv
todoist_api_key = os.getenv('TODOIST_API_KEY')
if todoist_api_key is None and not is_help_cmd:
exit('TODOIST_API_KEY environment variable not specified. Exiting.')
todoist = TodoistAPI(todoist_api_key)
# Create the TaskWarrior client, overriding config to
# create a `todoist_id` field which we'll use to
# prevent duplicates
taskwarrior = TaskWarrior(config_overrides={
'uda.todoist_id.type': 'string',
})
2018-11-23 22:36:32 +01:00
cli()