はじめに
以下の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を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
所感
時間を入力する時に、端数が入っていると計算が面倒になるので
UnsplashのGlenn Carstens-Petersが撮影した写真