Skip to content

Instantly share code, notes, and snippets.

@vzvu3k6k
Last active February 14, 2017 15:40
Show Gist options
  • Save vzvu3k6k/f028c6dbc6d53023e3846b3f81f8a096 to your computer and use it in GitHub Desktop.
Save vzvu3k6k/f028c6dbc6d53023e3846b3f81f8a096 to your computer and use it in GitHub Desktop.
A migration helper from Wunderlist to Todoist for node.js
tasks.json
secret.json
### https://raw.github.com/github/gitignore/a98a0444694f30e52fb6eef9aec08e9b5e211a44/Node.gitignore
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity

Wunderlist -> Todoist

This is a migration helper from Wunderlist to Todoist.

Collects tasks in Wunderlist and imports them to a Todoist project with the same name with its list in Wunderlist. If there is not a corresponding project in Todoist, it creates a new project.

Recurrence is supported. Subtask is not supported.

Requirements

  • Node.js which supports async and await

Usage

Run npm install.

Create ./secret.json and fill your tokens as below:

{
  "wunderlist": {
    "clientID": "...",
    "accessToken": "..."
  },
  "todoist": {
    "token": "..."
  }
}

Run node --harmony-async-await fetch_tasks_from_wunderlist.js, then tasks in Wunderlist will be exported into ./task.json.

Run node --harmony-async-await add_tasks_to_todoist.js to import ./task.json to your Todoist account.

License

See the license section in package.json.

let got = require('got')
let uuid = require('uuid/v4')
let secret = require('./secret.json')
let tasks = require(process.argv[2] || './tasks.json')
// Send a request to Todoist API
async function req (body) {
for (let key of Object.keys(body)) {
body[key] = JSON.stringify(body[key])
}
body = Object.assign({ token: secret.todoist.token }, body)
console.log(body)
let response = await got('https://todoist.com/API/v7/sync', { body })
return JSON.parse(response.body)
}
// Translate a Wunderlist task into
// an arg for a command to add an item to Todoist
function translateTask (task) {
let item = {}
item.content = task.title
if (task.due_date) {
// Set 23:59:59 as the official apps and web app does
// so that the due date is displayed like `Jan 1` or `150 days ago`,
// not `Jan 1 00:00` or `150 days ago 00:00`.
let [year, month, day] = task.due_date.split('-')
item.due_date_utc = (new Date(+year, --month, +day, 23, 59, 59))
.toISOString().slice(0, 19)
if (task.recurrence_type) {
item.date_string = `every ${task.recurrence_count} ${task.recurrence_type}`
} else {
item.date_string = null
}
}
return item
}
;(async () => {
let projects = await req({ sync_token: '*', resource_types: ['projects'] })
for (let [name, items] of Object.entries(tasks)) {
let commands = []
// Get an appropriate projectID
let projectID
if (name === 'inbox') {
projectID = null
} else {
let project = projects.projects.find(p => p.name === name)
if (project) {
projectID = project.id
} else {
// If project is not found, add a command to create it.
projectID = uuid()
commands.push({
type: 'project_add',
uuid: uuid(),
temp_id: projectID,
args: { name }
})
}
}
for (let item of items) {
commands.push({
type: 'item_add',
uuid: uuid(),
temp_id: uuid(),
args: Object.assign({ project_id: projectID }, translateTask(item))
})
}
console.log(commands)
console.log(await req({ commands }))
}
})().catch((err) => console.error(err))
let secret = require('./secret.json')
let fs = require('fs')
let WunderlistSDK = require('wunderlist')
let wunderlistAPI = new WunderlistSDK(secret.wunderlist)
wunderlistAPI.http.lists.all()
.done(async (lists) => {
let tasks = Object.create(null)
for (let list of lists) {
if (!tasks[list.title]) {
tasks[list.title] = []
}
let tasksForList = await wunderlistAPI.http.tasks.forList(list.id)
tasks[list.title] = tasks[list.title].concat(tasksForList)
}
fs.writeFileSync('./tasks.json', JSON.stringify(tasks, null, 2))
console.log('done')
})
.fail((err) => {
console.error(err)
})
{
"name": "wunderlist_to_todoist",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "vzvu3k6k <vzvu3k6k@gmail.com>",
"license": "Apache-2.0",
"dependencies": {
"got": "^6.7.1",
"uuid": "^3.0.1",
"wunderlist": "^0.1.3"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment