Browse Source

[GIN] 研究リポジトリ初期設定を完了

jovyan 2 years ago
parent
commit
6a511f39b2
66 changed files with 5163 additions and 11 deletions
  1. 19 0
      .gitignore
  2. 2 0
      README.md
  3. 24 0
      WORKFLOW/.github/PULL_REQUEST_TEMPLATE.md
  4. 3 0
      WORKFLOW/.gitignore
  5. 167 0
      WORKFLOW/FLOW/01_preparation_phase/base_setup_data_analysis_tools.ipynb
  6. 66 0
      WORKFLOW/FLOW/01_preparation_phase/base_work_with_GakuNinRDM.ipynb
  7. 482 0
      WORKFLOW/FLOW/02_experimental_phase/base_launch_an_experiment.ipynb
  8. 232 0
      WORKFLOW/FLOW/02_experimental_phase/base_monitor_data_size.ipynb
  9. 160 0
      WORKFLOW/FLOW/02_experimental_phase/base_monitor_dataset_structure.ipynb
  10. 267 0
      WORKFLOW/FLOW/02_experimental_phase/base_monitor_reproducibility.ipynb
  11. 54 0
      WORKFLOW/FLOW/03_after_experiments_phase/base_publish.ipynb
  12. 17 0
      WORKFLOW/FLOW/README.md
  13. 163 0
      WORKFLOW/FLOW/base_FLOW.ipynb
  14. 4 0
      WORKFLOW/FLOW/param_files/monitoring_params.json
  15. 24 0
      WORKFLOW/FLOW/param_files/params.json
  16. 1 0
      WORKFLOW/FLOW/util/README.md
  17. 91 0
      WORKFLOW/FLOW/util/base_datalad_save_push.ipynb
  18. 63 0
      WORKFLOW/FLOW/util/base_finish_research.ipynb
  19. 570 0
      WORKFLOW/FLOW/util/base_required_every_time.ipynb
  20. 0 0
      WORKFLOW/FLOW/util/scripts/__init__.py
  21. BIN
      WORKFLOW/FLOW/util/scripts/__pycache__/__init__.cpython-39.pyc
  22. BIN
      WORKFLOW/FLOW/util/scripts/__pycache__/utils.cpython-39.pyc
  23. 262 0
      WORKFLOW/FLOW/util/scripts/nb_utils.py
  24. 36 0
      WORKFLOW/FLOW/util/scripts/utils.py
  25. 19 0
      WORKFLOW/PACKAGE/base/.gitignore
  26. 62 0
      WORKFLOW/PACKAGE/base/Dockerfile
  27. 187 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/enter_metadata.ipynb
  28. 176 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/finish.ipynb
  29. 71 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/notebooks.diag
  30. BIN
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/usecase_annex_s3_1.png
  31. BIN
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/usecase_annex_s3_2.png
  32. BIN
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/コンテナ削除キャプチャ.png
  33. 24 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/param_files/params.json
  34. 198 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/prepare_input_data.ipynb
  35. 143 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/save.ipynb
  36. 91 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/base_datalad_save_push.ipynb
  37. 340 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/operate_s3_annex.ipynb
  38. 405 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/required_every_time.ipynb
  39. 256 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/scripts/nb_utils.py
  40. 91 0
      WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/scripts/utils.py
  41. 190 0
      WORKFLOW/PACKAGE/base/experiment.ipynb
  42. 0 0
      WORKFLOW/PACKAGE/base/meta_data.json
  43. 0 0
      WORKFLOW/PACKAGE/scheme/RCOS_only_data/input_data/.gitkeep
  44. 1 0
      WORKFLOW/PACKAGE/scheme/RCOS_only_data/output_data/.gitkeep
  45. 0 0
      WORKFLOW/PACKAGE/scheme/RCOS_with_code/input_data/.gitkeep
  46. 1 0
      WORKFLOW/PACKAGE/scheme/RCOS_with_code/output_data/.gitkeep
  47. 32 0
      WORKFLOW/PACKAGE/scheme/RCOS_with_code/source/main.ipynb
  48. 0 0
      WORKFLOW/PACKAGE/scheme/YODA/CHANGELOG.md
  49. 0 0
      WORKFLOW/PACKAGE/scheme/YODA/HOWTO.md
  50. 0 0
      WORKFLOW/PACKAGE/scheme/YODA/README.md
  51. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/ci/.gitkeep
  52. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/code/.gitkeep
  53. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/code/tests/.gitkeep
  54. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/docs/build/.gitkeep
  55. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/docs/source/.gitkeep
  56. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/envs/.gitkeep
  57. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/inputs/data/.gitkeep
  58. 1 0
      WORKFLOW/PACKAGE/scheme/YODA/outputs/.gitkeep
  59. 3 0
      WORKFLOW/README.md
  60. BIN
      WORKFLOW/images/maDMP実行ボタン.png
  61. 80 0
      WORKFLOW/images/notebooks.diag
  62. BIN
      WORKFLOW/images/result_on_README.png
  63. BIN
      WORKFLOW/images/sample_workflow.png
  64. BIN
      WORKFLOW/images/unfreeze_button.png
  65. 1 0
      experiments/pipeline.json
  66. 77 11
      maDMP.ipynb

+ 19 - 0
.gitignore

@@ -0,0 +1,19 @@
+
+/.cache/
+/.conda/
+/.config/
+/.ipython/
+/.local/
+/.tmp/
+/.bashrc
+/.bash_logout
+/.profile
+/.netrc
+.ipynb_checkpoints/
+.fonts/
+.jupyter/
+.npm/
+.ssh/
+.jupyter-server-log.txt
+.gitconfig
+

+ 2 - 0
README.md

