NozbeからTodoistにデータ移行するツール作った

2 min read読了の目安(約2500字

はじめに

タスク管理ツールで1年近くNozbeを使ってきましたが、
値段が高いわりにそんなに使いこなしてなくて勿体ないので、
もうちょっと安価なTodoistに移行することにしました。

どちらともデータのエクスポート・インポート機能がありますが、
公式・非公式ともに、データ移行ツールは提供されていないようです。
なので、作りました!

https://github.com/the-red/nozbe-to-todoist

NPMのCLIツールなので、これで一発起動できます。

npx nozbe-to-todoist path/to/data.json

distフォルダ配下に、todoist-プロジェクト名.csvというのが沢山できます。

解説

Nozbe

https://help.nozbe.com/ja/account/settings/#data_export
Nozbeのデータは、全プロジェクト、完了済みも含んだすべてを
zipファイルでまとめてダウンロードすることができます。
その中のdata.jsonが一番重要。
色んなプロパティがあって全部は把握してないですが、
projecttaskの2つさえチェックすれば最低限OK。
data.json
{
  "project": [...],
  "task": [...],
  ...
}

Todoist

https://todoist.com/ja/help/articles/how-to-format-your-csv-file-so-you-can-import-it-into-todoist
Nozbeとの大きな違いが4つ。
  • CSV形式であること
  • プロジェクト単位で1ファイルずつ作成すること
  • 「サブタスク」の概念があること
  • 「スター」ではなく「優先度」として4段階選べること

サンプルのCSVを落として、それを書き換えてインポートするのがオススメと書いてある。
今回はNozbeが起点なので、自分でプログラム内で作ります。

設計方針

  • Nozbeのproject単位で、TodoistのCSVファイルを作る
  • Nozbeでスター付きのタスクは優先度1、その他は優先度4
  • Nozbeのタスクコメント1つを、Todoistのサブタスク1つにする
    • Todoistにもタスクコメント機能はあるけど、個人的に「サブタスク」的な使い方をしていたので。

コード

#!/usr/bin/env node

const stringify = require('csv-stringify/lib/sync.js');
const fs = require('fs');
const path = require('path');
const json = require(path.resolve(process.argv[2]));

fs.mkdirSync('dist', { recursive: true });
const opts = { header: true, bom: true };

// この2つは必要ないけど、出しておくとNozbeのデータ構造を目視確認しやすい
fs.writeFileSync(`dist/project.csv`, stringify(json.project, opts));
fs.writeFileSync(`dist/task.csv`, stringify(json.task, opts));

json.project.forEach((project) => {
  const todoistTemplate = json.task
    .filter((_) => _.project_id === project.id)
    .flatMap((_) => {
      // メインタスク
      const task = {
        TYPE: 'task',
        CONTENT: _.name, // タスク名
        DESCRIPTION: '',
        PRIORITY: _.next || '4', // 優先度(1が最優先、4がデフォルト)
        INDENT: '1', // 1がメインタスク
        AUTHOR: '',
        RESPONSIBLE: '',
        DATE: _.datetime, // 期限
        DATE_LANG: '',
        TIMEZONE: '',
      };
      // コメントの数だけサブタスク作成
      const subtasks = _.comments.map((_) => ({
        TYPE: 'task',
        CONTENT: _.body, // コメント本文をサブタスク名に
        PRIORITY: '4', // 優先度なし(明記しないと1扱いになっちゃう)
        INDENT: '2', // 2がサブタスク
      }));
      return [task, ...subtasks]; // flatMapとっても便利
    });

  const todoistFileName = `dist/todoist-${project.name}.csv`;
  fs.writeFileSync(todoistFileName, stringify(todoistTemplate, opts));
  console.log(todoistFileName);
});

さて、どれくらい需要あるだろうか・・・?
僕はこれで一発流して終わりましたけど、
需要ありそうならWebアプリ化も検討しますのでコメントくださいね!