ObsidianでTaskChuteをやってみる

created:

updated:

thumbnail

はじめに

以下のNote記事で、ObsidianでTaskChuteをやっているのを見て、いいなぁと思った。だけど、ちょっと自分の使いたい感じじゃなかったので、自分でもありものを使ってできないかと思ってやってみた。

DataViewやDataViewjsを使って、集計する。ただ、フォルダ名に絵文字があると、DataViewがエラーになるのと、見出しに時間枠を"08:00-10:00"というフォーマットで書くとコロンがエスケープされるので、そこだけいまいち、、、。

2023/10/07更新 TaskChuteでは開始・終了時間、所要時間を書くが、Obsidianの標準機能では当然ながらサポートされていないので、@Tadashi_MAMANさんの📗モバイルからワンタップで現在時刻を挿入したいを参考に開始・終了時間をコマンドで入力できるようにして、所要時間は開始・終了時間から計算するようにした。

実現イメージ

Obsidianでデイリーノートを以下のようなフォーマットを作る。

# 08:00 - 10:00 120min 午前 I
- [x] Check emails [duration:: 15]
- [x] summarize Log issues [duration:: 40]
# 10:00 - 12:00 120min 午前 II
- [x] summarize issues [duration:: 40]
- [ ] Evaluation [duration:: 40]
# 13:00 - 15:00 120min 午後 I
- [ ] Meeting with XXX [duration:: 40]
- [ ] Design App [duration:: 30]
# 15:00 - 17:00 120min 午後 II
- [ ] Plan activities [duration:: 45]
- [ ] Design App [duration:: 45]

それが以下のように表示される。 DataViewのイメージ

実現方法

集計部分

DataViewを2つ書く。

TABLE sum(rows.T.duration) as "Total" FROM "" FLATTEN file.tasks AS T WHERE file.name = this.file.name AND T.duration GROUP BY meta(T.section).subpath as "Section"
let unfinishedTasks = dv.current().file.tasks.filter(t => !t.completed);

//dv.paragraph(`tasks: ${JSON.stringify(unfinishedTasks)}`);

let totalDuration = 0;
for (var i = 0; i < unfinishedTasks.length; i++){ 
    if(isNaN(unfinishedTasks[i].duration)){
		continue;
	}
    totalDuration += unfinishedTasks[i].duration; 
}

let finishTime = new Date();
finishTime.setTime(finishTime.getTime() + totalDuration * 60 * 1000);  // convert minutes to milliseconds

dv.paragraph(`終了予定時刻: ${finishTime.toLocaleString()}`);

入力部分

上記のような集計をするために、以下のようなフォーマットで開始時間、終了時間、所要時間を入力をする
所要時間はルーチンの場合はテンプレートに記入しておく。割込みタスクの場合は、タスク名を書いて、開始時間を入力し、タスク終了時に終了時間を入力してから、所要時間を計算する。

- [x] 顔を洗う  [start:: 08:26:22]  [end:: 08:36:05]  [duration::10]

作業開始

  [start::<%*
let now = tp.date.now("HH:mm:ss");
let seconds = parseInt(now.split(":")[2]);
let minutes = parseInt(now.split(":")[1]);

// 秒を30秒未満なら繰り下げ、30秒以上なら繰り上げ
if (seconds < 30) {
  seconds = 0;
} else {
  seconds = 0;
  minutes += 1;
}
// 分を5分刻みに丸める
minutes = Math.round(minutes / 5) * 5;

// 分が60以上になった場合、時間も繰り上げる
if (minutes === 60) {
  minutes = 0;
  // 次の時間の00秒を表すために秒も0に設定
  seconds = 0;
  now = now.replace(/:\d+:\d+/, `:${minutes < 10 ? "0" : ""}${minutes}:${seconds < 10 ? "0" : ""}${seconds}`);
} else {
  now = now.replace(/:\d+:\d+/, `:${minutes < 10 ? "0" : ""}${minutes}:${seconds < 10 ? "0" : ""}${seconds}`);
}
_%> <% now %>]

終了時間

  //上と同じでstartをendに変えるだけ

所要時間

  [duration::<%*
  let cmEditorAct = this.app.workspace.activeLeaf.view.editor;

  // Get contents of line under custor
  let currentCursor = cmEditorAct.getCursor();
  let currentLine = currentCursor.line;
  let lineContents = cmEditorAct.getLine(currentLine);
  let taskDescription = lineContents;
  const startRegex = /\[start:: (\d{2}:\d{2}:\d{2})\]/;
  const endRegex = /\[end:: (\d{2}:\d{2}:\d{2})\]/;

  const startMatch = lineContents.match(startRegex);
  const endMatch = lineContents.match(endRegex);
  let elapsedTime = "";
  if (startMatch && endMatch) {
	const startTime = new Date(`1970-01-01T${startMatch[1]}Z`);
    const endTime = new Date(`1970-01-01T${endMatch[1]}Z`);

    elapsedTime = Math.round((endTime - startTime) / (1000 * 60)); // 経過時間を分単位で計算
  }
_%> <% elapsedTime %>]

コードスニペットはChatGPTで書いた

ChatGPTでDataViewを書いてもらって、エラーになったので、ChatGPTのコードをコピペして検索して、うまく行っているものを探した。で、途中でTaskを集計した時にデータにうまくアクセス出来なかったので、JSON.stringifyでデータの構造を表示して、それをまたChatGPTにJSONを渡して、コードを書いてもらった。これで、うまくいった。

https://forum.obsidian.md/t/dataview-or-other-query-for-all-items-in-page-section/36437/4
https://forum.obsidian.md/t/group-by-woes/51418/2
https://obsidian.rocks/dataview-in-obsidian-a-beginners-guide/
https://forum.obsidian.md/t/dataviewjs-snippet-showcase/17847/84

ちなみにデータの構造はこんな感じ。
Alt text

所感

時間を入力する時に、端数が入っていると計算が面倒になるので

UnsplashGlenn Carstens-Petersが撮影した写真