Skip to content

JupyterLite builder ​

kyozai.jsonのbuild.kindにjupyterliteを指定すると、Notebookを置いたdirectoryからJupyterLite Applicationを作ります。JupyterLite buildはhosted buildで実行します。

例: kyozai-examples/jupyterlite Deploy on kyoz.ai

json
{
  "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を呼びます。

js
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>: checkpoint
  • initial-content/1: 初期contentsを複製済みであることを示す。存在する受講者には、Releaseを更新しても新しい初期contentsは配布されない