@@ -35,3 +35,5 @@
 ※maDMP作成前は、ボタンをクリックしても遷移できませんのでご注意ください。
 
 maDMP実行環境へ遷移:[![Binder](https://binder.cs.rcos.nii.ac.jp/badge_logo.svg)](https://binder.cs.rcos.nii.ac.jp/v2/git/http%3A%2F%2Fdg02.dg.rcos.nii.ac.jp%2Fivis-mizuguchi%2F0606-test.git/master?filepath=maDMP.ipynb)
+## ワークフロー実行が2回目以降の場合
+ワークフロー実行環境へ遷移する場合は以下のリンクをクリックしてください<br>https://jupyter.cs.rcos.nii.ac.jp/user/ivis_rino_mizuguchi@rdm.nii.ac.jp/ivis-mizuguchi-0606-test-rx9y76gn/notebooks/WORKFLOW/FLOW/base_FLOW.ipynb<br>※上記のリンクからワークフロー機能トップページに遷移できない場合は、「初期設定をする」のmaDMP遷移ボタンからワークフロー機能トップページに遷移してください。その場合maDMPの実行は不要です。

+ 24 - 0
WORKFLOW/.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,24 @@
+## やったこと
+
+* このプルリクで何をしたか(例:課題「○○」に関連した修正)
+
+## やらないこと
+
+* このプルリクでやらないことは何か(あれば。無いなら「無し」でOK)(やらない場合は、いつやるのかを明記する。)
+
+## できるようになること(ユーザ目線)
+
+* 何ができるようになるのか(あれば。無いなら「無し」でOK)
+
+## できなくなること(ユーザ目線)
+
+* 何ができなくなるのか(あれば。無いなら「無し」でOK)
+
+## 動作確認の内容と結果
+
+* どのような動作確認を行ったか
+* その結果はどうだったか
+
+## その他
+
+* レビュワーへの参考情報(実装上の懸念点や注意点などあれば記載)

+ 3 - 0
WORKFLOW/.gitignore

@@ -0,0 +1,3 @@
+.datalad/
+.gitattributes
+.ipynb_checkpoints/

+ 167 - 0
WORKFLOW/FLOW/01_preparation_phase/base_setup_data_analysis_tools.ipynb

@@ -0,0 +1,167 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "# 高性能実験環境を準備する\n",
+    "\n",
+    "高性能実験環境として、[mdx](https://www.u-tokyo.ac.jp/focus/ja/press/z0310_00027.html)環境を利用するための設定をします。  \n",
+    "以下のセルを上から順番に実行してください。  \n",
+    "2回目以降の実行の場合、このセルが選択された状態で画面上部に表示される以下のボタンをクリックしてから実行して下さい。  \n",
+    "![UnfreezeBotton](https://raw.githubusercontent.com/NII-DG/workflow-template/develop/sections/images/unfreeze_button.png)\n",
+    "\n",
+    "◆◆◆開発メモ◆◆◆  \n",
+    "将来的には、[学認クラウドオンデマンド構築サービス](https://cloud.gakunin.jp/ocs/)による動的な環境構築を実現する。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## 1. 高性能実験環境への接続情報の入力\n",
+    "\n",
+    "以下のセルを実行し、表示されるフォームに高性能実験環境への接続情報を入力してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import clear_output\n",
+    "import getpass\n",
+    "ip_mdx = input(\"利用する高性能実験環境のIPアドレス:\")\n",
+    "name_mdx = input(\"高性能実験環境におけるSSHユーザ名:\")\n",
+    "clear_output()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## 2. アカウント認証のための設定\n",
+    "\n",
+    "[こちら](../../../../tree) を押下し、ファイル一覧画面に遷移してください。  \n",
+    "遷移後、id_rsaファイルをドラッグアンドドロップによりアップロードしてください。  \n",
+    "アップロード後、以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!mkdir -p /home/jovyan/.ssh/\n",
+    "!mv ~/id_rsa* ~/.ssh/id_rsa\n",
+    "!chmod 600 ~/.ssh/id_rsa"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## 3. 高性能実験環境への接続設定の実施\n",
+    "\n",
+    "以下のセルを実行し、高性能実験環境に接続するための設定を実施してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "path_flow_root = '/home/jovyan/WORKFLOW/FLOW/'\n",
+    "os.chdir(path_flow_root)\n",
+    "from util.scripts import utils\n",
+    "import json\n",
+    "\n",
+    "# 以下の認証の手順で用いる、\n",
+    "# GINのドメイン名等をパラメタファイルから取得する\n",
+    "params = {}\n",
+    "with open(utils.fetch_param_file_path(), mode='r') as f:\n",
+    "    params = json.load(f)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "%cd ~/WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/scripts\n",
+    "import utils\n",
+    "from IPython.display import clear_output\n",
+    "\n",
+    "import json\n",
+    "import os\n",
+    "\n",
+    "# mdx接続情報を設定ファイルに記述する\n",
+    "utils.config_mdx(name_mdx=name_mdx, mdxDomain=ip_mdx)\n",
+    "clear_output()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 4. 接続テスト\n",
+    "\n",
+    "以下のセルを実行し、「高性能実験環境と正常に接続されています」と表示されることを確認してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!ssh mdx \"echo 高性能実験環境と正常に接続されています\""
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 5. ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../base_FLOW.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 66 - 0
WORKFLOW/FLOW/01_preparation_phase/base_work_with_GakuNinRDM.ipynb

@@ -0,0 +1,66 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# GakuNin RDMと連携する\n",
+    "\n",
+    "GakuNin RDMと連携するためのノートブックです。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## ◆◆◆開発メモ◆◆◆\n",
+    "GakuNin RDMとの連携方法が明確でないためマークダウンのみ。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1.GakuNin RDMに遷移する"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2.GakuNin RDMに新規プロジェクトを作成する\n",
+    "\n",
+    "GakuNinRDMにサインインして、新規プロジェクトを作成してください。\n",
+    "※既存プロジェクトと連携する場合はこのステップを飛ばしてください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3.データガバナンス機能連携アドオンを設定する"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 482 - 0
WORKFLOW/FLOW/02_experimental_phase/base_launch_an_experiment.ipynb

@@ -0,0 +1,482 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "# 実験を開始する\n",
+    "\n",
+    "実験リポジトリの新規作成と実験記録に必要な実験パッケージの用意によって実験の作成と開始をサポートします。  \n",
+    "実験リポジトリは、研究プロジェクトのリポジトリとは別のリポジトリですが、研究リポジトリのexperimentsフォルダ配下に子関係として追加されます。  \n",
+    "上から順番に実行してください。  \n",
+    "2回目以降の実行の場合、このセルが選択された状態で画面上部に表示される以下のボタンをクリックしてから実行して下さい。  \n",
+    "![UnfreezeBotton](https://raw.githubusercontent.com/NII-DG/workflow-template/develop/sections/images/unfreeze_button.png)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. 開始する実験の実験リポジトリ名を入力する\n",
+    "\n",
+    "以下のセルを実行し、表示されるテキストボックスに作成したい実験リポジトリ名を半角英数字で入力してください。  \n",
+    "データガバナンス機能にすでに存在するリポジトリ名のリポジトリは作成いただけません。実験名など分かりやすい名前をご記入ください。  \n",
+    "入力した実験リポジトリ名の履歴をこのノートブックに保存したい場合は、実行後にセルの左側に表示されるピンマークをクリックしてください。  \n",
+    "※入力値に誤りがある場合、もう一度実行することで訂正ができます。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import re\n",
+    "from IPython.display import clear_output\n",
+    "\n",
+    "# GINサーバのものに合わせたバリデーションルールを設定\n",
+    "validation = re.compile(r'[a-z|A-Z|0-9|\\-|_|.]+')\n",
+    "\n",
+    "print('作成したい実験リポジトリ名を半角英数字で入力してください。')\n",
+    "while True:\n",
+    "    repository_title = input(\"リポジトリ名:\")\n",
+    "    if validation.fullmatch(repository_title):\n",
+    "        break\n",
+    "    else:\n",
+    "        clear_output()\n",
+    "        print('リポジトリ名は英数字、および\"-\", \"_\", \".\"のみで入力してください。')\n",
+    "\n",
+    "clear_output()\n",
+    "print(\"作成したリポジトリ名:\", repository_title)\n",
+    "print('この実験名で処理を進めます。変更したい場合は、このセルをもう一度実行することで訂正ができます。')\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. 実験リポジトリを作成する\n",
+    "\n",
+    "先ほど入力いただいた名前の実験用リポジトリをデータガバナンス機能に作成します。  \n",
+    "そのために、以下のセルを実行し、表示されるテキストボックスにデータガバナンス機能のユーザ名とパスワードを入力してください。  \n",
+    "この情報は認証のために利用します。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import json\n",
+    "import os\n",
+    "\n",
+    "path_flow_root = '/home/jovyan/WORKFLOW/FLOW/'\n",
+    "os.chdir(path_flow_root)\n",
+    "from util.scripts import utils\n",
+    "\n",
+    "# 以下の認証の手順で用いる、\n",
+    "# GINのドメイン名等をパラメタファイルから取得する\n",
+    "params = {}\n",
+    "with open(utils.fetch_param_file_path(), mode='r') as f:\n",
+    "    params = json.load(f)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# リポジトリ作成のためのユーザ認証\n",
+    "import os\n",
+    "import time\n",
+    "import getpass\n",
+    "import requests\n",
+    "\n",
+    "from IPython.display import clear_output\n",
+    "from requests.auth import HTTPBasicAuth\n",
+    "from http import HTTPStatus\n",
+    "\n",
+    "# 正常に認証が終わるまで繰り返し\n",
+    "while True:\n",
+    "    name = input(\"ユーザー名:\")\n",
+    "    password = getpass.getpass(\"パスワード:\")\n",
+    "    clear_output()\n",
+    "    \n",
+    "    # GIN API Basic Authentication\n",
+    "    # refs: https://docs.python-requests.org/en/master/user/authentication/\n",
+    "    \n",
+    "    # 既存のトークンがあるか確認する\n",
+    "    response = requests.get(params['siblings']['ginHttp']+'api/v1/users/' + name + '/tokens', auth=(name, password))\n",
+    "    tokens = response.json()\n",
+    "\n",
+    "    # 既存のトークンがなければ作成する\n",
+    "    if len(tokens) < 1:\n",
+    "        response = requests.post(params['siblings']['ginHttp']+'api/v1/users/' + name + '/tokens', data={\"name\": \"system-generated\"} ,auth=(name, password))\n",
+    "\n",
+    "    if response.status_code == HTTPStatus.OK or HTTPStatus.CREATED:\n",
+    "        tokens = response.json()\n",
+    "        clear_output()\n",
+    "        print(\"認証が正常に完了しました。次の手順へお進みください。\")\n",
+    "        break\n",
+    "    else:\n",
+    "        clear_output()\n",
+    "        print(\"ユーザ名、またはパスワードが間違っています。\\n恐れ入りますがもう一度ご入力ください。\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# GIN APIでリポジトリを作成する\n",
+    "import requests\n",
+    "\n",
+    "create_repo = requests.post(\n",
+    "                params['siblings']['ginHttp']+'api/v1/user/repos?token=' + tokens[0]['sha1'],\n",
+    "                data={\n",
+    "                    \"name\": repository_title,\n",
+    "                    \"auto_init\": False\n",
+    "                }\n",
+    "                )\n",
+    "print(create_repo)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3. 実験記録管理のための準備を行う"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 3.1 実験リポジトリに実験パッケージを用意する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "tmp_path = '/home/jovyan/.tmp/' + repository_title + '/'"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%bash\n",
+    "#!/bin/bash\n",
+    "if [ ! -d ~/.tmp/ ]; then\n",
+    "    # .tmpフォルダが無ければ作成する\n",
+    "    mkdir ~/.tmp\n",
+    "fi\n",
+    "\n",
+    "if [ ! -d $tmp_path ]; then\n",
+    "    # 実験タイトル名のフォルダが無ければ作成する\n",
+    "    mkdir $tmp_path\n",
+    "fi"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "\n",
+    "os.chdir(path_flow_root)\n",
+    "from util.scripts import utils\n",
+    "\n",
+    "# DS構成のスキーム名をパラメタファイルから取得する\n",
+    "monitoring_params = {}\n",
+    "with open(utils.fetch_monitoring_param_file_path(), mode='r') as f:\n",
+    "    monitoring_params = json.load(f)\n",
+    "    \n",
+    "scheme_name = monitoring_params['datasetStructure']\n",
+    "\n",
+    "# urlを生成する\n",
+    "remote_url = params['siblings']['ginSsh']+name+'/'+repository_title+'.git'\n",
+    "remote_http_url = params['siblings']['ginHttp']+name+'/'+repository_title+'.git'\n",
+    "\n",
+    "# 実験パッケージを用意する\n",
+    "!cp -r ~/WORKFLOW/PACKAGE/base/. $tmp_path\n",
+    "!cp -r ~/WORKFLOW/PACKAGE/scheme/$scheme_name/. $tmp_path\n",
+    "\n",
+    "# dataset化してsiblingを設定する\n",
+    "%cd $tmp_path\n",
+    "!datalad create --force .\n",
+    "!datalad siblings add -s \"gin\" --url $remote_url"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# データガバナンス機能に同期する\n",
+    "import papermill as pm\n",
+    "pm.execute_notebook(\n",
+    "    '/home/jovyan/WORKFLOW/FLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = '実験パッケージの用意', PATH=tmp_path, TO_GIT = True)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 実験リポジトリをexperiments配下にクローンする\n",
+    "%cd ~/\n",
+    "!datalad clone -d . $remote_http_url ./experiments/$repository_title\n",
+    "!rm -rf $tmp_path"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 3.2 pipeline.jsonに実験名を追記する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import json\n",
+    "\n",
+    "with open('/home/jovyan/experiments/pipeline.json', 'r') as f:\n",
+    "    pipeline = json.load(f)\n",
+    "\n",
+    "pipeline.append(repository_title)\n",
+    "\n",
+    "with open('/home/jovyan/experiments/pipeline.json', 'w') as f:\n",
+    "    json.dump(pipeline, f, indent = 4)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 4. 実行結果をデータガバナンス機能に同期する\n",
+    "\n",
+    "ここまでの内容を保存し、データガバナンス機能に同期します。  \n",
+    "以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "\n",
+    "pm.execute_notebook(\n",
+    "    'WORKFLOW/FLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = '実験用リポジトリの作成', IS_RECURSIVE = False, TO_GIT = True)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## 5. 実験環境にアクセスする\n",
+    "\n",
+    "以下のうち、いずれかの手順を実施してください。\n",
+    "\n",
+    "- 標準的な実験活動の場合:「5-A. 標準環境での実験」\n",
+    "- 高性能実験環境を利用する場合:「5-B. 高性能実験環境での実験」"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 5-A. 標準環境での実験\n",
+    "\n",
+    "以下のセルを実行した後に出力されるリンクをクリックして実験に移ってください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import urllib\n",
+    "\n",
+    "print(\n",
+    "    \"https://binder.cs.rcos.nii.ac.jp/v2/git/\" + urllib.parse.quote(remote_http_url, safe='') + \"/HEAD?filepath=experiment.ipynb\"\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 5-B. 高性能実験環境での実験"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "以下のセルを実行し、「高性能実験環境と正常に接続されています」と表示されることを確認してください。  \n",
+    "表示されない場合、[高性能実験環境を準備する](../01_preparation_phase/base_setup_data_analysis_tools.ipynb) の手順を実施してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!ssh mdx \"echo 高性能実験環境と正常に接続されています\""
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "高性能実験環境の接続先情報を取得します。以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mdx_host = ''\n",
+    "with open('/home/jovyan/.ssh/config', 'r') as f:\n",
+    "    tmp = f.read()\n",
+    "    tmp = tmp[tmp.find('mdx'):]\n",
+    "    tmp = tmp[tmp.find('Hostname ') + len('Hostname '):]\n",
+    "    mdx_host = tmp[:tmp.find('\\n')]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "高性能実験環境に実験パッケージ情報を転送します。以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cmd = \"git clone \" + remote_http_url\n",
+    "!ssh mdx $cmd"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "高性能実験環境においてJupyterLabを起動します。以下のセルを実行してください。  \n",
+    "実行後、表示されるリンクをクリックすると、パスワードの入力が求められますので「gpu-jupyter」と入力してLoginしてください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import clear_output\n",
+    "\n",
+    "# JupyterLabコンテナイメージの作成\n",
+    "cmd = \"docker build -t dg/jupyterlab:1.1 ./\" + repository_title + \"/\"\n",
+    "!ssh mdx $cmd\n",
+    "clear_output()\n",
+    "\n",
+    "# JupyterLabコンテナの起動\n",
+    "if mdx_host == '163.220.176.51':\n",
+    "    cmd = \"docker run --gpus all -d -P dg/jupyterlab:1.1\"\n",
+    "else:\n",
+    "    cmd = \"docker run -d -P dg/jupyterlab:1.1\"\n",
+    "container_id = !ssh mdx $cmd\n",
+    "container_id = container_id[0][:12]\n",
+    "\n",
+    "# ホストマシン上で動的に割り当てられたポートを取得\n",
+    "cmd = \"docker ps -l\"\n",
+    "port = !ssh mdx $cmd\n",
+    "port = port[1]\n",
+    "port = port[port.find(\"0.0.0.0:\") + 8: port.find(\"->8888/tcp\")]\n",
+    "\n",
+    "# JupyterLab利用のためのURLを表示\n",
+    "url = \"http://\" + mdx_host + \":\" + str(port) + \"/notebooks/experiment.ipynb\"\n",
+    "print(url)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 6. ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../base_FLOW.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 232 - 0
WORKFLOW/FLOW/02_experimental_phase/base_monitor_data_size.ipynb

@@ -0,0 +1,232 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "# モニタリング(研究データ容量)\n",
+    "\n",
+    "研究データ容量について、DMPに記載された上限を超過していないかモニタリングします。  \n",
+    "以下のセルを上から実行してください。2回目以降の実行では、画面上部に表示される以下のボタンをクリックしてから実行して下さい。  \n",
+    "![UnfreezeBotton](https://raw.githubusercontent.com/NII-DG/workflow-template/develop/sections/images/unfreeze_button.png)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. 基準値となるデータ容量上限値を取得する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import json\n",
+    "\n",
+    "os.chdir('..')\n",
+    "from util.scripts import utils \n",
+    "\n",
+    "path_params = utils.fetch_monitoring_param_file_path()\n",
+    "limit = \"\"\n",
+    "with open(path_params, \"r\") as f:\n",
+    "    dmp = json.load(f)\n",
+    "    limit = dmp[\"dataSize\"]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print(\"input : \" + str(limit))\n",
+    "\n",
+    "if isinstance(limit, str):  # \"100\", \"100B\", \"100b\", \"100KB\", \"100kb\"\n",
+    "    limit = limit.lower()\n",
+    "\n",
+    "    if limit[-1] == \"b\":\n",
+    "        limit = limit[:-1]\n",
+    "\n",
+    "    units = [\"k\", \"m\", \"g\", \"t\", \"p\", \"e\", \"z\"]\n",
+    "    if limit[-1] in units:\n",
+    "        limit = 1024 ** (units.index(limit[-1]) + 1) * int(limit[:-1])\n",
+    "\n",
+    "print(\"output: \" + str(limit))\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. 実験パッケージ情報を取得する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import json\n",
+    "import glob\n",
+    "\n",
+    "# 作業ディレクトリに移動\n",
+    "%cd ~/experiments/\n",
+    "\n",
+    "# 実験パッケージ群の読み込み\n",
+    "dirs = glob.glob(\"./*/\")\n",
+    "\n",
+    "experiments = []\n",
+    "for dir_name in dirs:\n",
+    "    experiments += [os.path.basename(dir_name[:-1])]\n",
+    "print(\"実験パッケージ :\" + str(experiments))\n",
+    "\n",
+    "# 元のディレクトリに移動\n",
+    "%cd -"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3. 各実験パッケージについて実データを取得する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "for experiment in experiments:\n",
+    "    %cd ~/experiments/$experiment\n",
+    "    !git pull\n",
+    "    !datalad get -r .\n",
+    "    %cd ../"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 4. データ容量をチェックする"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "isSucceeded = True\n",
+    "\n",
+    "result = !du -s ~/experiments/\n",
+    "result = result[0].split(\"\\t\")[0]\n",
+    "result = int(result)\n",
+    "print(\"datasize: \" + str(result))\n",
+    "\n",
+    "if result > limit:\n",
+    "    isSucceeded = False\n",
+    "    raise Exception(\"データ容量を超過しています\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## 4. モニタリング結果をREADMEに反映する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from util.scripts import utils\n",
+    "\n",
+    "utils.reflect_monitoring_results('dataAmount', isSucceeded)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 5. 実行結果をデータガバナンス機能に同期する\n",
+    "\n",
+    "ここまでの内容を保存し、データガバナンス機能に同期します。  \n",
+    "以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "\n",
+    "pm.execute_notebook(\n",
+    "    'WORKFLOW/FLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = 'モニタリング(研究データ容量)', IS_RECURSIVE = False, TO_GIT = True)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## 6. FLOWに遷移する\n",
+    "\n",
+    "続けてワークフローを実行する場合は、[こちら](../base_FLOW.ipynb)からFLOWに遷移できます。"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.7"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 160 - 0
WORKFLOW/FLOW/02_experimental_phase/base_monitor_dataset_structure.ipynb

@@ -0,0 +1,160 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# データセット構成状態モニタリング\n",
+    "\n",
+    "ここでは、作成いただいたデータセット(実験リポジトリ)の構成状態をモニタリングします。以下のセルを実行することで、それぞれのデータセットがその管理品質上、適合的であるかどうかを確認できます。モニタリングの結果はREADME.mdへ自動的に反映されます。\n",
+    "\n",
+    "![result_on_README](https://raw.githubusercontent.com/NII-DG/workflow-template/main/images/result_on_README.png)\n",
+    "\n",
+    "## ◇◆◇開発メモ◇◆◇\n",
+    "\n",
+    "### 処理の流れ\n",
+    "\n",
+    "- DMPの情報として、`datasetStructure`が定義されている想定。何種類かのスキームから選んでもらうようなイメージでいる。\n",
+    "    - 現在、設定しているのは「RCOS_with_code」「RCOS_only_data」「YODA」の3種類。\n",
+    "- 実験名のリストとスキーム名のリストとをそれぞれ作成し、モニタリング処理を回す。\n",
+    "    - 現状、スキームごとに望ましいフォルダ構成を定義しており、それぞれのパスが実際に存在するかを確認し、その結果を出力するような実装になっている。\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import json\n",
+    "from typing import List\n",
+    "\n",
+    "os.chdir('..')\n",
+    "from util.scripts import utils\n",
+    "\n",
+    "def get_ideal_paths(scheme_name: str, path_to_dataset: str) -> List[str]:\n",
+    "    # 存在することが望ましいディレクトリ・ファイルのリスト\n",
+    "    ideal_paths_RCOS_with_code = [\n",
+    "        path_to_dataset + '/source',\n",
+    "        path_to_dataset + '/input_data',\n",
+    "        path_to_dataset + '/output_data',\n",
+    "        path_to_dataset + '/Dockerfile',\n",
+    "        path_to_dataset + '/.gitattributes',\n",
+    "        path_to_dataset + '/.gitignore',\n",
+    "        path_to_dataset + '/meta_data.json',\n",
+    "        path_to_dataset + '/experiment.ipynb'\n",
+    "    ]\n",
+    "\n",
+    "    ideal_paths_RCOS_only_data = [\n",
+    "        path_to_dataset + '/input_data',\n",
+    "        path_to_dataset + '/output_data',\n",
+    "        path_to_dataset + '/Dockerfile',\n",
+    "        path_to_dataset + '/.gitattributes',\n",
+    "        path_to_dataset + '/.gitignore',\n",
+    "        path_to_dataset + '/meta_data.json',\n",
+    "        path_to_dataset + '/experiment.ipynb'\n",
+    "    ]\n",
+    "\n",
+    "    ideal_paths_YODA = [\n",
+    "        path_to_dataset + '/ci',\n",
+    "        path_to_dataset + '/code',\n",
+    "        path_to_dataset + '/code/tests',\n",
+    "        path_to_dataset + '/docs',\n",
+    "        path_to_dataset + '/docs/build',\n",
+    "        path_to_dataset + '/docs/source',\n",
+    "        path_to_dataset + '/envs',\n",
+    "        path_to_dataset + '/inputs',\n",
+    "        path_to_dataset + '/inputs/data',\n",
+    "        path_to_dataset + '/outputs',\n",
+    "        path_to_dataset + '/CHANGELOG.md',\n",
+    "        path_to_dataset + '/HOWTO.md',\n",
+    "        path_to_dataset + '/README.md',\n",
+    "    ]\n",
+    "\n",
+    "    return eval('ideal_paths_' + scheme_name)\n",
+    "\n",
+    "###########################################################################################################\n",
+    "\n",
+    "\n",
+    "def monitor_dataset_structure(scheme_name: str, paths_to_dataset: List[str]) -> bool:\n",
+    "    ideal_paths: List[str] = []\n",
+    "    dataset_name: str = ''\n",
+    "    isOK: bool = True\n",
+    "\n",
+    "    for dataset in paths_to_dataset:\n",
+    "        ideal_paths = get_ideal_paths(scheme_name, dataset)\n",
+    "        dataset_name = os.path.basename(dataset)\n",
+    "\n",
+    "        print('================================ Monitoring Dataset Structure ================================')\n",
+    "        print('Dataset name: ' + dataset_name)\n",
+    "        print(' STATUS    PATH')\n",
+    "\n",
+    "        for path in ideal_paths:\n",
+    "            if os.path.exists(path):\n",
+    "                print('  OK    ' + path)\n",
+    "            else:\n",
+    "                print('  NG    ' + path)\n",
+    "                print('        [WARN] Your dataset structure is deprecated.')\n",
+    "                if isOK:\n",
+    "                    isOK = False\n",
+    "\n",
+    "        print('\\nMonitered by \"' + scheme_name + '\" scheme.\\n')\n",
+    "    return isOK\n",
+    "\n",
+    "###########################################################################################################\n",
+    "\n",
+    "# スクリプトファイルの拡張子リストを定義(あるいは読み出し)\n",
+    "# something cool...\n",
+    "\n",
+    "\n",
+    "def main() -> None:\n",
+    "    perspective = 'datasetStructure'\n",
+    "    path_monitoring_params = utils.fetch_monitoring_param_file_path()\n",
+    "\n",
+    "    # データセットのパスのリストを作成\n",
+    "    base_path = '/home/jovyan/experiments/'\n",
+    "    files = os.listdir(base_path)\n",
+    "    paths_to_dataset = [base_path + f for f in files if os.path.isdir(os.path.join(base_path, f))]\n",
+    "\n",
+    "    # データセット構成のスキームを取得\n",
+    "    scheme_name = ''\n",
+    "    with open(path_monitoring_params, \"r\") as f:\n",
+    "        dmp = json.load(f)\n",
+    "        scheme_name = dmp[\"datasetStructure\"]\n",
+    "\n",
+    "    isOK: bool = monitor_dataset_structure(scheme_name, paths_to_dataset)\n",
+    "    utils.reflect_monitoring_results(perspective, isOK)\n",
+    "\n",
+    "\n",
+    "if __name__ == '__main__':\n",
+    "    main()\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "\n",
+    "pm.execute_notebook(\n",
+    "    'WORKFLOW/FLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters=dict(SAVE_MESSAGE='モニタリング(データセット構成)', IS_RECURSIVE=False, TO_GIT=True)\n",
+    ")\n"
+   ]
+  }
+ ],
+ "metadata": {
+  "language_info": {
+   "name": "python"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 267 - 0
WORKFLOW/FLOW/02_experimental_phase/base_monitor_reproducibility.ipynb

@@ -0,0 +1,267 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# モニタリング(再現性)\n",
+    "\n",
+    "研究の再現性についてモニタリングします。モニタリングでは、[pipeline.json](../../../experiments/pipeline.json)に設定された順番に実験が再現されます。  \n",
+    "以下のセルを上から実行してください。2回目以降の実行では、画面上部に表示される以下のボタンをクリックしてから実行して下さい。  \n",
+    "![UnfreezeBotton](https://raw.githubusercontent.com/NII-DG/workflow-template/develop/sections/images/unfreeze_button.png)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. パイプライン定義ファイルをチェックする\n",
+    "\n",
+    "パイプライン定義ファイルとパッケージ群の情報に相違がないか確認します。  \n",
+    "以下のセルを実行し、エラーが起きないことを確認してください。  \n",
+    "エラーが起きた場合は、表示されたリンクに遷移してパイプライン定義ファイルを修正&エラーが起きなくなるまで再実行を繰り返してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import json\n",
+    "import glob\n",
+    "\n",
+    "# 作業ディレクトリに移動\n",
+    "%cd ~/experiments/\n",
+    "\n",
+    "# 実験パイプライン定義ファイルの読み込み\n",
+    "pipeline = json.load(open(\"pipeline.json\"))\n",
+    "print(\"実験パイプライン:\" + str(pipeline))\n",
+    "\n",
+    "# 実験パッケージ群の読み込み\n",
+    "files = glob.glob(\"./*/\")\n",
+    "\n",
+    "experiments = []\n",
+    "for file in files:\n",
+    "    experiments += [os.path.basename(file[:-1])]\n",
+    "print(\"実験パッケージ :\" + str(experiments))\n",
+    "\n",
+    "# 元のディレクトリに移動\n",
+    "%cd -\n",
+    "\n",
+    "# 過不足の確認\n",
+    "if set(pipeline) != set(experiments):\n",
+    "    link = \"https://jupyter.cs.rcos.nii.ac.jp\" + os.environ[\"JUPYTERHUB_SERVICE_PREFIX\"] + \"edit/experiments/pipeline.json\"\n",
+    "    raise Exception(\"実験パイプラインと実験パッケージ間で相違があります\\n\\n以下のURLに遷移して修正してください\\n\" + link)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. 実験パイプラインを実行する"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 2.1 実験パイプラインの定義情報を取得する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import json\n",
+    "\n",
+    "# 作業ディレクトリに移動\n",
+    "%cd ~/experiments/\n",
+    "\n",
+    "# 実験パイプライン定義ファイルの読み込み\n",
+    "pipeline = json.load(open(\"pipeline.json\"))\n",
+    "print(\"実験パイプライン:\" + str(pipeline))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 2.2 実験パッケージを最新化する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for experiment in pipeline:\n",
+    "    %cd $experiment\n",
+    "    !git pull\n",
+    "    !datalad get -r .\n",
+    "    %cd ../"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 2.3 実験パイプラインを実行する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "%cd ~/experiments/\n",
+    "\n",
+    "import papermill\n",
+    "\n",
+    "isSucceeded = True\n",
+    "for experiment in pipeline:\n",
+    "    print(\"\\n============================================================ \" + experiment)\n",
+    "\n",
+    "    print(\"\\n# 複数回実行に備えて正解データ保管用ディレクトリを削除\")\n",
+    "    cmd = \"rm -rf \" + experiment + \"/output_data_correct/\" \n",
+    "    print(\"$ \" + cmd)\n",
+    "    !$cmd\n",
+    "\n",
+    "    print(\"\\n# 既存の出力ディレクトリのデータを正解データとして移動\")\n",
+    "    cmd = \"mv \" + experiment + \"/output_data/ \" + experiment + \"/output_data_correct/\" \n",
+    "    print(\"$ \" + cmd)\n",
+    "    !$cmd\n",
+    "\n",
+    "    print(\"\\n# 出力ディレクトリの初期化\")\n",
+    "    cmd = \"mkdir \" + experiment + \"/output_data/\"\n",
+    "    print(\"$ \" + cmd)\n",
+    "    !$cmd\n",
+    "    cmd = \"cp \" + experiment + \"/output_data_correct/.gitkeep \" + experiment + \"/output_data/\"\n",
+    "    print(\"$ \" + cmd)\n",
+    "    !$cmd\n",
+    "    cmd = \"cp \" + experiment + \"/output_data_correct/.ipynb_checkpoints \" + experiment + \"/output_data/\"\n",
+    "    print(\"$ \" + cmd)\n",
+    "    !$cmd\n",
+    "\n",
+    "    print(\"\\n実験再現のための環境構築\")\n",
+    "    cmd = \"pip install -r \" + experiment + \"/requirements.txt\"\n",
+    "    print(\"$ \" + cmd)\n",
+    "    !$cmd\n",
+    "\n",
+    "    print(\"\\n実験ソースコードの実行\")\n",
+    "    cmd = experiment + \"/source/\"\n",
+    "    print(\"$ cd \" + cmd)\n",
+    "    %cd $cmd\n",
+    "\n",
+    "    papermill.execute_notebook(\"main.ipynb\", \"log.ipynb\")\n",
+    "    \n",
+    "    cmd = \"-\"\n",
+    "    print(\"$ cd \" + cmd)\n",
+    "    %cd $cmd\n",
+    "\n",
+    "    print(\"\\n差分の摘出\")\n",
+    "    cmd = \"diff -r \" + experiment + \"/output_data_correct/ \" + experiment + \"/output_data/\"\n",
+    "    print(\"$ \" + cmd)\n",
+    "    result = !$cmd\n",
+    "    print(result)\n",
+    "    \n",
+    "    if len(result) > 0:\n",
+    "        isSucceeded = False\n",
+    "        raise Exception(\"出力データが一致しません\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3. モニタリング結果をREADMEに反映する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/WORKFLOW/FLOW/\n",
+    "from util.scripts import utils \n",
+    "\n",
+    "utils.reflect_monitoring_results('reproducibility', isSucceeded)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 4. 実行結果をデータガバナンス機能に同期する\n",
+    "\n",
+    "ここまでの内容を保存し、データガバナンス機能に同期します。  \n",
+    "以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "\n",
+    "pm.execute_notebook(\n",
+    "    'WORKFLOW/FLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = 'モニタリング(再現性)', IS_RECURSIVE = False, TO_GIT = True)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 5. ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../base_FLOW.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.7"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 54 - 0
WORKFLOW/FLOW/03_after_experiments_phase/base_publish.ipynb

@@ -0,0 +1,54 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# 研究成果を公開する"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## ◆◆◆開発メモ◆◆◆\n",
+    "後期フェーズについては不明確な部分が多いためプロトタイプではTBDとする"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## キュレーション機能と連携する"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 公開基盤と連携する"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 17 - 0
WORKFLOW/FLOW/README.md

@@ -0,0 +1,17 @@
+# FLOWS
+
+## FLOWSでは分野ごとのフロー群を整理する
+
+## ◆◆◆開発メモ◆◆◆
+
+現時点では、プロトタイプを動かすために以下の作業が必要なのでprepare_for_datalad_push.ipynbで作業を定義している。
+
+- .gitconfigにユーザー名とメールアドレスを登録する
+- jupyterhubでid_rsa(秘密鍵)を.ssh下に配置し、ginリポジトリにペアの公開鍵を登録する
+- .gitignoreで.local以下のファイルをgit管理から外す
+- .ssh/configで以下を指定
+    host *
+            StrictHostKeyChecking no
+            UserKnownHostsFile=/dev/null
+
+

+ 163 - 0
WORKFLOW/FLOW/base_FLOW.ipynb

@@ -0,0 +1,163 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "b6e6fa62",
+   "metadata": {},
+   "source": [
+    "# ワークフロー機能トップページ\n",
+    "\n",
+    "研究プロセスで生じる研究者のタスクを半機械的に実行支援します。  \n",
+    "ワークフロー機能では、研究プロセスを研究準備、実験期、実験終了後というフェーズに大別し、それぞれのフェーズの特徴に沿ったタスクの実行支援を用意しています。  \n",
+    "また、個々のタスクの実行支援は、Jupyter Notebook形式で機械的に実行可能な手順書として記述されているため、柔軟に手順を修正いただけます。 \n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ab1b456b",
+   "metadata": {},
+   "source": [
+    "## ワークフロー機能の使い方\n",
+    "\n",
+    "「0.ワークフロー機能を実行するための準備をする」を最初に実行したあと、研究準備フェーズから実験期フェーズ、実験終了後フェーズの順に必要なワークフローを実行してください。  \n",
+    "0フェーズを実行した後は、お好きなタイミングでワークフロー機能を中断可能です。再度ワークフロー機能を利用する際は、研究リポジトリのREADME.mdにあるリンクをクリックしてください。  \n",
+    "また、全てのワークフローを実行する必要はありません。  \n",
+    "「フェーズの概要」を確認後、セルを実行してワークフロー図を作成してください。  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4b5bd2b2",
+   "metadata": {},
+   "source": [
+    "## フェーズの概要"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d27970ec",
+   "metadata": {},
+   "source": [
+    "### 0. ワークフロー機能を実行するための準備をする\n",
+    "\n",
+    "次のフェーズに進む前に必ず実行する必要があります。  \n",
+    "ワークフロー機能を使うのが初めての場合は、「初期セットアップを行う」、「起動毎に必要な準備を行う」の順に実行してください。  \n",
+    "2回目以降でワークフロー機能にREADMEのリンクからではなく、以下のmaDMP実行環境への遷移ボタンから遷移した場合は、ワークフロー機能を実行する前に「起動毎に必要な準備を行う」を実行してください。  \n",
+    "![maDMP実行ボタン](https://raw.githubusercontent.com/NII-DG/workflow-template/main/images/maDMP実行ボタン.png)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "65dbb0c0",
+   "metadata": {},
+   "source": [
+    "### 1. 研究準備フェーズ\n",
+    "\n",
+    "研究準備フェーズでは実験前の準備をサポートします。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7883887f",
+   "metadata": {},
+   "source": [
+    "### 2. 実験フェーズ\n",
+    "\n",
+    "実験フェーズでは実験の開始から、複数の実験の再現性の確認などをサポートします。<br>まずは、「実験を開始する」から実験を作成してください。複数の実験の終了後、再現性を確認したいタイミングで「モニタリングを実施する」を実行してください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4e056777",
+   "metadata": {},
+   "source": [
+    "### 3 実験終了後フェーズ\n",
+    "\n",
+    "実験終了後フェーズでは研究成果の公開をサポートします。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "077a5ac5",
+   "metadata": {},
+   "source": [
+    "## ワークフロー図を作成する\n",
+    "\n",
+    "以下のセルを実行してワークフロー図を作成できます。  \n",
+    "すでに実行したワークフローを確認したい場合は再度実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "440e5e62",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "from IPython.display import SVG\n",
+    "\n",
+    "nb_utils_path   = os.path.join(os.environ['HOME'], 'WORKFLOW/FLOW/util/scripts/nb_utils.py')\n",
+    "\n",
+    "basic_path      = os.path.join(os.environ['HOME'], 'WORKFLOW/images')\n",
+    "basic_path_diag = os.path.join(basic_path, 'notebooks.diag')\n",
+    "basic_path_svg  = os.path.join(basic_path, 'notebooks.svg')\n",
+    "\n",
+    "os.chdir(os.environ['HOME'])\n",
+    "!python3 $nb_utils_path $basic_path_diag\n",
+    "\n",
+    "SVG(filename=basic_path_svg)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d88cb836",
+   "metadata": {},
+   "source": [
+    "ワークフロー図の左のノードはワークフローのフェーズとその流れを表しており、フェーズの右側にあるノードは個々のワークフローを表しています。  \n",
+    "各ワークフローを実行するには、ワークフローの各ノードのリンクをクリックしてください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2eb12c2c",
+   "metadata": {},
+   "source": [
+    "## データガバナンス機能へ戻る\n",
+    "\n",
+    "以下のセルを実行し、表示されるURLをクリックするとデータガバナンス機能へアクセスできます。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8545af99",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!git config --get remote.origin.url"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}

+ 4 - 0
WORKFLOW/FLOW/param_files/monitoring_params.json

@@ -0,0 +1,4 @@
+{
+    "dataSize": "1GB",
+    "datasetStructure": "RCOS_with_code"
+}

+ 24 - 0
WORKFLOW/FLOW/param_files/params.json

@@ -0,0 +1,24 @@
+{
+    "siblings": {
+        "ginHttp": "http://dg02.dg.rcos.nii.ac.jp/",
+        "ginSsh": "ssh://root@dg02.dg.rcos.nii.ac.jp:3001/",
+        "gitHugibHttp": "https://github.com/",
+        "gitHubSsh": "git@github.com:"
+    },
+    "rcosBinderUrl": "https://jupyter.cs.rcos.nii.ac.jp",
+    "monitoring": {
+        "dataAmount": {
+            "name": "\u30c7\u30fc\u30bf\u5bb9\u91cf",
+            "path": "./WORKFLOW/FLOW/02_experimental_phase/base_monitor_data_size.ipynb"
+        },
+        "datasetStructure": {
+            "name": "\u30c7\u30fc\u30bf\u69cb\u9020",
+            "path": "./WORKFLOW/FLOW/02_experimental_phase/base_monitor_dataset_structure.ipynb"
+        },
+        "reproducibility": {
+            "name": "\u518d\u73fe\u6027",
+            "path": "./WORKFLOW/FLOW/02_experimental_phase/base_monitor_reproducibility.ipynb"
+        }
+    },
+    "ginKeyId": "450"
+}

+ 1 - 0
WORKFLOW/FLOW/util/README.md

@@ -0,0 +1 @@
+どのフェーズでも必要なセクションをモジュール化する

+ 91 - 0
WORKFLOW/FLOW/util/base_datalad_save_push.ipynb

@@ -0,0 +1,91 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# ワークフロー実行結果を書き戻す\n",
+    "\n",
+    "このノートブックでは、変更内容をGINリポジトリに書き戻します。  \n",
+    "※各セクションのセルでpapermillによって自動実行されるノートブックなので手作業での実施は非推奨です。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "tags": [
+     "parameters"
+    ]
+   },
+   "outputs": [],
+   "source": [
+    "SIBLING_NAME = 'gin'\n",
+    "SAVE_MESSAGE = '[GIN] modify on binder'\n",
+    "PATH = '/home/jovyan'\n",
+    "IS_RECURSIVE = True\n",
+    "TO_GIT = False\n",
+    "RESULT_RENDERER = 'default'"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1.書き戻しの準備を行う"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from datalad import api\n",
+    "\n",
+    "api.save(message=SAVE_MESSAGE, path=PATH, recursive=IS_RECURSIVE, to_git=TO_GIT)\n",
+    "api.unlock(PATH)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2.変更内容を書き戻す"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from datalad import api\n",
+    "\n",
+    "api.push(to=SIBLING_NAME, result_renderer=RESULT_RENDERER, path=PATH, recursive=IS_RECURSIVE)"
+   ]
+  }
+ ],
+ "metadata": {
+  "celltoolbar": "Tags",
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 63 - 0
WORKFLOW/FLOW/util/base_finish_research.ipynb

@@ -0,0 +1,63 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# 研究を終了する\n",
+    "\n",
+    "研究結果の公開などを終え、この研究を終了させる場合に実行してください。  \n",
+    "ワークフロー機能やモニタリング機能の実行環境である当環境を停止・削除します。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. この実行環境を停止・削除する\n",
+    "\n",
+    "不要になったこの実行環境を停止・削除します。  \n",
+    "コード付帯機能では20までしか実行環境を構築できないため、不要な実行環境は削除することを推奨します。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 1.1 この実行環境を確認する\n",
+    "\n",
+    "以下のセルを実行して実行環境のサーバー名を確認して下さい。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "\n",
+    "print(os.environ[\"JUPYTERHUB_SERVICE_PREFIX\"].split('/')[3])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 1.2 コード付帯機能のコントロールパネルへ遷移し、実行環境を停止・削除する\n",
+    "\n",
+    "[コントロールパネル](https://jupyter.cs.rcos.nii.ac.jp/hub/home)へ遷移して、`1.1`で確認したサーバーを`stop`、`delete`ボタンをクリックして停止・削除してください。  \n",
+    "※`delete`ボタンは、以下の図のように`stop`ボタンをクリックした後に表示されます。  \n",
+    "![コンテナ削除キャプチャ](https://raw.githubusercontent.com/NII-DG/workflow-template/main/PACKAGE/base/EX-WORKFLOW/images/コンテナ削除キャプチャ.png)"
+   ]
+  }
+ ],
+ "metadata": {
+  "language_info": {
+   "name": "python"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 570 - 0
WORKFLOW/FLOW/util/base_required_every_time.ipynb

@@ -0,0 +1,570 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "4b6b7c2a",
+   "metadata": {},
+   "source": [
+    "# 初期セットアップを行う\n",
+    "\n",
+    "ワークフロー実行のための準備を行います。\n",
+    "上から順番にセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "396fd7ef",
+   "metadata": {},
+   "source": [
+    "## 1.ユーザーの認証を行う\n",
+    "\n",
+    "この手順では、あなたのユーザ情報をシステムに認証させる手続きを行います。  \n",
+    "以下のセルを実行し、画面の表示に沿ってデータガバナンス機能に登録したユーザー名、パスワード、メールアドレスを入力してください。\n",
+    "\n",
+    "◆◆◆開発メモ◆◆◆  \n",
+    "ゆくゆくはSSOに対応し、コンテナ立ち上げ毎のユーザー名、パスワード、メールアドレスの入力を避けたい"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "b5bb6bcc",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [],
+   "source": [
+    "import json\n",
+    "from scripts import utils\n",
+    "\n",
+    "# 以下の認証の手順で用いる、\n",
+    "# GINのドメイン名等をパラメタファイルから取得する\n",
+    "params = {}\n",
+    "with open(utils.fetch_param_file_path(), mode='r') as f:\n",
+    "    params = json.load(f)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "b8192cc3",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "認証が正常に完了しました。次の手順へお進みください。\n"
+     ]
+    }
+   ],
+   "source": [
+    "import os\n",
+    "import time\n",
+    "import getpass\n",
+    "import requests\n",
+    "\n",
+    "from IPython.display import clear_output\n",
+    "from requests.auth import HTTPBasicAuth\n",
+    "from http import HTTPStatus\n",
+    "\n",
+    "tokens = []\n",
+    "access_token = {}\n",
+    "\n",
+    "# 正常に認証が終わるまで繰り返し\n",
+    "while True:\n",
+    "    name = input(\"ユーザー名:\")\n",
+    "    password = getpass.getpass(\"パスワード:\")\n",
+    "    email = input(\"メールアドレス:\")\n",
+    "    clear_output()\n",
+    "    \n",
+    "    # GIN API Basic Authentication\n",
+    "    # refs: https://docs.python-requests.org/en/master/user/authentication/\n",
+    "    \n",
+    "    # 既存のトークンがあるか確認する\n",
+    "    response = requests.get(params['siblings']['ginHttp']+'api/v1/users/' + name + '/tokens', auth=(name, password))\n",
+    "    if response.status_code == HTTPStatus.UNAUTHORIZED:\n",
+    "        print(\"ユーザ名、またはパスワードが間違っています。\\n恐れ入りますがもう一度ご入力ください。\")\n",
+    "        continue\n",
+    "    \n",
+    "    tokens = response.json()\n",
+    "    if len(tokens) >= 1:\n",
+    "        access_token = response.json()[-1]\n",
+    "        clear_output()\n",
+    "        break\n",
+    "    elif len(tokens) < 1:\n",
+    "        # 既存のトークンがなければ作成する\n",
+    "        response = requests.post(params['siblings']['ginHttp']+'api/v1/users/' + name + '/tokens', data={\"name\": \"system-generated\"} ,auth=(name, password))\n",
+    "        if response.status_code == HTTPStatus.CREATED:\n",
+    "            access_token = response.json()\n",
+    "            clear_output()\n",
+    "            break\n",
+    "    \n",
+    "!git config --global user.name $name\n",
+    "!git config --global user.email $email\n",
+    "!cp ~/.gitconfig ~/WORKFLOW/PACKAGE/.gitconfig\n",
+    "\n",
+    "print(\"認証が正常に完了しました。次の手順へお進みください。\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "47ab2ef7",
+   "metadata": {},
+   "source": [
+    "## 2. データ同期のための設定をする\n",
+    "\n",
+    "この手順では、今の実行環境とデータガバナンス機能のリポジトリでデータの同期をとるための準備をします。  \n",
+    "以下を実行することで、システムがデータ同期の準備の手続きを行います。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "312e1feb",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [],
+   "source": [
+    "%%bash\n",
+    "#!/bin/bash\n",
+    "if [ ! -e ~/.ssh/id_ed25519 ]; then\n",
+    "    # 鍵ペアが無ければ作成\n",
+    "    ssh-keygen -t ed25519 -N \"\" -f ~/.ssh/id_ed25519\n",
+    "fi\n",
+    "if [ ! -d ~/.datalad/ ]; then\n",
+    "    # Dataladのデータセットでなければデータセット化する\n",
+    "    datalad create --force /home/jovyan\n",
+    "fi"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "55a949e7",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Public key is ready.\n"
+     ]
+    }
+   ],
+   "source": [
+    "# 公開鍵アップロード\n",
+    "# refs: https://github.com/gogs/docs-api/blob/master/Users/Public%20Keys.md#create-a-public-key\n",
+    "import os\n",
+    "import requests\n",
+    "import time\n",
+    "from http import HTTPStatus\n",
+    "\n",
+    "import json\n",
+    "from scripts import utils\n",
+    "\n",
+    "pubkey = !cat ~/.ssh/id_ed25519.pub\n",
+    "\n",
+    "# 認証時に取得したトークンを使ってPOSTリクエスト\n",
+    "response = requests.post(\n",
+    "                params['siblings']['ginHttp']+'api/v1/user/keys?token=' + access_token['sha1'],\n",
+    "                data={\n",
+    "                    \"title\": \"system-generated-\"+str(time.time()),\n",
+    "                    \"key\": pubkey[0]\n",
+    "                })\n",
+    "msg = response.json()\n",
+    "\n",
+    "# コンテナを消す際にコンテナとつなぐための公開鍵も削除のため、\n",
+    "# パラメータとしてGINから発行された鍵IDを保存\n",
+    "if response.status_code == HTTPStatus.CREATED:\n",
+    "    # params.jsonへの追記(鍵ID)\n",
+    "    params['ginKeyId'] = str(response.json()['id'])\n",
+    "    with open(utils.fetch_param_file_path(), mode='w') as f:\n",
+    "        json.dump(params, f, indent=4)\n",
+    "    print('Public key is ready.')\n",
+    "elif msg['message'] == 'Key content has been used as non-deploy key':\n",
+    "    print('Public key is ready before time.')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "e9d09497",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "SSH connection is ready.\n"
+     ]
+    }
+   ],
+   "source": [
+    "from datalad import api\n",
+    "from IPython.display import clear_output\n",
+    "\n",
+    "# sibling url をsshに変更する\n",
+    "%cd ~/\n",
+    "http_url = !git config --get remote.origin.url\n",
+    "for item in http_url:\n",
+    "    ssh_url = item.replace(params['siblings']['ginHttp'], params['siblings']['ginSsh'])\n",
+    "    \n",
+    "# siblingsにGINを登録する\n",
+    "sibling = !datalad siblings -s gin\n",
+    "for item in sibling:\n",
+    "    if 'unknown sibling name' in item:\n",
+    "        api.siblings(action='add', name='gin', url=ssh_url)\n",
+    "    else:\n",
+    "        pass\n",
+    "\n",
+    "clear_output()\n",
+    "print('SSH connection is ready.')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "88c7b474",
+   "metadata": {},
+   "source": [
+    "## 3. リポジトリ内のファイルを更新する"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e18d033c-54af-4e79-be20-d6feaf0b7d8e",
+   "metadata": {},
+   "source": [
+    "### 3-1. Git管理対象外ファイルを.gitignoreで設定する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "baa5f1a7-4097-4b38-9dfa-0126f784fea5",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [],
+   "source": [
+    "%%sh\n",
+    "#!/bin/bash\n",
+    "\n",
+    "if [ ! -f ~/.gitignore ]; then  # .gitignoreファイルがなければ作成\n",
+    "\n",
+    "echo \"\n",
+    "/.cache/\n",
+    "/.conda/\n",
+    "/.config/\n",
+    "/.ipython/\n",
+    "/.local/\n",
+    "/.tmp/\n",
+    "/.bashrc\n",
+    "/.bash_logout\n",
+    "/.profile\n",
+    "/.netrc\n",
+    ".ipynb_checkpoints/\n",
+    ".fonts/\n",
+    ".jupyter/\n",
+    ".npm/\n",
+    ".ssh/\n",
+    ".jupyter-server-log.txt\n",
+    ".gitconfig\n",
+    "\" >> ~/.gitignore\n",
+    "\n",
+    "fi"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "781cf0ad-13ff-4bf3-938d-69ad7030fa18",
+   "metadata": {},
+   "source": [
+    "###  3-2. 実験記録管理ファイルを作成する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "75fec939-2f46-45cf-b0c7-d4c00c36f2e3",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [],
+   "source": [
+    "%%sh\n",
+    "#!/bin/bash\n",
+    "if [ ! -d ~/experiments/ ]; then\n",
+    "    # 実験配置用ディレクトリがなければ作成\n",
+    "    mkdir ~/experiments\n",
+    "fi\n",
+    "\n",
+    "if [ ! -f ~/experiments/pipeline.json ]; then\n",
+    "    # pipeline.jsonがなければ初期化\n",
+    "    echo \"[]\" > ~/experiments/pipeline.json\n",
+    "fi"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "86cc20a7-cc8c-4e80-b892-47de45f07e76",
+   "metadata": {},
+   "source": [
+    "### 3-3 実験パッケージに必要なファイルを追加する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "e05b378c-b2cb-47cd-99ff-ea911bd7d10e",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "/home/jovyan\n"
+     ]
+    }
+   ],
+   "source": [
+    "%cd ~/\n",
+    "\n",
+    "path_base_package = 'WORKFLOW/PACKAGE/base'\n",
+    "\n",
+    "if not os.path.exists(path_base_package + '/.gitignore'):\n",
+    "    !cp .gitignore $path_base_package/.gitignore\n",
+    "\n",
+    "if not os.path.exists(path_base_package + '/EX-WORKFLOW/param_files'):\n",
+    "    !mkdir -p $path_base_package/EX-WORKFLOW/param_files\n",
+    "    !cp WORKFLOW/FLOW/param_files/params.json $path_base_package/EX-WORKFLOW/param_files/params.json\n",
+    "\n",
+    "if not os.path.exists(path_base_package + '/EX-WORKFLOW/util/base_datalad_save_push.ipynb'):\n",
+    "    !cp WORKFLOW/FLOW/util/base_datalad_save_push.ipynb $path_base_package/EX-WORKFLOW/util/base_datalad_save_push.ipynb"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3b0d26fe-3a6a-4e5e-9975-af37ec2125f5",
+   "metadata": {},
+   "source": [
+    "### 3-4. READMEに実行環境へのリンクを追加する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "42e2a369",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "/home/jovyan\n"
+     ]
+    }
+   ],
+   "source": [
+    "import os\n",
+    "%cd ~/\n",
+    "\n",
+    "# READMEに遷移リンクがあれば削除してから、新しく実行環境のリンクを追記する\n",
+    "path = 'README.md'\n",
+    "s = ''\n",
+    "with open(path, 'r') as f:\n",
+    "    s = f.read()\n",
+    "    s = s[:s.find('## ワークフロー実行が2回目以降の場合')]\n",
+    "    \n",
+    "with open(path, 'w') as f:\n",
+    "    f.write(s)\n",
+    "\n",
+    "with open(path, 'a', newline='\\n') as f:\n",
+    "    f.write(\"\\n## ワークフロー実行が2回目以降の場合\\nワークフロー実行環境へ遷移する場合は以下のリンクをクリックしてください<br>https://jupyter.cs.rcos.nii.ac.jp\" + os.environ[\"JUPYTERHUB_SERVICE_PREFIX\"] + \"notebooks/WORKFLOW/FLOW/base_FLOW.ipynb\")\n",
+    "    f.write(\"<br>※上記のリンクからワークフロー機能トップページに遷移できない場合は、「初期設定をする」のmaDMP遷移ボタンからワークフロー機能トップページに遷移してください。その場合maDMPの実行は不要です。\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "771fbbaf",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## 4. 実行結果をデータガバナンス機能に同期する\n",
+    "\n",
+    "ここまでの内容を保存し、データガバナンス機能に同期します。  \n",
+    "以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "94d13667",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# ノートブックを保存する\n",
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7eafffd9",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# SSHホスト(=GIN)を信頼する設定\n",
+    "# ドメイン名がハードコーディングにつき要修正\n",
+    "with open('/home/jovyan/.ssh/config', mode='w') as f:\n",
+    "    f.write('host dg02.dg.rcos.nii.ac.jp\\n\\tStrictHostKeyChecking no\\n\\tUserKnownHostsFile=/dev/null\\n')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5abba0fe",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/WORKFLOW/FLOW/util\n",
+    "pm.execute_notebook(\n",
+    "    './base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = '[GIN] 研究リポジトリ初期設定を完了', TO_GIT=True)\n",
+    ")\n",
+    "print('データ同期が完了しました。')\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ab25d6d7",
+   "metadata": {},
+   "source": [
+    "## 5. ワークフロー図を更新する\n",
+    "\n",
+    "ワークフロー図にこのワークフローが実行済みであることを反映します。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "dc2d2061",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/\n",
+    "path = 'WORKFLOW/images/notebooks.diag'\n",
+    "\n",
+    "with open('.gitignore', 'r') as f:\n",
+    "    text = f.read()\n",
+    "    if text.find(path) == -1:\n",
+    "        !echo \"/\" + $path >> ./.gitignore\n",
+    "\n",
+    "# notebooks.diagのgit管理を外す\n",
+    "!git update-index --skip-worktree $path"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "4ccde93b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/\n",
+    "find = '\"base_required_every_time\"[fontsize = 14];'\n",
+    "replace = '\"base_required_every_time\"[numbered = 済, fontsize = 14];'\n",
+    "\n",
+    "with open(path, 'r') as f:\n",
+    "    s = f.read()\n",
+    "\n",
+    "with open(path, 'w') as f:\n",
+    "    s = s.replace(find, replace)\n",
+    "    f.write(s)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "845ce791",
+   "metadata": {},
+   "source": [
+    "## 6. ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../base_FLOW.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}

+ 0 - 0
WORKFLOW/FLOW/util/scripts/__init__.py


BIN
WORKFLOW/FLOW/util/scripts/__pycache__/__init__.cpython-39.pyc


BIN
WORKFLOW/FLOW/util/scripts/__pycache__/utils.cpython-39.pyc


+ 262 - 0
WORKFLOW/FLOW/util/scripts/nb_utils.py

@@ -0,0 +1,262 @@
+# -*- coding: utf-8 -*-
+import os
+import re
+import sys
+import shutil
+from subprocess import run, CalledProcessError
+from tempfile import TemporaryDirectory
+from pathlib import Path
+from lxml import etree
+from nbformat import read, NO_CONVERT
+from itertools import chain, zip_longest
+from jinja2 import Template
+from datetime import datetime
+
+title_font_size = 12
+item_font_size = 9
+head_margin = 3
+text_margin = 2
+title_font_color = 'rgb(255,140,0)'
+text_font_color = 'rgb(0,0,0)'
+
+SVG_TEXT = '{http://www.w3.org/2000/svg}text'
+SVG_RECT = '{http://www.w3.org/2000/svg}rect'
+
+
+def parse_headers(nb_path):
+    nb = read(str(nb_path), as_version=NO_CONVERT)
+
+    # Notebookのセルからmarkdownの部分を取り出し、行ごとのリストにする
+    lines = [
+        line.strip()
+        for line in chain.from_iterable(
+            cell['source'].split('\n')
+            for cell in nb.cells
+            if cell['cell_type'] == 'markdown'
+        )
+        if len(line.strip()) > 0 and not line.startswith('---')
+    ]
+
+    # h1, h2 の行とその次行の最初の1文を取り出す
+    headers = [
+        (' '.join(line0.split()[1:]),
+         line1.split("。")[0] if line1 is not None else '')
+        for (line0, line1) in zip_longest(lines, lines[1:])
+        if line0.startswith('# ') or line0.startswith('## ')
+    ]
+    # 最初の見出しはtitle, 残りはheadersとして返す
+    return {
+        'path': nb_path.as_posix(),
+        'title': {
+            'text': _to_title_text(nb_path, headers[0][0]),
+            'summary': headers[0][1],
+        },
+        'headers': [
+            {
+                'text': text,
+                'summary': (
+                    summary if not re.match(r'(?:#|!\[)', summary) else ''),
+            }
+            for (text, summary) in headers[1:]
+        ],
+    }
+
+def _to_title_text(nb_path, text):
+    no = nb_path.name.split('-')[0]
+    title = text if not text.startswith('About:') else text[6:]
+    return f'{title}'
+
+def _get_notebook_headers(nb_dir):
+    return dict([
+        (nb.name, parse_headers(nb))
+        for nb in nb_dir.glob("*.ipynb")
+    ])
+
+def notebooks_toc(nb_dir):
+    nb_headers = sorted(
+        _get_notebook_headers(Path(nb_dir)).items(),
+        key=lambda x: x[0])
+
+    return "\n".join(chain.from_iterable([
+        [
+            f'* [{headers["title"]["text"]}]({nb_dir}/{str(nb)})'
+        ] + list(chain.from_iterable([
+            [
+                f'    - {header["text"]}',
+                (f'      - {header["summary"]}'
+                 if len(header["summary"]) > 0 else ''),
+            ]
+            for header in headers['headers']
+        ]))
+        for nb, headers in nb_headers
+    ]))
+
+import json
+
+JSON = ""
+def load_json(PATH):
+    with open(PATH) as f:
+        JSON = json.load(f)
+        return JSON
+
+
+def generate_svg_diag(
+        output='WORKFLOW/images/notebooks.svg',
+        diag='WORKFLOW/images/notebooks.diag',
+        dir_util='WORKFLOW/FLOW/util',
+        dir_01='WORKFLOW/FLOW/01_preparation_phase',
+        dir_02='WORKFLOW/FLOW/02_experimental_phase',
+        dir_03='WORKFLOW/FLOW/03_after_research_phase',
+        font='.fonts/ipag.ttf',
+):
+    with TemporaryDirectory() as workdir:
+        skeleton = Path(workdir) / 'skeleton.svg'
+        _generate_skeleton(skeleton, Path(diag), Path(font))
+        _embed_detail_information(Path(output), skeleton, Path(dir_util), Path(dir_01), Path(dir_02), Path(dir_03))
+        return output
+
+def _generate_skeleton(output, diag, font):
+    run(['blockdiag', '-f', font, '-Tsvg', '-o', output, diag], check=True)
+
+def setup_python_path():
+    ver = sys.version_info
+    lib_path = f'~/.local/lib/python{ver.major}.{ver.minor}/site-packages'
+    lib_path = str(Path(lib_path).expanduser())
+    if lib_path not in sys.path:
+        sys.path.append(lib_path)
+
+def _embed_detail_information(output, skeleton, dir_util, dir_01, dir_02, dir_03):
+    # Notebookのヘッダ取得
+    nb_headers = _get_notebook_headers(dir_util)
+    nb_headers.update(_get_notebook_headers(dir_01))
+    nb_headers.update(_get_notebook_headers(dir_02))
+    nb_headers.update(_get_notebook_headers(dir_03))
+
+    # 雛形の読み込み
+    tree = etree.parse(str(skeleton))
+
+    # 雛形をNotebook情報で置き換え
+    for elem in list(tree.findall(SVG_TEXT)):
+        if _is_target_rect(elem, nb_headers.keys()):
+            nb_name = _find_matching_notebook(nb_headers.keys(), elem.text)
+            _embed_info_in_one_rect(elem, nb_headers, Path('WORKFLOW/FLOW'), nb_name)
+
+    # SVGの保存
+    output.parent.mkdir(parents=True, exist_ok=True)
+    with output.open(mode='wb') as f:
+        f.write(etree.tostring(tree, method='xml', pretty_print=True))
+
+def _is_target_rect(elem, notebooks):
+    return (
+        elem.getprevious() is not None and
+        elem.getprevious().tag == SVG_RECT and
+        len(elem.text) > 0 and
+        _find_matching_notebook(notebooks, elem.text) is not None)
+
+def _find_matching_notebook(notebooks, title):
+    for nb in notebooks:
+        if nb.startswith(title):
+            return nb
+
+def _embed_info_in_one_rect(elem, nb_headers, nb_dir, nb_name):
+    headers = nb_headers[nb_name]
+    nb_file = nb_headers[nb_name]['path']
+    nb_file = nb_file.replace('WORKFLOW/FLOW/', '')
+    rect_elem = elem.getprevious()
+    rect = (
+        (int(rect_elem.attrib['x']), int(rect_elem.attrib['y'])),
+        (int(rect_elem.attrib['width']), int(rect_elem.attrib['height'])))
+    childpos = elem.getparent().index(elem)
+    parent_elem = elem.getparent()
+    remove_texts(elem)
+    title = headers['title']['text']
+    if elem.text.find(':') >= 0:
+        title = title + ' - ' + elem.text.split(':')[1]
+    line_num = insert_title(parent_elem, childpos, rect, title, str(nb_file))
+    insert_headers(parent_elem, childpos, rect, headers['headers'], line_num)
+
+def remove_texts(elem):
+    old_text = elem
+    while old_text is not None:
+        if (old_text.getnext() is not None and
+                old_text.getnext().tag == SVG_TEXT):
+            next_text = old_text.getnext()
+        else:
+            next_text = None
+        old_text.getparent().remove(old_text)
+        old_text = next_text
+
+def insert_title(parent_elem, childpos, rect, title, link):
+    height_title = (
+        text_margin + (title_font_size + text_margin) * 2 + head_margin * 2)
+    lines = split_title(title)
+    if len(lines) == 2:
+        text_elem = create_text(rect, title_font_size, font_weight='bold', font_color=title_font_color)
+        text_elem.text = lines[0]
+        text_elem.attrib['y'] = str(
+                rect[0][1] + head_margin + text_margin + title_font_size)
+        text_elems = [text_elem]
+
+        text_elem = create_text(rect, title_font_size, font_weight='bold', font_color=title_font_color)
+        text_elem.text = lines[1]
+        text_elem.attrib['y'] = str(
+                rect[0][1] + height_title - text_margin - head_margin)
+        text_elems.append(text_elem)
+    else:
+        text_elem = create_text(rect, title_font_size, font_weight='bold', font_color=title_font_color)
+        text_elem.text = title
+        text_elem.attrib['y'] = str(
+                rect[0][1] + height_title // 2 + title_font_size // 2)
+        text_elems = [text_elem]
+
+    parent_elem.insert(childpos, create_anchor(text_elems, link))
+    return len(lines)
+
+def insert_headers(parent_elem, childpos, rect, headers, title_lines):
+    offset_y = (
+        text_margin +
+        (title_font_size + text_margin) * (title_lines + 1) +
+        head_margin * 2 + text_margin)
+    for i, header in enumerate(headers):
+        text_elem = create_text(rect, item_font_size, font_color=text_font_color)
+        text_elem.text = header['text']
+        text_elem.attrib['y'] = str(
+                rect[0][1] + offset_y + (item_font_size + text_margin) * i +
+                item_font_size)
+        parent_elem.insert(childpos, text_elem)
+
+def split_title(title):
+    if u':' in title:
+        return [title[:title.index(u':') + 1], title[title.index(u':') + 1:]]
+    elif len(title) >= 15:
+        words = re.split(r'([-((])', title, 1)
+        ret = words[0:1] + [''.join(x) for x in zip(words[1::2], words[2::2])]
+        return [re.sub(r'^--', '- ', x) for x in ret]
+    else:
+        return [title]
+
+def create_text(rect, font_size, font_color, font_weight='normal', font_style='normal'):
+    text_elem = etree.Element(SVG_TEXT)
+    text_elem.attrib['fill'] = font_color
+    text_elem.attrib['font-family'] = 'sans-serif'
+    text_elem.attrib['font-size'] = str(font_size)
+    text_elem.attrib['font-style'] = font_style
+    text_elem.attrib['font-weight'] = font_weight
+    text_elem.attrib['font-anchor'] = 'middle'
+    text_elem.attrib['x'] = str(rect[0][0] + text_margin)
+    text_elem.attrib['width'] = str(rect[1][0] - text_margin * 2)
+    text_elem.attrib['inline-size'] = '400px'
+    return text_elem
+
+def create_anchor(elems, link):
+    a_elem = etree.Element('a')
+    a_elem.attrib['{http://www.w3.org/1999/xlink}href'] = link
+    for elem in elems:
+        a_elem.append(elem)
+    return a_elem
+
+
+# refs: https://note.nkmk.me/python-if-name-main/
+# maDMP.ipynbからコマンドライン引数でdiagファイルのパスが渡されてくる
+if __name__ == '__main__':
+    generate_svg_diag(diag=sys.argv[1])

+ 36 - 0
WORKFLOW/FLOW/util/scripts/utils.py

@@ -0,0 +1,36 @@
+import json
+
+
+def fetch_param_file_path() -> str:
+    return '/home/jovyan/WORKFLOW/FLOW/param_files/params.json'
+
+
+def fetch_monitoring_param_file_path() -> str:
+    return '/home/jovyan/WORKFLOW/FLOW/param_files/monitoring_params.json'
+
+
+def reflect_monitoring_results(monitoring_item, isOK: bool) -> None:
+    # モニタリング観点名とnotebookへのパスとを取得
+    path_params = fetch_param_file_path()
+    params = {}
+    with open(path_params, 'r') as f:
+        params = json.load(f)
+
+    nb = params['monitoring'][monitoring_item]
+    # nb['name']: モニタリング観点名(str)
+    # nb['path']: Notebookへのパス(str)
+
+    # READMEの内容を取得する
+    with open("/home/jovyan/README.md", "r") as f:
+        readme = f.read()
+    point1 = readme.find("| " + nb['name'] + " |")
+    output = readme[:point1]
+
+    # 該当する行を書き換え
+    output += "| " + nb['name'] + " | [" + ("OK" if isOK else "NG") + "](" + nb['path'] + ") |"
+
+    point2 = readme[point1:].find("\n")
+    output += readme[point1 + point2:]
+
+    with open("/home/jovyan/README.md", "w") as f:
+        f.write(output)

+ 19 - 0
WORKFLOW/PACKAGE/base/.gitignore

@@ -0,0 +1,19 @@
+
+/.cache/
+/.conda/
+/.config/
+/.ipython/
+/.local/
+/.tmp/
+/.bashrc
+/.bash_logout
+/.profile
+/.netrc
+.ipynb_checkpoints/
+.fonts/
+.jupyter/
+.npm/
+.ssh/
+.jupyter-server-log.txt
+.gitconfig
+

+ 62 - 0
WORKFLOW/PACKAGE/base/Dockerfile

@@ -0,0 +1,62 @@
+FROM cschranz/gpu-jupyter:v1.4_cuda-11.2_ubuntu-20.04_python-only
+
+# install netbase
+USER root
+RUN apt update -y \
+    && apt install -y netbase \
+	&& apt-get clean \
+	&& rm -rf /var/lib/apt/lists/*
+
+# mamba installを使いたかったがdatalad pushに失敗するため
+# conda installを利用している(2/2時点)
+RUN conda install --quiet --yes git-annex==8.20210903 \
+    && conda install --quiet --yes git==2.35.0 \
+    && conda install --quiet --yes datalad==0.15.4 \
+    && conda clean -i -t -y
+
+# install the notebook package etc.
+RUN pip install --no-cache --upgrade pip \
+    && pip install --no-cache notebook \
+    && pip install --no-cache jupyter_contrib_nbextensions \
+    && pip install --no-cache git+https://github.com/NII-cloud-operation/Jupyter-LC_run_through \
+    && pip install --no-cache git+https://github.com/NII-cloud-operation/Jupyter-multi_outputs \
+    && pip install --no-cache datalad==0.15.4 \
+    && pip install --no-cache lxml==4.7.1 \
+    && pip install --no-cache blockdiag==3.0.0 \
+    && pip install --no-cache -U nbformat==5.2.0 \
+    && pip install --no-cache papermill==2.3.3 \
+    && pip install --no-cache black==21.12b0
+
+RUN jupyter contrib nbextension install --user \
+    && jupyter nbextensions_configurator enable --user \
+    && jupyter run-through quick-setup --user \
+    && jupyter nbextension install --py lc_multi_outputs --user \
+    && jupyter nbextension enable --py lc_multi_outputs --user
+
+# install Japanese-font (for blockdiag)
+ARG font_deb=fonts-ipafont-gothic_00303-18ubuntu1_all.deb
+RUN mkdir ${HOME}/.fonts \
+    && wget -P ${HOME}/.fonts http://archive.ubuntu.com/ubuntu/pool/universe/f/fonts-ipafont/${font_deb} \
+    && dpkg-deb -x ${HOME}/.fonts/${font_deb} ~/.fonts \
+    && cp ~/.fonts/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf ~/.fonts/ipag.ttf \
+    && rm ${HOME}/.fonts/${font_deb} \
+    && rm -rf ${HOME}/.fonts/etc ${HOME}/.fonts/usr \
+    && rm .wget-hsts
+
+ARG NB_USER=jovyan
+ARG NB_UID=1000
+
+RUN rm -rf ${HOME}/work
+
+# prepare datalad procedure dir
+RUN mkdir -p ${HOME}/.config/datalad/procedures
+
+WORKDIR ${HOME}
+COPY . ${HOME}
+
+USER root
+RUN chown -R ${NB_UID} ${HOME}
+USER ${NB_USER}
+
+# Specify the default command to run
+CMD ["jupyter", "notebook", "--ip", "0.0.0.0"]

+ 187 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/enter_metadata.ipynb

@@ -0,0 +1,187 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# 実験メタデータを入力する\n",
+    "\n",
+    "実験日や実験者などのメタデータを実験記録に追加します。  \n",
+    "上から順番に実行してください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. メタデータを入力する"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "1. 以下のセルを実行して、実験日を入力してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from ipywidgets import Text, Button\n",
+    "from IPython.display import clear_output\n",
+    "import datetime\n",
+    "\n",
+    "d_today = datetime.date.today()\n",
+    "def on_click_callback(clicked_button: Button) -> None:\n",
+    "    global experiment_date\n",
+    "    experiment_date = text.value\n",
+    "    clear_output()\n",
+    "    print(\"登録しました:\", experiment_date)\n",
+    "\n",
+    "# テキストボックス\n",
+    "text = Text(\n",
+    "    value=str(d_today),\n",
+    "    description='実験日:'\n",
+    ")\n",
+    "button = Button(description='入力完了')\n",
+    "button.on_click(on_click_callback)\n",
+    "text.on_submit(on_click_callback)\n",
+    "display(text, button)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "2. 以下のセルを実行して、実験者を入力してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from ipywidgets import Text, Button\n",
+    "from IPython.display import clear_output\n",
+    "import datetime\n",
+    "\n",
+    "def on_click_callback(clicked_button: Button) -> None:\n",
+    "    global experimenter\n",
+    "    experimenter = text.value\n",
+    "    clear_output()\n",
+    "    print(\"登録しました:\", experimenter)\n",
+    "\n",
+    "# テキストボックス\n",
+    "text = Text(\n",
+    "    description='実験者:'\n",
+    ")\n",
+    "button = Button(description='入力完了')\n",
+    "button.on_click(on_click_callback)\n",
+    "text.on_submit(on_click_callback)\n",
+    "display(text, button)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2.メタ情報をファイルに保存する"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### - 2.1 メタ情報をmeta_data.jsonに書き込む"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import json\n",
+    "from collections import OrderedDict\n",
+    "\n",
+    "meta_data = {\n",
+    "    \"experiment_date\": experiment_date,\n",
+    "    \"experimenter\": experimenter\n",
+    "}\n",
+    "with open('../meta_data.json', 'w') as jf:\n",
+    "    json.dump(meta_data, jf, ensure_ascii=False, indent=2)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3. 実行結果をデータガバナンス機能に同期する\n",
+    "\n",
+    "ここまでの内容を保存し、データガバナンス機能に同期します。  \n",
+    "以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "save_path = ['/home/jovyan/meta_data.json', '/home/jovyan/EX-WORKFLOW/enter_metadata.ipynb']\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = 'メタデータ登録', IS_RECURSIVE = False, TO_GIT = True, PATH = save_path)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 4. 実験中ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../experiment.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 176 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/finish.ipynb

@@ -0,0 +1,176 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# 実験を終了する\n",
+    "\n",
+    "実験記録をデータガバナンス機能に保存して、実験を終了します。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1.実行環境構成を記録する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%sh\n",
+    "conda env export -n base > ~/environment.yml\n",
+    "pip freeze > ~/requirements.txt"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. 実験記録をデータガバナンス機能に同期する\n",
+    "\n",
+    "実験記録をデータガバナンス機能に同期します。  \n",
+    "以下のセルを実行してください。\n",
+    "\n",
+    "※データの保存先としてAWS S3準拠のオブジェクトストレージを利用する場合は、  \n",
+    "[こちら](util/operate_S3_annex.ipynb)も併せて実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import glob\n",
+    "\n",
+    "# Git管理のパスのリストを作成する\n",
+    "%cd ~/\n",
+    "files = os.listdir()\n",
+    "# ディレクトリ一覧からGit-annex管理するディレクトリ(input_dataとoutput_data)を排除する\n",
+    "dirs = [f for f in files if os.path.isdir(f)]\n",
+    "dirs.remove('input_data')\n",
+    "dirs.remove('output_data')\n",
+    "# HOME直下のファイルを取得\n",
+    "files = [f for f in files if os.path.isfile(f)]\n",
+    "# Git管理するパスの配列を作成する\n",
+    "files.extend(dirs)\n",
+    "save_path = files\n",
+    "\n",
+    "# Git-annex管理するパスの配列を作成する\n",
+    "annexed_save_path = ['input_data', 'output_data']"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "# Git-annex管理ファイルを保存\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = '実験終了 (1/2)', PATH = annexed_save_path, IS_RECURSIVE = False)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "# Git管理ファイルを保存\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = '実験終了 (2/2)', TO_GIT = True, PATH = save_path, IS_RECURSIVE = False)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3. この実験実行環境を停止・削除する\n",
+    "\n",
+    "コード利用機能を利用している場合、不要になったこの実行環境を停止・削除します。  \n",
+    "コード付帯機能では20までしか実行環境を構築できないため、不要な実行環境は削除することを推奨します。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 3.1 この実行環境を確認する\n",
+    "\n",
+    "以下のセルを実行して実行環境のサーバー名を確認して下さい。  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "\n",
+    "print(os.environ[\"JUPYTERHUB_SERVICE_PREFIX\"].split('/')[3])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 3.2 コード付帯機能のコントロールパネルへ遷移し、実行環境を停止・削除する\n",
+    "\n",
+    "[コントロールパネル](https://jupyter.cs.rcos.nii.ac.jp/hub/home)へ遷移して、`4.1`で確認したサーバーを`stop`、`delete`ボタンをクリックして停止・削除してください。  \n",
+    "※`delete`ボタンは、以下の図のように`stop`ボタンをクリックした後に表示されます。  \n",
+    "![コンテナ削除キャプチャ](https://raw.githubusercontent.com/NII-DG/workflow-template/develop/sections/PACKAGE/base/EX-WORKFLOW/images/%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E5%89%8A%E9%99%A4%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3.png)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 71 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/notebooks.diag

@@ -0,0 +1,71 @@
+   blockdiag {
+    node_width = 230;
+    node_height = 145;
+    span_width = 40
+
+        group {
+            orientation = portrait;
+            shape = line;
+            style = none;
+
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#ffeed9";
+                    "0. 実験ワークフロー実行準備"[label = "0. 実験ワークフロー実行準備", fontsize = 14];
+                    "required_every_time"[fontsize = 14];
+                }
+            }
+
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#fcdcb1";
+                    "1. 実験準備"[label = "1. 実験準備", fontsize = 14];
+                    "enter_metadata";
+                }
+            }
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    color = "#ffd6a1";
+                    orientation = portrait;
+                    "2. 実験"[label = "2. 実験", fontsize = 14];
+                    "prepare_input_data";
+                    "save";
+                }
+            }
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#ffb95c";
+                    "3. 実験データなどの整理"[label = "3. 実験データなどの整理", fontsize = 14];
+                }
+            }
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#ffa329";
+                    "4. 実験終了"[label = "4. 実験終了", fontsize = 14];
+                    "finish";
+                }
+            }
+        }
+
+        "0. 実験ワークフロー実行準備" -> "1. 実験準備" -> "2. 実験" -> "3. 実験データなどの整理" -> "4. 実験終了";
+
+    }

BIN
WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/usecase_annex_s3_1.png


BIN
WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/usecase_annex_s3_2.png


BIN
WORKFLOW/PACKAGE/base/EX-WORKFLOW/images/コンテナ削除キャプチャ.png


+ 24 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/param_files/params.json

@@ -0,0 +1,24 @@
+{
+    "siblings": {
+        "ginHttp": "http://dg02.dg.rcos.nii.ac.jp/",
+        "ginSsh": "ssh://root@dg02.dg.rcos.nii.ac.jp:3001/",
+        "gitHugibHttp": "https://github.com/",
+        "gitHubSsh": "git@github.com:"
+    },
+    "rcosBinderUrl": "https://jupyter.cs.rcos.nii.ac.jp",
+    "monitoring": {
+        "dataAmount": {
+            "name": "\u30c7\u30fc\u30bf\u5bb9\u91cf",
+            "path": "./WORKFLOW/FLOW/02_experimental_phase/base_monitor_data_size.ipynb"
+        },
+        "datasetStructure": {
+            "name": "\u30c7\u30fc\u30bf\u69cb\u9020",
+            "path": "./WORKFLOW/FLOW/02_experimental_phase/base_monitor_dataset_structure.ipynb"
+        },
+        "reproducibility": {
+            "name": "\u518d\u73fe\u6027",
+            "path": "./WORKFLOW/FLOW/02_experimental_phase/base_monitor_reproducibility.ipynb"
+        }
+    },
+    "ginKeyId": "450"
+}

+ 198 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/prepare_input_data.ipynb

@@ -0,0 +1,198 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# 他リポジトリから実験の入力データを用意する\n",
+    "\n",
+    "[データガバナンス機能](http://dg02.dg.rcos.nii.ac.jp/)に公開されているリポジトリやあなたのリポジトリのデータをこの実験の入力データとして用意します。  \n",
+    "一括でファイルの実体をダウンロードするのではなく、必要なファイルのみファイルの実体をダウンロードするため、大容量データでも時間がかかりにくくなります。  \n",
+    "上から順番に実行してください。\n",
+    "\n",
+    "※もし入力データとして利用したいファイルの実体が「git-annex」によってAWS S3準拠のオブジェクトストレージに保存されている場合、[こちら](util/operate_s3_annex.ipynb)を実行することで当該データを取得できます。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. 入力データにするリポジトリのURLを入力する\n",
+    "\n",
+    "以下のセルを実行して、入力データにしたいリポジトリのURLを入力してください。  \n",
+    "入力したURLの履歴をこのノートブックに保存したい場合は、実行後にセルの左側に表示されるピンマークをクリックしてください。  \n",
+    "※入力に誤りがある場合は、再度このセルを実行して下さい。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from ipywidgets import Text, Button, Layout\n",
+    "from IPython.display import clear_output\n",
+    "import datetime\n",
+    "\n",
+    "def on_click_callback(clicked_button: Button) -> None:\n",
+    "    global input_repo\n",
+    "    input_repo = text.value\n",
+    "    clear_output()\n",
+    "    print(\"入力完了しました:\", input_repo)\n",
+    "\n",
+    "# テキストボックス\n",
+    "text = Text(\n",
+    "    description='URL:',\n",
+    "    placeholder='http://dg02.dg.rcos.nii.ac.jp/user/repository_title.git',\n",
+    "    layout=Layout(width='500px')\n",
+    ")\n",
+    "button = Button(description='入力完了')\n",
+    "button.on_click(on_click_callback)\n",
+    "text.on_submit(on_click_callback)\n",
+    "display(text, button)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. リポジトリのディレクトリ階層をダウンロードする"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# URLからリポジトリ名を抽出する\n",
+    "input_repo_title = input_repo.split('/')[-1].replace('.git', '')\n",
+    "\n",
+    "# 入力データリポジトリをサブデータセットとしてクローンする\n",
+    "%cd ~/\n",
+    "!datalad clone -d . $input_repo \\input_data/$input_repo_title"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3. ファイルの実体をダウンロードする\n",
+    "\n",
+    "出力データ(output_dataフォルダ配下)全ての実体をダウンロードしたい場合は`3.1`を、  \n",
+    "特定のファイルを選んで実体をダウンロードしたい場合は`3.2`を実行してください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 3.1 出力データ(output_dataフォルダ配下)の実体をダウンロードする\n",
+    "\n",
+    "指定したリポジトリの出力データ(output_dataフォルダ配下)全ての実体をダウンロードしたい場合に実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/input_data/$input_repo_title/output_data\n",
+    "!datalad get . -r"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 3.2 特定のファイルを指定して実体をダウンロードする\n",
+    "\n",
+    "出力データの特定のファイルや、出力データ以外のファイルをダウンロードしたい場合に、以下のセルの`\"ファイルパス\"`をダウンロードしたいファイルパスに書き換えてから実行してください。  \n",
+    "※複数ファイルの実体をダウンロードしたい場合は、`!datalad get \"ファイルパス\"`の処理を必要な数だけコピーしてください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/input_data/$input_repo_title/output_data\n",
+    "\n",
+    "# (例)\n",
+    "# !datalad get \"sample_a.csv\"\n",
+    "# !datalad get \"b/sample_b.csv\"\n",
+    "\n",
+    "!datalad get \"ファイルパス\""
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 4. 実行結果をデータガバナンス機能に同期する\n",
+    "\n",
+    "ここまでの内容を保存し、データガバナンス機能に同期します。  \n",
+    "以下のセルを実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "save_path = [ '/home/jovyan/input_data/'+input_repo_title, '/home/jovyan/.gitmodules']\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = '入力データの準備', PATH = save_path, IS_RECURSIVE = False)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 6. 実験中ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../experiment.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 143 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/save.ipynb

@@ -0,0 +1,143 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# データガバナンス機能に途中保存する\n",
+    "\n",
+    "実験終了前の段階で実験をデータガバナンス機能に途中保存します。  \n",
+    "上から順番に実行してください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. コミットメッセージを変数に入力する\n",
+    "\n",
+    "以下のセルを実行して、実験作業のログとして残す短いメッセージを入力してください。  \n",
+    "※入力値に誤りがある場合、もう一度実行することで訂正ができます。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "message = input()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. 実験をデータガバナンス機能に途中保存する\n",
+    "\n",
+    "※データの保存先としてAWS S3準拠のオブジェクトストレージを利用する場合は、  \n",
+    "[こちら](util/operate_S3_annex.ipynb)を実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import glob\n",
+    "\n",
+    "# Git管理のパスのリストを作成する\n",
+    "%cd ~/\n",
+    "files = os.listdir()\n",
+    "# ディレクトリ一覧からGit-annex管理するディレクトリ(input_dataとoutput_data)を排除する\n",
+    "dirs = [f for f in files if os.path.isdir(f)]\n",
+    "dirs.remove('input_data')\n",
+    "dirs.remove('output_data')\n",
+    "# HOME直下のファイルを取得\n",
+    "files = [f for f in files if os.path.isfile(f)]\n",
+    "# Git管理するパスの配列を作成する\n",
+    "files.extend(dirs)\n",
+    "save_path = files\n",
+    "\n",
+    "# Git-annex管理するパスの配列を作成する\n",
+    "annexed_save_path = ['input_data', 'output_data']"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "# Git-annex管理ファイルを保存\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = message + ' (1/2)', PATH = annexed_save_path, IS_RECURSIVE = False)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "# Git管理ファイルを保存\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = message + ' (2/2)', TO_GIT = True, PATH = save_path, IS_RECURSIVE = False)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 4. 実験中ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../experiment.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 91 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/base_datalad_save_push.ipynb

@@ -0,0 +1,91 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# ワークフロー実行結果を書き戻す\n",
+    "\n",
+    "このノートブックでは、変更内容をGINリポジトリに書き戻します。  \n",
+    "※各セクションのセルでpapermillによって自動実行されるノートブックなので手作業での実施は非推奨です。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "tags": [
+     "parameters"
+    ]
+   },
+   "outputs": [],
+   "source": [
+    "SIBLING_NAME = 'gin'\n",
+    "SAVE_MESSAGE = '[GIN] modify on binder'\n",
+    "PATH = '/home/jovyan'\n",
+    "IS_RECURSIVE = True\n",
+    "TO_GIT = False\n",
+    "RESULT_RENDERER = 'default'"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1.書き戻しの準備を行う"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from datalad import api\n",
+    "\n",
+    "api.save(message=SAVE_MESSAGE, path=PATH, recursive=IS_RECURSIVE, to_git=TO_GIT)\n",
+    "api.unlock(PATH)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2.変更内容を書き戻す"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from datalad import api\n",
+    "\n",
+    "api.push(to=SIBLING_NAME, result_renderer=RESULT_RENDERER, path=PATH, recursive=IS_RECURSIVE)"
+   ]
+  }
+ ],
+ "metadata": {
+  "celltoolbar": "Tags",
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 340 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/operate_s3_annex.ipynb

@@ -0,0 +1,340 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# S3準拠オブジェクトストレージを大容量データの保管場所として使う\n",
+    "\n",
+    "このNotebookは[AWS S3](https://aws.amazon.com/jp/s3/)に準拠したオブジェクトストレージを大容量データの保管場所として利用する際のサポートを提供します。\n",
+    "\n",
+    "## データガバナンス機能における大容量データの管理について\n",
+    "\n",
+    "データガバナンス機能では、大容量データの管理にあたって、データファイルのメタデータ(ファイル名、作成日時、実データの所在等の情報)と実データとを分けて管理することができる技術「git-annex」を採用しています。これにより、目下必要となる実データのみをこの実験環境にダウンロードできるため、データ利用の利便性が向上するメリットがあります。このNotebookでは、この管理手法を用いた実データの操作をサポートします。\n",
+    "\n",
+    "## このNotebookでできること(ユースケース例を基に)\n",
+    "\n",
+    "### 例① データをgit-annex管理下へ移管する\n",
+    "\n",
+    "![usecase_annex_s3_1](https://raw.githubusercontent.com/NII-DG/workflow-template/main/PACKAGE/base/EX-WORKFLOW/images/usecase_annex_s3_1.png)\n",
+    "\n",
+    "### 例② git-annex管理下のデータを利用する\n",
+    "\n",
+    "![usecase_annex_s3_2](https://raw.githubusercontent.com/NII-DG/workflow-template/main/PACKAGE/base/EX-WORKFLOW/images/usecase_annex_s3_2.png)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 0.オブジェクトストレージとの接続設定をする\n",
+    "\n",
+    "データ保管の場所として、オブジェクトストレージをシステムが認識するための設定を行います。\n",
+    "\n",
+    "※ここでは、適切なキーとシークレットアクセスキーとのIDが必要です。詳しくは、各オブジェクトストレージのドキュメントをご覧いただくか、各管理者にお問い合わせください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "from IPython.display import clear_output\n",
+    "\n",
+    "# 将来的にはGRDM等を利用したキー情報管理を検討する\n",
+    "os.environ['AWS_ACCESS_KEY_ID'] = input('アクセスキーID: ')\n",
+    "os.environ['AWS_SECRET_ACCESS_KEY'] = input('シークレットアクセスキー: ')\n",
+    "\n",
+    "clear_output()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import ipywidgets as widgets\n",
+    "from IPython.display import display\n",
+    "\n",
+    "def on_click_callback(clicked_button: widgets.Button) -> str:\n",
+    "    with output:\n",
+    "        output.clear_output()\n",
+    "        # セレクタで選択されているアイテムを使う\n",
+    "        print(f'{select.value}が選択されました。')\n",
+    "        print('次のセルでは、このオブジェクトストレージへの接続処理を進めます。')\n",
+    "        print('選択したストレージでよろしければ、次のセルの実行へお進みください。')\n",
+    "        print('もし選び直す場合は、もう一度上のボックスから選択した後、「決定」ボタンを押してください。')\n",
+    "        \n",
+    "print('どのオブジェクトストレージ(S3)を使いますか?')\n",
+    "print('\\t※AWS: Amazon Web Service上で稼働するストレージ\\n\\t  mdx: mdx環境上で稼働するストレージ')\n",
+    "\n",
+    "list_s3 = ['AWS', 'mdx']\n",
+    "\n",
+    "# select.valueの値を次のセルで処理の場合分けに利用する\n",
+    "select = widgets.Select(options=list_s3)\n",
+    "button = widgets.Button(description='決 定')\n",
+    "button.on_click(on_click_callback)\n",
+    "\n",
+    "output = widgets.Output(layour={'border': '1px solid black'})\n",
+    "\n",
+    "display(select, button, output)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print('データをアップロード/ダウンロードしたいオブジェクトストレージのバケット名を入力してください。')\n",
+    "print('入力された名前のバケットがなければ作成され、あればその既存のバケットが利用されます。')\n",
+    "bucket_name = input('バケット名:')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Note: 前までのセルから、select.valueとbucket_nameとを利用している\n",
+    "\n",
+    "import time\n",
+    "\n",
+    "# 次のセルでも利用する変数のため、関数の外で宣言している\n",
+    "special_remote_name = 'remote_' + select.value + '_' + bucket_name\n",
+    "\n",
+    "def connect_s3_storage():\n",
+    "    print('接続設定中です...')\n",
+    "    try:\n",
+    "        msg = ''\n",
+    "        if select.value == 'AWS':\n",
+    "            # for Amazon Web Service(AWS) S3\n",
+    "            msg = !git-annex initremote $special_remote_name type=S3 bucket=$bucket_name encryption=none autoenable=true\n",
+    "        elif select.value == 'mdx':\n",
+    "            # for mdx(s3ds.mdx.jp) S3\n",
+    "            msg = !git-annex initremote $special_remote_name type=S3 port=443 host=s3ds.mdx.jp bucket=$bucket_name encryption=none signature=v2 requeststyle=path autoenable=true\n",
+    "        else:\n",
+    "            raise ValueError('select.value is wrong: ' + select.value) \n",
+    "            \n",
+    "        if msg[0] in 'There is already a special remote':\n",
+    "            !git-annex enableremote $special_remote_name\n",
+    "        print(select.value + 'への接続設定が完了しました。')\n",
+    "    except:\n",
+    "        print('処理を完了できませんでした。\\n利用するオブジェクトストレージが選択されていない可能性があります。')\n",
+    "        print('前のセルを実行いただき、利用したいオブジェクトストレージを選んでください。')\n",
+    "        print('もし選択してもこのメッセージが表示されている場合、恐れ入りますがデータガバナンス機能ヘルプセンター(仮)へお問い合わせください。')\n",
+    "        return\n",
+    "\n",
+    "if __name__ == '__main__':\n",
+    "    connect_s3_storage()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1-A. 実データをオブジェクトストレージへ保存する\n",
+    "\n",
+    "この手順では、任意のデータを「git-annex」による管理下に置き、「0.オブジェクトストレージとの接続設定をする」で設定したオブジェクトストレージへ、実データをアップロードする処理のサポートを提供します。\n",
+    "\n",
+    "以下のセルを順番に実行し、表示にしたがって操作することでデータをアップロードできます。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# データフォルダ内のフォルダ構造を取得し、どれをannex管理するか決める\n",
+    "import os\n",
+    "import ipywidgets as widgets\n",
+    "from ipywidgets import Layout\n",
+    "from IPython.display import display\n",
+    "\n",
+    "from scripts import utils\n",
+    "\n",
+    "####################################################################################################\n",
+    "########################################## callback functions ######################################\n",
+    "####################################################################################################\n",
+    "def on_click_callback(clicked_button: widgets.Button) -> None:\n",
+    "    with output:\n",
+    "        output.clear_output()\n",
+    "        # セレクタで選択されているアイテムを使う\n",
+    "        print(f'選択されたファイル:{upload_selects.value}')\n",
+    "        print('これらのファイルをオブジェクトストレージへ保存します。問題ない場合は、次のセルへ進んでください。')\n",
+    "        print('選び直す場合は、再度ボックスから選択し、「決定」ボタンを押してください。')\n",
+    "        \n",
+    "def on_click_callback_ALL(clicked_button: widgets.Button):\n",
+    "    with output:\n",
+    "        print('上のボックスに表示されている、全てのファイルをオブジェクトストレージへ保存します。\\n問題ない場合は、次のセルへ進んでください。')\n",
+    "        print('選び直す場合は、再度ボックスから選択し、「決定」ボタンを押してください。')\n",
+    "        upload_selects.value = data_files\n",
+    "\n",
+    "####################################################################################################\n",
+    "####################################################################################################\n",
+    "####################################################################################################\n",
+    "\n",
+    "print('どのデータをアップロードしますか?')\n",
+    "print(' ◆◆◆ctrlキー(MAC:commandキー)を押しながらクリックすることで、複数データを選択できます◆◆◆')\n",
+    "\n",
+    "home_path = '/home/jovyan'\n",
+    "annexed_data_paths = [os.path.join(home_path, 'input_data'), os.path.join(home_path, 'output_data')]\n",
+    "data_files = []\n",
+    "selects = []\n",
+    "\n",
+    "# 表示するボタン幅のデフォルト値\n",
+    "default_width = '40%'\n",
+    "\n",
+    "# データフォルダの読み込み\n",
+    "for data_path in annexed_data_paths:\n",
+    "    data_files += utils.fetch_files(data_path)\n",
+    "\n",
+    "data_files.sort()\n",
+    "\n",
+    "# upload_selects.valueの値を次のセルで処理の場合分けに利用する\n",
+    "upload_selects = widgets.SelectMultiple(options=data_files, layout=Layout(width=default_width))\n",
+    "button = widgets.Button(description='決 定', layout=Layout(width=default_width))\n",
+    "button.on_click(on_click_callback)\n",
+    "\n",
+    "# 「すべてアップロードする」がクリックされたら全ファイルをupload_selectsに追加\n",
+    "button_all = widgets.Button(description='すべてアップロードする', layout=Layout(width=default_width))\n",
+    "button_all.on_click(on_click_callback_ALL)\n",
+    "\n",
+    "output = widgets.Output()\n",
+    "\n",
+    "display(upload_selects, button, button_all, output)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "upload_data_paths = list(upload_selects.value)\n",
+    "\n",
+    "index = 1\n",
+    "for save_path in upload_data_paths:\n",
+    "    print(f'保存中です... ({index}/{len(upload_data_paths)})')\n",
+    "    pm.execute_notebook(\n",
+    "        './base_datalad_save_push.ipynb',\n",
+    "        '/home/jovyan/.local/push_log.ipynb',\n",
+    "        parameters = dict(SIBLING_NAME=special_remote_name, SAVE_MESSAGE='[GIN] データを保存', PATH=save_path, TO_GIT=False)\n",
+    "    )\n",
+    "    index += 1\n",
+    "\n",
+    "print('データのアップロードが完了しました。')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1-B. 実データをオブジェクトストレージから取得する\n",
+    "\n",
+    "この手順では、「0.オブジェクトストレージとの接続設定をする」で設定したオブジェクトストレージから、「git-annex」の管理下におかれた実データをダウンロードする際のサポートを提供します。\n",
+    "\n",
+    "以下のセルを順番に実行し、表示にしたがって操作することでデータをアップロードできます。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import ipywidgets as widgets\n",
+    "from ipywidgets import Layout\n",
+    "from IPython.display import display\n",
+    "\n",
+    "from scripts import utils\n",
+    "\n",
+    "####################################################################################################\n",
+    "########################################## callback functions ######################################\n",
+    "####################################################################################################\n",
+    "def on_click_callback(clicked_button: widgets.Button) -> None:\n",
+    "    with output:\n",
+    "        output.clear_output()\n",
+    "        # セレクタで選択されているアイテムを使う\n",
+    "        print(f'選択されたファイル:{download_selects.value}')\n",
+    "        print('これらのファイルをオブジェクトストレージから取得します。問題ない場合は、次のセルへ進んでください。')\n",
+    "        print('選び直す場合は、再度ボックスから選択し、「決定」ボタンを押してください。')\n",
+    "        \n",
+    "def on_click_callback_ALL(clicked_button: widgets.Button):\n",
+    "    with output:\n",
+    "        print('上のボックスに表示されている、全てのファイルをオブジェクトストレージから取得します。\\n問題ない場合は、次のセルへ進んでください。')\n",
+    "        print('選び直す場合は、再度ボックスから選択し、「決定」ボタンを押してください。')\n",
+    "        download_selects.value = data_files\n",
+    "\n",
+    "####################################################################################################\n",
+    "####################################################################################################\n",
+    "####################################################################################################\n",
+    "\n",
+    "print('どのデータをダウンロードしますか?')\n",
+    "print(' ◆◆◆ctrlキー(MAC:commandキー)を押しながらクリックすることで、複数データを選択できます◆◆◆')\n",
+    "\n",
+    "home_path = '/home/jovyan'\n",
+    "annexed_data_paths = [os.path.join(home_path, 'input_data'), os.path.join(home_path, 'output_data')]\n",
+    "data_files = []\n",
+    "selects = []\n",
+    "\n",
+    "# 表示するボタン幅のデフォルト値\n",
+    "default_width = '40%'\n",
+    "\n",
+    "# データフォルダの読み込み\n",
+    "for data_path in annexed_data_paths:\n",
+    "    data_files += utils.fetch_files(data_path)\n",
+    "\n",
+    "data_files.sort()\n",
+    "    \n",
+    "# download_selects.valueの値を次のセルで処理の場合分けに利用する\n",
+    "download_selects = widgets.SelectMultiple(options=data_files, layout=Layout(width=default_width))\n",
+    "button = widgets.Button(description='決 定', layout=Layout(width=default_width))\n",
+    "button.on_click(on_click_callback)\n",
+    "\n",
+    "# 「すべてアップロードする」がクリックされたら全ファイルをdownload_selectsに追加\n",
+    "button_all = widgets.Button(description='すべてダウンロードする', layout=Layout(width=default_width))\n",
+    "button_all.on_click(on_click_callback_ALL)\n",
+    "\n",
+    "output = widgets.Output(layour={'border': '1px solid black'})\n",
+    "\n",
+    "display(download_selects, button, button_all, output)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Note: 前のセルからdownload_selects.valueを利用している\n",
+    "download_data_paths = list(download_selects.value)\n",
+    "\n",
+    "index = 1\n",
+    "for get_path in download_data_paths:\n",
+    "    print(f'データ取得中です... ({index}/{len(download_data_paths)})')\n",
+    "    !datalad get $get_path\n",
+    "    !datalad unlock $get_path\n",
+    "    index += 1\n",
+    "\n",
+    "print('実データのダウンロードが完了しました。')"
+   ]
+  }
+ ],
+ "metadata": {
+  "language_info": {
+   "name": "python"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 405 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/required_every_time.ipynb

@@ -0,0 +1,405 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "4719bc46",
+   "metadata": {},
+   "source": [
+    "# 必要な準備を行う\n",
+    "\n",
+    "実験中のワークフロー実行のために必要な準備を行います。  \n",
+    "上から順番に実行してください。"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "dd0a038d",
+   "metadata": {},
+   "source": [
+    "## 1. ユーザー認証を行う\n",
+    "\n",
+    "この手順では、あなたのユーザ情報をシステムに認証させる手続きを行います。  \n",
+    "以下のセルを実行行し、画面の表示に沿ってデータガバナンス機能に登録したユーザー名、パスワード、メールアドレスを入力してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "bf28557d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/EX-WORKFLOW/util/scripts\n",
+    "import json\n",
+    "from scripts import utils\n",
+    "\n",
+    "# 以下の認証の手順で用いる、\n",
+    "# GINのドメイン名等をパラメタファイルから取得する\n",
+    "params = {}\n",
+    "with open(utils.fetch_param_file_path(), mode='r') as f:\n",
+    "    params = json.load(f)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "61c68c98",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import time\n",
+    "import getpass\n",
+    "import requests\n",
+    "\n",
+    "from IPython.display import clear_output\n",
+    "from requests.auth import HTTPBasicAuth\n",
+    "from http import HTTPStatus\n",
+    "\n",
+    "# 正常に認証が終わるまで繰り返し\n",
+    "while True:\n",
+    "    name = input(\"ユーザー名:\")\n",
+    "    password = getpass.getpass(\"パスワード:\")\n",
+    "    email = input(\"メールアドレス:\")\n",
+    "    clear_output()\n",
+    "    \n",
+    "    # GIN API Basic Authentication\n",
+    "    # refs: https://docs.python-requests.org/en/master/user/authentication/\n",
+    "    \n",
+    "    # 既存のトークンがあるか確認する\n",
+    "    response = requests.get(params['siblings']['ginHttp']+'api/v1/users/' + name + '/tokens', auth=(name, password))\n",
+    "    tokens = response.json()\n",
+    "\n",
+    "    # 既存のトークンがなければ作成する\n",
+    "    if len(tokens) < 1:\n",
+    "        response = requests.post(params['siblings']['ginHttp']+'api/v1/users/' + name + '/tokens', data={\"name\": \"system-generated\"} ,auth=(name, password))\n",
+    "\n",
+    "    if response.status_code == HTTPStatus.OK or HTTPStatus.CREATED:\n",
+    "        tokens = response.json()\n",
+    "        clear_output()\n",
+    "        print(\"認証が正常に完了しました。次の手順へお進みください。\")\n",
+    "        break\n",
+    "    else:\n",
+    "        clear_output()\n",
+    "        print(\"ユーザ名、またはパスワードが間違っています。\\n恐れ入りますがもう一度ご入力ください。\")\n",
+    "\n",
+    "!git config --global user.name $name\n",
+    "!git config --global user.email $email"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fce82ba7",
+   "metadata": {},
+   "source": [
+    "## 2. データ同期のための設定をする\n",
+    "\n",
+    "この手順では、今の実行環境とデータガバナンス機能のリポジトリでデータの同期をとるための準備をします。  \n",
+    "以下を実行することで、システムがデータ同期の準備の手続きを行います。\n",
+    "\n",
+    "※実データの保存先として、必要であればAWS S3準拠のオブジェクトストレージを利用することもできます。  \n",
+    "もし当該ストレージを利用する場合は、このNotebookと合わせて[こちら](operate_S3_annex.ipynb)も実行してください。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e277f2c2",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%bash\n",
+    "#!/bin/bash\n",
+    "if [ ! -e ~/.ssh/id_ed25519 ]; then\n",
+    "    # 鍵ペアが無ければ作成\n",
+    "    ssh-keygen -t ed25519 -N \"\" -f ~/.ssh/id_ed25519\n",
+    "fi\n",
+    "\n",
+    "if [ ! -d ~/.datalad/ ]; then\n",
+    "    # Dataladのデータセットでなければデータセット化する\n",
+    "    datalad create --force /home/jovyan\n",
+    "fi"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8181ce0f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 公開鍵アップロード\n",
+    "# refs: https://github.com/gogs/docs-api/blob/master/Users/Public%20Keys.md#create-a-public-key\n",
+    "import os\n",
+    "import requests\n",
+    "import time\n",
+    "from http import HTTPStatus\n",
+    "\n",
+    "import json\n",
+    "from scripts import utils\n",
+    "\n",
+    "pubkey = !cat ~/.ssh/id_ed25519.pub\n",
+    "\n",
+    "# 認証時に取得したトークンを使ってPOSTリクエスト\n",
+    "response = requests.post(\n",
+    "                params['siblings']['ginHttp']+'api/v1/user/keys?token=' + tokens[0]['sha1'],\n",
+    "                data={\n",
+    "                    \"title\": \"system-generated-\"+str(time.time()),\n",
+    "                    \"key\": pubkey[0]\n",
+    "                })\n",
+    "msg = response.json()\n",
+    "\n",
+    "# コンテナを消す際にコンテナとつなぐための公開鍵も削除のため、\n",
+    "# パラメータとしてGINから発行された鍵IDを保存\n",
+    "if response.status_code == HTTPStatus.CREATED:\n",
+    "    # params.jsonへの追記(鍵ID)\n",
+    "    params['ginKeyId'] = str(response.json()['id'])\n",
+    "    with open(utils.fetch_param_file_path(), mode='w') as f:\n",
+    "        json.dump(params, f, indent=4)\n",
+    "    print('Public key is ready.')\n",
+    "elif msg['message'] == 'Key content has been used as non-deploy key':\n",
+    "    print('Public key is ready before time.')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "13f1918f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from datalad import api\n",
+    "from IPython.display import clear_output\n",
+    "\n",
+    "# sibling url をsshに変更する\n",
+    "%cd ~/\n",
+    "http_url = !git config --get remote.origin.url\n",
+    "for item in http_url:\n",
+    "    http_url = item\n",
+    "    ssh_url = item.replace(params['siblings']['ginHttp'], params['siblings']['ginSsh'])\n",
+    "    \n",
+    "# siblingsにGINを登録する\n",
+    "sibling = !datalad siblings -s gin\n",
+    "for item in sibling:\n",
+    "    if 'unknown sibling name' in item:\n",
+    "        api.siblings(action='add', name='gin', url=ssh_url)\n",
+    "    else:\n",
+    "        pass\n",
+    "\n",
+    "clear_output()\n",
+    "print('SSH connection is ready.')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4264f9f0",
+   "metadata": {},
+   "source": [
+    "## 3. READMEに実験実行環境へのリンクを追加する\n",
+    "\n",
+    "当実行環境へアクセスするためのリンクを、データガバナンス機能の当実験リポジトリのREADMEに追記します。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "11e7f6fc",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os, urllib\n",
+    "from IPython.display import clear_output\n",
+    "\n",
+    "print('当画面のURLをブラウザからコピーし、以下のフォームに入力してください。')\n",
+    "url = input()\n",
+    "print(url.find(\"/notebooks/\"))\n",
+    "url = url[:url.find(\"/notebooks/\")] + \"/notebooks/experiment.ipynb\"\n",
+    "\n",
+    "%cd ~/\n",
+    "with open('README.md', 'w', newline='\\n') as f:\n",
+    "    f.write(\"\\n## 実験実行環境にアクセスしたい場合\\n以下のリンクをクリックしてください<br>\" + url + \"  \")\n",
+    "    f.write(\"\\n\\n上記リンクからアクセスできない場合は以下のリンクから実験実行環境を再度立ち上げてください  \")\n",
+    "    f.write(\"\\n※「他リポジトリから実験の入力データを用意する」で入力データを用意した場合は、再度必要なファイルの実体をdatalad get 'ファイルパス'で取得する必要があります。  \")\n",
+    "    f.write(\"\\nhttps://binder.cs.rcos.nii.ac.jp/v2/git/\" + urllib.parse.quote(http_url, safe='') + \"/HEAD?filepath=experiment.ipynb\")\n",
+    "\n",
+    "clear_output()\n",
+    "print('READMEに実行環境へのリンクを追加しました。')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ed3fb5e4",
+   "metadata": {},
+   "source": [
+    "## 4. 実行結果を実験リポジトリに同期する"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d12d4375",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7b237228",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/EX-WORKFLOW/util/scripts\n",
+    "from scripts import utils\n",
+    "\n",
+    "# SSHホスト(=GIN)を信頼する設定\n",
+    "utils.config_GIN(ginHttp = params['siblings']['ginHttp'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "4f506e02",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import glob\n",
+    "\n",
+    "# Git管理のパスのリストを作成する\n",
+    "%cd ~/\n",
+    "files = os.listdir()\n",
+    "# ディレクトリ一覧からGit-annex管理するディレクトリ(input_dataとoutput_data)を排除する\n",
+    "dirs = [f for f in files if os.path.isdir(f)]\n",
+    "dirs.remove('input_data')\n",
+    "dirs.remove('output_data')\n",
+    "# HOME直下のファイルを取得\n",
+    "files = [f for f in files if os.path.isfile(f)]\n",
+    "# Git管理するパスの配列を作成する\n",
+    "files.extend(dirs)\n",
+    "save_path = files\n",
+    "\n",
+    "# Git-annex管理するパスの配列を作成する\n",
+    "annexed_save_path = ['input_data', 'output_data']"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3d44ccef",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "# Git-annex管理ファイルを保存\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = 'ワークフロー実行準備 (1/2)', PATH = annexed_save_path, IS_RECURSIVE = False)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9201ef94",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import papermill as pm\n",
+    "\n",
+    "%cd ~/\n",
+    "# Git管理ファイルを保存\n",
+    "pm.execute_notebook(\n",
+    "    'EX-WORKFLOW/util/base_datalad_save_push.ipynb',\n",
+    "    '/home/jovyan/.local/push_log.ipynb',\n",
+    "    parameters = dict(SAVE_MESSAGE = 'ワークフロー実行準備 (2/2)', TO_GIT = True, PATH = save_path, IS_RECURSIVE = False)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1ff4751e",
+   "metadata": {},
+   "source": [
+    "## 5. ワークフロー図を更新する\n",
+    "\n",
+    "ワークフロー図にこのワークフローが実行済みであることを反映します。"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f2e74fd0",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/\n",
+    "path = 'EX-WORKFLOW/images/notebooks.diag'\n",
+    "\n",
+    "with open('.gitignore', 'r') as f:\n",
+    "    text = f.read()\n",
+    "    if text.find(path) == -1:\n",
+    "        !echo \"/\" + $path >> ./.gitignore\n",
+    "\n",
+    "# notebooks.diagのgit管理を外す\n",
+    "!git update-index --skip-worktree $path"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fa6250de",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%cd ~/\n",
+    "find = '\"required_every_time\"[fontsize = 14];'\n",
+    "replace = '\"required_every_time\"[numbered = 済, fontsize = 14];'\n",
+    "\n",
+    "with open(path, 'r') as f:\n",
+    "    s = f.read()\n",
+    "\n",
+    "with open(path, 'w') as f:\n",
+    "    s = s.replace(find, replace)\n",
+    "    f.write(s)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "dc2e4eab",
+   "metadata": {},
+   "source": [
+    "## 6. 実験中ワークフロー機能トップページに遷移する\n",
+    "\n",
+    "続けてワークフロー機能を実行する場合は、[こちら](../../experiment.ipynb)からトップページに遷移できます。  "
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}

+ 256 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/scripts/nb_utils.py

@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+import os
+import re
+import sys
+import shutil
+from subprocess import run, CalledProcessError
+from tempfile import TemporaryDirectory
+from pathlib import Path
+from lxml import etree
+from nbformat import read, NO_CONVERT
+from itertools import chain, zip_longest
+from jinja2 import Template
+from datetime import datetime
+
+title_font_size = 12
+item_font_size = 9
+head_margin = 3
+text_margin = 2
+title_font_color = 'rgb(255,140,0)'
+text_font_color = 'rgb(0,0,0)'
+
+SVG_TEXT = '{http://www.w3.org/2000/svg}text'
+SVG_RECT = '{http://www.w3.org/2000/svg}rect'
+
+
+def parse_headers(nb_path):
+    nb = read(str(nb_path), as_version=NO_CONVERT)
+
+    # Notebookのセルからmarkdownの部分を取り出し、行ごとのリストにする
+    lines = [
+        line.strip()
+        for line in chain.from_iterable(
+            cell['source'].split('\n')
+            for cell in nb.cells
+            if cell['cell_type'] == 'markdown'
+        )
+        if len(line.strip()) > 0 and not line.startswith('---')
+    ]
+
+    # h1, h2 の行とその次行の最初の1文を取り出す
+    headers = [
+        (' '.join(line0.split()[1:]),
+         line1.split("。")[0] if line1 is not None else '')
+        for (line0, line1) in zip_longest(lines, lines[1:])
+        if line0.startswith('# ') or line0.startswith('## ')
+    ]
+    # 最初の見出しはtitle, 残りはheadersとして返す
+    return {
+        'path': nb_path.as_posix(),
+        'title': {
+            'text': _to_title_text(nb_path, headers[0][0]),
+            'summary': headers[0][1],
+        },
+        'headers': [
+            {
+                'text': text,
+                'summary': (
+                    summary if not re.match(r'(?:#|!\[)', summary) else ''),
+            }
+            for (text, summary) in headers[1:]
+        ],
+    }
+
+def _to_title_text(nb_path, text):
+    no = nb_path.name.split('-')[0]
+    title = text if not text.startswith('About:') else text[6:]
+    return f'{title}'
+
+def _get_notebook_headers(nb_dir):
+    return dict([
+        (nb.name, parse_headers(nb))
+        for nb in nb_dir.glob("*.ipynb")
+    ])
+
+def notebooks_toc(nb_dir):
+    nb_headers = sorted(
+        _get_notebook_headers(Path(nb_dir)).items(),
+        key=lambda x: x[0])
+
+    return "\n".join(chain.from_iterable([
+        [
+            f'* [{headers["title"]["text"]}]({nb_dir}/{str(nb)})'
+        ] + list(chain.from_iterable([
+            [
+                f'    - {header["text"]}',
+                (f'      - {header["summary"]}'
+                 if len(header["summary"]) > 0 else ''),
+            ]
+            for header in headers['headers']
+        ]))
+        for nb, headers in nb_headers
+    ]))
+
+import json
+
+JSON = ""
+def load_json(PATH):
+    with open(PATH) as f:
+        JSON = json.load(f)
+        return JSON
+
+
+def generate_svg_diag(
+        output='EX-WORKFLOW/images/notebooks.svg',
+        diag='EX-WORKFLOW/images/notebooks.diag',
+        font='.fonts/ipag.ttf',
+        dir_util='EX-WORKFLOW/util',
+        dir_experiment='EX-WORKFLOW',
+):
+    with TemporaryDirectory() as workdir:
+        skeleton = Path(workdir) / 'skeleton.svg'
+        _generate_skeleton(skeleton, Path(diag), Path(font))
+        _embed_detail_information(Path(output), skeleton, Path(dir_util), Path(dir_experiment))
+        return output
+
+def _generate_skeleton(output, diag, font):
+    run(['blockdiag', '-f', font, '-Tsvg', '-o', output, diag], check=True)
+
+def setup_python_path():
+    ver = sys.version_info
+    lib_path = f'~/.local/lib/python{ver.major}.{ver.minor}/site-packages'
+    lib_path = str(Path(lib_path).expanduser())
+    if lib_path not in sys.path:
+        sys.path.append(lib_path)
+
+def _embed_detail_information(output, skeleton, dir_util, dir_experiment):
+    # Notebookのヘッダ取得
+    nb_headers = _get_notebook_headers(dir_util)
+    nb_headers.update(_get_notebook_headers(dir_experiment))
+
+    # 雛形の読み込み
+    tree = etree.parse(str(skeleton))
+
+    # 雛形をNotebook情報で置き換え
+    for elem in list(tree.findall(SVG_TEXT)):
+        if _is_target_rect(elem, nb_headers.keys()):
+            nb_name = _find_matching_notebook(nb_headers.keys(), elem.text)
+            _embed_info_in_one_rect(elem, nb_headers, Path('EX-WORKFLOW'), nb_name)
+
+    # SVGの保存
+    output.parent.mkdir(parents=True, exist_ok=True)
+    with output.open(mode='wb') as f:
+        f.write(etree.tostring(tree, method='xml', pretty_print=True))
+
+def _is_target_rect(elem, notebooks):
+    return (
+        elem.getprevious() is not None and
+        elem.getprevious().tag == SVG_RECT and
+        len(elem.text) > 0 and
+        _find_matching_notebook(notebooks, elem.text) is not None)
+
+def _find_matching_notebook(notebooks, title):
+    for nb in notebooks:
+        if nb.startswith(title):
+            return nb
+
+def _embed_info_in_one_rect(elem, nb_headers, nb_dir, nb_name):
+    headers = nb_headers[nb_name]
+    nb_file = nb_headers[nb_name]['path']
+    rect_elem = elem.getprevious()
+    rect = (
+        (int(rect_elem.attrib['x']), int(rect_elem.attrib['y'])),
+        (int(rect_elem.attrib['width']), int(rect_elem.attrib['height'])))
+    childpos = elem.getparent().index(elem)
+    parent_elem = elem.getparent()
+    remove_texts(elem)
+    title = headers['title']['text']
+    if elem.text.find(':') >= 0:
+        title = title + ' - ' + elem.text.split(':')[1]
+    line_num = insert_title(parent_elem, childpos, rect, title, str(nb_file))
+    insert_headers(parent_elem, childpos, rect, headers['headers'], line_num)
+
+def remove_texts(elem):
+    old_text = elem
+    while old_text is not None:
+        if (old_text.getnext() is not None and
+                old_text.getnext().tag == SVG_TEXT):
+            next_text = old_text.getnext()
+        else:
+            next_text = None
+        old_text.getparent().remove(old_text)
+        old_text = next_text
+
+def insert_title(parent_elem, childpos, rect, title, link):
+    height_title = (
+        text_margin + (title_font_size + text_margin) * 2 + head_margin * 2)
+    lines = split_title(title)
+    if len(lines) == 2:
+        text_elem = create_text(rect, title_font_size, font_weight='bold', font_color=title_font_color)
+        text_elem.text = lines[0]
+        text_elem.attrib['y'] = str(
+                rect[0][1] + head_margin + text_margin + title_font_size)
+        text_elems = [text_elem]
+
+        text_elem = create_text(rect, title_font_size, font_weight='bold', font_color=title_font_color)
+        text_elem.text = lines[1]
+        text_elem.attrib['y'] = str(
+                rect[0][1] + height_title - text_margin - head_margin)
+        text_elems.append(text_elem)
+    else:
+        text_elem = create_text(rect, title_font_size, font_weight='bold', font_color=title_font_color)
+        text_elem.text = title
+        text_elem.attrib['y'] = str(
+                rect[0][1] + height_title // 2 + title_font_size // 2)
+        text_elems = [text_elem]
+
+    parent_elem.insert(childpos, create_anchor(text_elems, link))
+    return len(lines)
+
+def insert_headers(parent_elem, childpos, rect, headers, title_lines):
+    offset_y = (
+        text_margin +
+        (title_font_size + text_margin) * (title_lines + 1) +
+        head_margin * 2 + text_margin)
+    for i, header in enumerate(headers):
+        text_elem = create_text(rect, item_font_size, font_color=text_font_color)
+        text_elem.text = header['text']
+        text_elem.attrib['y'] = str(
+                rect[0][1] + offset_y + (item_font_size + text_margin) * i +
+                item_font_size)
+        parent_elem.insert(childpos, text_elem)
+
+def split_title(title):
+    if u':' in title:
+        return [title[:title.index(u':') + 1], title[title.index(u':') + 1:]]
+    elif len(title) >= 15:
+        words = re.split(r'([-((])', title, 1)
+        ret = words[0:1] + [''.join(x) for x in zip(words[1::2], words[2::2])]
+        return [re.sub(r'^--', '- ', x) for x in ret]
+    else:
+        return [title]
+
+def create_text(rect, font_size, font_color, font_weight='normal', font_style='normal'):
+    text_elem = etree.Element(SVG_TEXT)
+    text_elem.attrib['fill'] = font_color
+    text_elem.attrib['font-family'] = 'sans-serif'
+    text_elem.attrib['font-size'] = str(font_size)
+    text_elem.attrib['font-style'] = font_style
+    text_elem.attrib['font-weight'] = font_weight
+    text_elem.attrib['font-anchor'] = 'middle'
+    text_elem.attrib['x'] = str(rect[0][0] + text_margin)
+    text_elem.attrib['width'] = str(rect[1][0] - text_margin * 2)
+    return text_elem
+
+def create_anchor(elems, link):
+    a_elem = etree.Element('a')
+    a_elem.attrib['{http://www.w3.org/1999/xlink}href'] = link
+    for elem in elems:
+        a_elem.append(elem)
+    return a_elem
+
+
+# refs: https://note.nkmk.me/python-if-name-main/
+# maDMP.ipynbからコマンドライン引数でdiagファイルのパスが渡されてくる
+if __name__ == '__main__':
+    generate_svg_diag(diag=sys.argv[1])

+ 91 - 0
WORKFLOW/PACKAGE/base/EX-WORKFLOW/util/scripts/utils.py

@@ -0,0 +1,91 @@
+import os
+import json
+import glob
+
+def fetch_param_file_path():
+    param_file_path = '/home/jovyan/EX-WORKFLOW/param_files/params.json'
+    return param_file_path
+
+def fetch_ssh_config_path():
+    ssh_config_path = '/home/jovyan/.ssh/config'
+    return ssh_config_path
+
+def config_mdx(name_mdx, mdxDomain):
+    # mdx接続情報を設定ファイルに反映させる
+    path = fetch_ssh_config_path()
+    s = ''
+
+    if os.path.exists(path):
+        # 設定ファイルがある場合
+        with open(path, "r") as f:
+            s = f.read()
+
+        # mdxの設定があれば該当部分のみ削除して設定を新たに追記する
+        if s.find('Host mdx') == -1:
+            # mdxの設定が無ければ追記する
+            write_mdx_config(mode='a', mdxDomain=mdxDomain, name_mdx=name_mdx)
+
+        else:
+            #mdxの設定があれば該当部分のみ削除して設定を新たに追記する
+            front = s[:s.find('Host mdx')]
+            front = front.rstrip()
+            find_words = 'IdentityFile ~/.ssh/id_rsa\n\tStrictHostKeyChecking no'
+            back = s[(s.find(find_words)  + len(find_words)):]
+            back = back.strip()
+            if len(back) >= 1:
+                s = front + '\n' + back + '\n'
+            elif len(front) <= 0:
+                s = front
+            else:
+                s = front + '\n'
+            with open(path, 'w') as f:
+                f.write(s)
+            write_mdx_config(mode='a', mdxDomain=mdxDomain, name_mdx=name_mdx)
+    else:
+        # 設定ファイルが無い場合、新規作成して新たに書き込む
+        write_mdx_config(mode='w', mdxDomain=mdxDomain, name_mdx=name_mdx)
+
+def write_mdx_config(mode, mdxDomain, name_mdx):
+    path = fetch_ssh_config_path()
+    with open(path, mode) as f:
+        f.write('\nHost mdx\n')
+        f.write('\tHostname ' + mdxDomain + '\n')
+        f.write('\tUser ' + name_mdx + '\n')
+        f.write('\tPort 22\n')
+        f.write('\tIdentityFile ~/.ssh/id_rsa\n')
+        f.write('\tStrictHostKeyChecking no\n')
+
+def config_GIN(ginHttp):
+    # SSHホスト(=GIN)を信頼する設定
+    path = fetch_ssh_config_path()
+    s=''
+    ginDomain = ginHttp.split('/')[-2]
+    if os.path.exists(path):
+        with open(path, 'r') as f:
+            s = f.read()
+        if s.find('host ' + ginDomain +'\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile=/dev/null') == -1:
+        # 設定が無い場合は追記する
+            with open('/home/jovyan/.ssh/config', mode='a') as f:
+                write_GIN_config(mode='a', ginDomain = ginDomain)
+        else:
+        # すでにGINを信頼する設定があれば何もしない
+            pass
+    else:
+        # 設定ファイルが無い場合は新規作成して設定を書きこむ
+        with open('/home/jovyan/.ssh/config', mode='w') as f:
+            write_GIN_config(mode='w', ginDomain = ginDomain)
+
+def write_GIN_config(mode, ginDomain):
+    path = fetch_ssh_config_path()
+    with open(path, mode) as f:
+        f.write('\nhost ' + ginDomain +'\n')
+        f.write('\tStrictHostKeyChecking no\n')
+        f.write('\tUserKnownHostsFile=/dev/null\n')
+
+def fetch_files(dir_path):
+    """引数に与えたディレクトリパス以下にあるファイルのリストを作成して返す"""
+    data_list = []
+    files = glob.glob(dir_path + "/*")
+    for f in files:
+        data_list += [f]
+    return data_list

File diff suppressed because it is too large
+ 190 - 0
WORKFLOW/PACKAGE/base/experiment.ipynb


+ 0 - 0
WORKFLOW/PACKAGE/base/meta_data.json


+ 0 - 0
WORKFLOW/PACKAGE/scheme/RCOS_only_data/input_data/.gitkeep


+ 1 - 0
WORKFLOW/PACKAGE/scheme/RCOS_only_data/output_data/.gitkeep

@@ -0,0 +1 @@
+

+ 0 - 0
WORKFLOW/PACKAGE/scheme/RCOS_with_code/input_data/.gitkeep


+ 1 - 0
WORKFLOW/PACKAGE/scheme/RCOS_with_code/output_data/.gitkeep

@@ -0,0 +1 @@
+

+ 32 - 0
WORKFLOW/PACKAGE/scheme/RCOS_with_code/source/main.ipynb

@@ -0,0 +1,32 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 0 - 0
WORKFLOW/PACKAGE/scheme/YODA/CHANGELOG.md


+ 0 - 0
WORKFLOW/PACKAGE/scheme/YODA/HOWTO.md


+ 0 - 0
WORKFLOW/PACKAGE/scheme/YODA/README.md


+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/ci/.gitkeep

@@ -0,0 +1 @@
+

+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/code/.gitkeep

@@ -0,0 +1 @@
+

+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/code/tests/.gitkeep

@@ -0,0 +1 @@
+

+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/docs/build/.gitkeep

@@ -0,0 +1 @@
+

+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/docs/source/.gitkeep

@@ -0,0 +1 @@
+

+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/envs/.gitkeep

@@ -0,0 +1 @@
+

+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/inputs/data/.gitkeep

@@ -0,0 +1 @@
+

+ 1 - 0
WORKFLOW/PACKAGE/scheme/YODA/outputs/.gitkeep

@@ -0,0 +1 @@
+

+ 3 - 0
WORKFLOW/README.md

@@ -0,0 +1,3 @@
+# データガバナンス機能 ワークフロー
+
+[WORKFLOW/FLOWS/FLOW.ipynb](WORKFLOW/FLOWS/FLOW.ipynb)

BIN
WORKFLOW/images/maDMP実行ボタン.png


+ 80 - 0
WORKFLOW/images/notebooks.diag

@@ -0,0 +1,80 @@
+   
+   blockdiag {
+    node_width = 230;
+    node_height = 145;
+    orientation = portrait;
+    span_width = 40
+
+        group {
+            shape = line;
+            style = none;
+
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#ffeed9";
+                    "0. prepare_for_workflow"[label = "0. ワークフロー機能の実行準備", fontsize = 14];
+                    "base_required_every_time"[fontsize = 14];
+                }
+            }
+
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#fcdcb1";
+                    "1. preparation_phase"[label = "1. 研究準備フェーズ", fontsize = 14];
+                    "base_setup_data_analysis_tools";
+                    "base_work_with_GakuNinRDM";
+                }
+            }
+
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#ffd6a1";
+                    "2. experimental_phase"[label = "2. 実験フェーズ", fontsize = 14];
+                    "base_launch_an_experiment";
+                    "dummy"[shape=none, width=1, height=1];
+                    "base_monitor_data_size";
+                    "base_monitor_dataset_structure";
+                    "base_monitor_reproducibility";
+                }
+            }
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#ffb95c";
+                    "3. after_experiments_phase"[label = "3 実験終了後フェーズ", fontsize = 14];
+                    "base_publish"[fontsize = 14];
+                }
+            }
+            group {
+                shape = line;
+                style = none;
+                orientation = portrait;
+                group {
+                    orientation = portrait;
+                    color = "#ffa329";
+                    "4. after_research"[label = "4. 研究終了後", fontsize = 14];
+                    "base_finish_research";
+                }
+            }
+        }
+
+        "0. prepare_for_workflow" -> "1. preparation_phase" -> "2. experimental_phase" -> "3. after_experiments_phase" -> "4. after_research";
+        "base_launch_an_experiment" -> "base_launch_an_experiment";
+        "base_launch_an_experiment" -> "dummy"[dir=none];
+        "dummy" -> "base_monitor_data_size", "base_monitor_dataset_structure", "base_monitor_reproducibility";
+    }

BIN
WORKFLOW/images/result_on_README.png


BIN
WORKFLOW/images/sample_workflow.png


BIN
WORKFLOW/images/unfreeze_button.png


+ 1 - 0
experiments/pipeline.json

@@ -0,0 +1 @@
+[]

+ 77 - 11
maDMP.ipynb

@@ -15,8 +15,13 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    },
     "scrolled": true
    },
    "outputs": [],
@@ -29,11 +34,30 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 2,
    "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    },
     "scrolled": true
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Cloning into '/home/jovyan/WORKFLOW'...\n",
+      "remote: Enumerating objects: 3964, done.\u001b[K\n",
+      "remote: Counting objects: 100% (445/445), done.\u001b[K\n",
+      "remote: Compressing objects: 100% (211/211), done.\u001b[K\n",
+      "remote: Total 3964 (delta 238), reused 421 (delta 233), pack-reused 3519\u001b[K\n",
+      "Receiving objects: 100% (3964/3964), 51.95 MiB | 9.39 MiB/s, done.\n",
+      "Resolving deltas: 100% (1813/1813), done.\n"
+     ]
+    }
+   ],
    "source": [
     "# ワークフローテンプレート取得\n",
     "clone_url = 'https://github.com/NII-DG/workflow-template'+'.git'\n",
@@ -44,11 +68,24 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 3,
    "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    },
     "scrolled": true
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "最適化処理が完了しました。次の手順へお進みください。\n"
+     ]
+    }
+   ],
    "source": [
     "# dmp.jsonに\"fields\"プロパティがある想定\n",
     "import os\n",
@@ -71,8 +108,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
+   "execution_count": 4,
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
    "outputs": [],
    "source": [
     "# モニタリング準備\n",
@@ -107,9 +150,25 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 5,
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "run_through_control": {
+     "frozen": true
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "ワークフローの生成が完了しました。以下のURLをクリックすることで\n",
+      "ワークフロー全体図を閲覧できるページに遷移します。\n",
+      "https://jupyter.cs.rcos.nii.ac.jp//user/ivis_rino_mizuguchi@rdm.nii.ac.jp/ivis-mizuguchi-0606-test-rx9y76gn/notebooks/WORKFLOW/FLOW/base_FLOW.ipynb\n"
+     ]
+    }
+   ],
    "source": [
     "import os\n",
     "import nbformat\n",
@@ -165,6 +224,13 @@
     "print('ワークフロー全体図を閲覧できるページに遷移します。')\n",
     "print(rcos_binder_url + os.environ['JUPYTERHUB_SERVICE_PREFIX'] + flow_path_notebook)"
    ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
   }
  ],
  "metadata": {
@@ -183,7 +249,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.12"
+   "version": "3.9.10"
   }
  },
  "nbformat": 4,