日刊記録sheetを自動生成するUserScript
その日までの日刊記録sheetを自動でつくるUserScript
開発動機
振り返り(takker-workflow@0.0.1)を自動生成するUserScriptで振り返り(takker-workflow@0.0.1)の手間が少し減って嬉しかった
日刊記録sheetの生成も手間なので省きたくなった
起床時にスマホで生成ボタンを毎回押さないといけない
色々とUIまわりの修正をさぼっているせいで、PCからだと生成できなくなっている
✅日刊記録sheetの生成時に、その日やることをタスクリンクから自動で書き込みたい
機能
/takker-memexへのアクセス時に生成する
自動生成のタイミング等は振り返り(takker-workflow@0.0.1)を自動生成するUserScriptと同じ
タスクリンクはtakker-workflow@0.0.1/listで取得し、takker-workflow@0.0.1/linkToTaskで変換する
日刊記録sheetの生成はtakker99/takker-schedulerを使う
バグ
/takkerに書いたタスクリンクを取り忘れていた
いずれにせよAPIを叩く必要があるなら、scrapbox.Project.pagesの生成を待つメリットがあんまりないな
いや、とっているはずだよな。list("takker")を実行しているし……
なにかがおかしい
自projectのタスクリンクを取得出来ていない
?自動生成時にタスクリンクを書き漏らしている~@2023-04-02
2023-03-10
14:19:27 scrapbox.Project.pagesはキャッシュして使う
2023-01-24
11:59:56 テスト終了
importのtakker-memex.jsにくわえる
11:41:25 できた
動くかな?
/icons/hr.icon
タスクリンクの書式変更.@2023-09-10前に使っていたもの
今はtakker99/takker-schedulerに移して改変したものを使っている
$ deno check --remote -r=https://scrapbox.io "https://scrapbox.io/api/code/takker/日刊記録sheetを自動生成するUserScript/main.ts"
https://scrapbox-bundler.vercel.app?url=https://scrapbox.io/api/code/takker/日刊記録sheetを自動生成するUserScript/main.ts&bundle&minify&run&reload
code:main.ts
/// <reference no-default-lib="true"/>
/// <reference lib="esnext"/>
/// <reference lib="dom"/>
import {
useStatusBar,
patch,
makeSocket,
disconnect,
Socket,
} from "../scrapbox-userscript-std/mod.ts";
import { delay as sleep } from "../deno_std%2Fasync/mod.ts";
import eachDayOfInterval from "../date-fns/eachDayOfInterval.ts";
import { addDays } from "../date-fns/addDays.ts";
import { lightFormat } from "../date-fns/lightFormat.ts";
import { readProgrammableTasks } from "../takker99%2Ftakker-scheduler/plan.ts";
import { toString, Task } from "../takker99%2Ftakker-scheduler/deps.ts";
import { toTitle, format } from "../takker99%2Ftakker-scheduler/diary.ts";
import { list, linkToTask } from "../takker99%2Ftakker-scheduler/workflow.ts";
import type { Scrapbox } from "../scrapbox-jp%2Ftypes/userscript.ts";
declare const scrapbox: Scrapbox;
const project = "takker-memex";
type Key = ${number}-${string}-${string};
/** 日付ごとに一意なkeyを生成する */
const toKey = (date: Date): Key => `${
date.getFullYear()
}-${${date.getMonth() + 1}.padStart(2, "0")}-${${date.getDate()}.padStart(2, "0")}`;
// 一回のみ実行する
// awaitはつけない
// - moduleのtop levelにあるので、awaitするとほかのmoduleの読み込みをblockしてしまう
(async () => {
if (scrapbox.Project.name !== project) return;
// scrapbox.Project.pagesが生成されるまで待つ
// 生成したpagesはcacheしておく
let pages = scrapbox.Project.pages;
await new Promise<void>((resolve) => {
const timer = setInterval(() => {
if (pages.length === 0) {
pages = scrapbox.Project.pages;
return;
}
clearInterval(timer);
resolve();
}, 2000);
});
const start = new Date(2023, 0, 24);
const now = new Date();
const interval = { start, end: now };
//console.debug(eachDayOfInterval(interval));
// 生成する振り返りページの日付リスト
const dates = eachDayOfInterval(interval).filter((date) => {
const title = toTitle(date);
const page = pages.find((page) => page.title === title);
return !page || !page.exists;
});
//console.debug("create the following diaries:", dates)
if (dates.length === 0) return;
const { render, dispose } = useStatusBar();
let socket: Socket | undefined;
try {
const tasks = new Map<Key, Task[]>();
/** projectにある全てのタスクリンクをtask lineに変換してtasksに格納する */
const crawlTaskLinks = async (project: string) => {
for await (const { task: taskLink } of list(project)) {
const task = linkToTask(taskLink);
if (!task) return;
const key = toKey(task.base);
tasks.set(key, [...(tasks.get(key) ?? []), task]);
}
};
render(
{ type: "spinner" },
{ type: "text", text: load tasks for ${dates.length} diary pages},
);
// タスクリンクを取得する
await Promise.all([
crawlTaskLinks("takker-memex"),
crawlTaskLinks("takker"),
// 並行してprogrammable tasksも取得する
(async () => {
for (const date of dates) {
const key = toKey(date);
for await (const tasks_ of readProgrammableTasks(date)) {
tasks.set(key, [...(tasks.get(key) ?? []), ...tasks_]);
}
}
})(),
]);
// ページを作る
socket = await makeSocket();
let counter = 0;
render(
{ type: "spinner" },
{ type: "text", text: create 0/${dates.length} diary pages...},
);
for (const date of dates) {
const title = toTitle(date);
const lines = tasks.get(toKey(date))?.map?.((task) => toString(task)) ?? [];
const dateTag = #${lightFormat(date, "yyyy-MM-dd")};
const datetimeTag = #${lightFormat(date, "yyyy-MM-dd HH:mm:ss")};
await patch(
project,
title,
(oldLines, { persistent }) => {
// 新規作成のみ
if (persistent) return;
const newLines = ...oldLines.map((line) => line.text), ...lines;
if (newLines.some((line) => line.includes(dateTag))) return format(newLines);
return format(...newLines, datetimeTag);
},
{ socket },
);
counter++;
render(
{ type: "spinner" },
{ type: "text", text: create ${counter}/${dates.length} diary pages...},
);
}
render(
{ type: "check-circle" },
{ type: "text", text: created ${dates.length} diary pages.},
);
} catch(e: unknown) {
render(
{ type: "exclamation-triangle" },
{ type: "text", text: e instanceof Error ?
${e.name} ${e.message} :
Unknown error! (see developper console),
},
);
console.error(e);
} finally {
if (socket) await disconnect(socket);
await sleep(1000);
dispose();
}
})();
#2023-09-17 19:15:17
#2023-03-10 14:20:21
#2023-02-03 06:39:42
#2023-01-31 06:05:24
#2023-01-24 11:36:21
#2022-12-07 12:58:48
#2022-11-19 05:58:04
#2022-11-17 20:45:48
#2022-11-14 10:30:25
#2022-11-08 06:15:12
#2022-11-03 11:19:56