Skip to content

Instantly share code, notes, and snippets.

@kbl
Last active September 24, 2023 21:10
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save kbl/5970131 to your computer and use it in GitHub Desktop.
Save kbl/5970131 to your computer and use it in GitHub Desktop.
importing tasks/lists from wunderlist into todoist
# -*- coding: utf8 -*-
import json
import urllib2
import urllib
import sys
import os
from argparse import ArgumentParser
from collections import defaultdict
def log(message):
sys.stdout.write(message + os.linesep)
class TodoistAPI(object):
def __init__(self, email, password):
self.token = self._api_call('login', {'email': email, 'password': password}, add_token=False)['token']
def _api_call(self, api_method, parameters, add_token=True):
parameters = dict(parameters)
if add_token:
parameters.update({'token': self.token})
params = urllib.urlencode(parameters)
url = u'https://todoist.com/API/%s?%s' % (api_method, params)
log('Calling url %s' % url)
return_value = json.load(urllib2.urlopen(url))
if type(return_value) is unicode:
raise Exception('Call finished with error! %s' % return_value)
return return_value
def add_list(self, list_name):
log('Adding list %s' % list_name)
return self._api_call('addProject', {'name': list_name})['id']
def add_task(self, list_id, task_name):
log('Adding task %s to list %d' % (task_name, list_id))
return self._api_call('addItem', {'project_id': list_id, 'content': task_name})['id']
def create_parser():
parser = ArgumentParser()
parser.add_argument('wunderlist_dump_json')
parser.add_argument('todoist_email')
parser.add_argument('todoist_password')
return parser
if __name__ == '__main__':
parser = create_parser()
args = parser.parse_args()
todoist_api = TodoistAPI(args.todoist_email, args.todoist_password)
with open(args.wunderlist_dump_json) as f:
j = json.load(f)
lists = {}
for l in j['lists']:
lists[l['id']] = todoist_api.add_list(l['title'])
lists['inbox'] = todoist_api.add_list('inbox')
for t in j['tasks']:
todoist_api.add_task(lists[t['list_id']], t['title'])
@duellsy
Copy link

duellsy commented Oct 2, 2014

I've made some more mods to this, to allow completed tasks to be marked as completed in todoist, as well as migrate over all notes:

https://gist.github.com/duellsy/1031e84c7c7a9c199145

@jalyst
Copy link

jalyst commented Dec 18, 2014

Thanks @duellsy,

Did you also incorporate @glnarayanan's fixes? His post is just above your last post.
If you did then your script is the latest, incorporating: KBL, your, & glnarayanan's work.

@jalyst
Copy link

jalyst commented Dec 18, 2014

@duellsy,

I tried yours, got it going but it doesn't seem to have brought notes & sub-tasks along with it, completed task are all there I think, I can show you the output of my terminal session?

@marekbrzoska
Copy link

Marvelous, thanks!!!

@manontop
Copy link

I installed python and run the command in terminal and I got this:

Calling url https://todoist.com/API/login?password=%5B234234246645%5D&email=%5Bmyemailaddress%40gmail.com%5D
Traceback (most recent call last):
  File "import.py", line 62, in <module>
    todoist_api = TodoistAPI(args.todoist_email, args.todoist_password)
  File "import.py", line 16, in __init__
    self.token = self._api_call('login', {'email': email, 'password': password}, add_token=False)['token']
  File "import.py", line 28, in _api_call
    raise Exception('Call finished with error! %s' % return_value)
Exception: Call finished with error! LOGIN_ERROR

The login info is correct I typed it as you advised:
python import.py [wunderlist_dump.json] [wrote my email here] [wrote my password here]

Is it required to type the email and password somewhere inside .py file as well? Please advice what am I doing wrong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment