JupyterLite builder
kyozai.jsonのbuild.kindにjupyterliteを指定すると、Notebookを置いたdirectoryからJupyterLite Applicationを作ります。JupyterLite buildはhosted buildで実行します。
例: kyozai-examples/jupyterlite
{
"build": {
"kind": "jupyterlite",
"contents": "files",
"static": "site",
"hooks": "hooks.js"
},
"artifacts": {
"assets": "dist/assets"
},
"paths": {
"learner": "/lab/index.html",
"instructor": "/dashboard.html"
},
"capabilities": ["personal-objects"],
"egress": {
"browser": ["https://cdn.jsdelivr.net"]
}
}contents: JupyterLiteの初期contentsにするdirectory。Notebookやdata fileをsubdirectoryごと置く。受講者が初めて開いたときにPersonal Objectsへ複製され、以後は受講者ごとの内容になるstatic: assetsのrootへそのまま配置するdirectory。教員用画面などのHTML、CSS、JavaScriptを置く。JupyterLiteが生成するfileと同じpathは指定できない。HTML内のinline scriptはContent Security Policyで実行されないため、scriptは別fileにするhooks: Notebookの保存・削除時に呼ぶJavaScriptまたはTypeScript module。Application内のfileだけをimportできる。省略すると保存時に何も記録しない
contentsは必須です。staticとhooksは必要な場合だけ指定します。artifacts.assetsにはbuilderの出力先を指定し、paths.learnerには/lab/index.htmlを指定します。
JupyterLite builderは、受講者のNotebookをPersonal Objectsへ保存するdriveを組み込みます。このためcapabilitiesにpersonal-objectsが必要です。Python packageはhttps://cdn.jsdelivr.netから取得するため、egress.browserにも指定します。不足している場合はbuildが失敗します。
hookとstaticに置いた教員用画面は、いずれもPlatform APIをfetchで利用します。Application Databaseへ記録する場合は、capabilitiesにapplication-databaseを加え、databaseにmigrationとtableを定義します。
Hooks
hooksのmoduleはsaved、removed、executedをexportできます。driveはNotebookの保存・複製・checkpoint復元でsavedを、削除でremovedを呼びます。名前変更では元のpathでremoved、新しいpathでsavedを呼びます。cellの実行が終わるたびに、成否にかかわらずexecutedを呼びます。
const TABLE_URL = '/_kyozai/capabilities/database/membership/notebook_progress';
export async function saved(model) {
if (model.type !== 'notebook') {
return;
}
await record('PUT', {
key: { path: model.path },
values: { last_modified: model.last_modified },
});
}
export async function removed(model) {
if (model.type !== 'notebook') {
return;
}
await record('DELETE', { key: { path: model.path } });
}
async function record(method, body) {
const response = await fetch(TABLE_URL, {
method,
headers: { 'content-type': 'application/json' },
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error(`${TABLE_URL}: ${response.status}`);
}
}modelはJupyterLabのContents.IModelで、path、name、type(notebook、file、directory)、last_modified、contentを持ちます。savedのcontentにはNotebookのJSONなど保存した内容が入り、removedのcontentはnullのことがあります。directoryの操作では、directory自身とその配下のfileごとに呼ばれます。初期contentsの複製時には呼ばれません。
hookは受講者のbrowserで、その受講者の権限で実行されます。Personal ObjectsとApplication Databaseへの書き込みは、いずれもその受講者自身のscopeに限られます。
savedとremovedはPersonal Objectsへの保存・削除が完了した後に呼ばれます。hookが例外を投げるとJupyterLiteには保存の失敗として表示されますが、Personal Objectsの内容は既に更新されています。同じmodelで再度呼ばれても結果が変わらないように、Membership scopeのupsertとdeleteのような冪等な操作で記録します。
executed(model, execution)のmodelはsavedと同じ形で、contentには実行直後のNotebookの内容が入ります。この内容は保存されておらず、last_modifiedは最後に保存した時刻のままです。execution.cellは実行したcellのnbformat JSONで、id、source、execution_count、outputsを持ちます。errorはoutputsのoutput_type: "error"(ename、evalue、traceback)として入ります。execution.successは実行が成功したかどうかです。実行時刻は渡さないので、必要ならhookで取得します。executedが例外を投げてもcellの実行は失敗にならず、browserのconsoleにerrorとして残ります。
初期contentsの一覧
builderは初期contentsの一覧を/kyozai/contents.jsonとして配信します。各要素はpath、type(notebook、file、directory)、format、mimetype、sizeを持ち、教員用画面でNotebookの一覧を得る場合などに利用できます。
Personal Objectsのkey
JupyterLite builderのdriveは、受講者のfileを次のkeyでPersonal Objectsへ保存します。教員画面から受講者のNotebookを読む場合はこのkeyを使います。
contents/<path>: fileまたはdirectoryのContents.IModelをJSONで保存するcheckpoints/<path>/<id>: checkpointinitial-content/1: 初期contentsを複製済みであることを示す。存在する受講者には、Releaseを更新しても新しい初期contentsは配布されない