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'])
@nickhammond
Copy link

Works for me, the only thing I changed was to not import completed tasks from Wunderlist. That might be what @cjsnet was seeing too where he mentioned everything was going into the "inbox".

for t in j['tasks']:
    if 'completed_at' not in t:
        todoist_api.add_task(lists[t['list_id']], t['title'])

@ludob
Copy link

ludob commented Jul 13, 2014

Works well as it is for me.
Just need to edit the json file from Wunderlist because of text encoding. Special characters in French are not supported by the script (for example: à).
Otherwise, perfect, thanks a lot kbl!

@ajtb
Copy link

ajtb commented Sep 4, 2014

Does not work for me. All I get is:

Traceback (most recent call last): File "import.py", line 54, in <module> for l in j['lists']: KeyError: 'lists'

Any chance of checking on it kbl?

@silvoc
Copy link

silvoc commented Sep 16, 2014

I have got the same problem....

Please kbl :)

Thanks in advance

@glnarayanan
Copy link

@kbl

I've updated the gist so that it fixes the issues mentioned above. It's got to do with some changes in the Wunderlist JSON structure :P

Please update the original gist with my revision 👍

https://gist.github.com/glnarayanan/a5fb88089e8ba4a43ddc/4723919b790062cfce7492ef1ec4475f07dcfdb8

@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