プログラミング演習を実施できるウェブシステム

# 2021/06/25 追記

[新しいバージョンに関する説明記事はこちら](https://qiita.com/tatkeshi/items/0d6879ee6b492e4dfa93)

# 2021/05/24 追記

同等以上の機能を有する新しいバージョンのLMSを作成しました([https://github.com/kjmtks/hugin-lms](https://github.com/kjmtks/hugin-lms))．
時間ができたら紹介記事を書きたいと思います．

なお，本記事で紹介するLMSのアップデートは以後行いません．

# はじめに

はじめて Qiita に投稿します．普段，私は非情報系の学科でプログラミングに関連する内容の演習や講義を行っています．数十人のプログラミング（というよりコンピュータ）の初心者を対象に，統計学や制御工学の文脈で，R や Octave，Pythonなどを教えているのですが，教員ひとりでこれをハンドリングするのは非常に大変です．そこで，数年前にウェブでプログラミング課題を実施できる教育管理システムを開発し，授業で使用しています．このシステムを導入してから，プログラミングの教育にかかる些末な手間から解放され，本来伝えたい話に集中できていて，手放せないツールになっています．

ところで，大学は COVID-19 対策に追われています．私も教育業務だけに限っても，授業の内容をすべて見直す必要があり，本当に大変です．ただ，幸いにもプログラミング系の授業は開発していたシステムがあるので，Zoomも併用すればなんとかなりそうです．

Zoomもそうですが，いま，色々な人がこの難局を乗り越えるために動いて下さっていて，事実，大変助かっています．私も誰かの役に立てられれれば幸いと思い，元々ひとりでひっそりと使っていくつもりだったこのシステムを公開させていただきます．

[https://github.com/kjmtks/LMS7](https://github.com/kjmtks/LMS7)

数年間の運用経験に基づいて仕様を練り直し，最近，短期集中的に再開発したものです．
主たる機能を挙げておきます:

* chrootで切り離されたサンドボックス環境を任意に構築することができ，その環境内でプログラムを実行する演習課題を作成することができる
* プログラム課題の合否をテストケース方式で自動的に判定することができる
* Gitをインタフェースとして，教育コンテンツやユーザーのデータをアップロード・ダウンロードすることができる

ひとまず動かしてみるには，Docker および docker-compose をインストール後，

```
$ git clonet https://github.com/kjmtks/LMS7.git
$ cd LMS7
$ make development-up
```

を実行後に `http://localhost:8080` へアクセスするだけです．ただし，独特の仕様となっておりますので，どう使えば良いのか全くわからないと思います．

本記事では，デモ用コンテンツを動作させる手順について解説します．

# 入手方法

本システムは Docker および docker-compose を用いることを推奨しており，以降もこれらを前提として解説しています．また，Windows 10 pro, macOS, Ubuntuでの動作を確認しています．

ソースはGitHubの[リポジトリ](https://github.com/kjmtks/LMS7)から最新版を入手してもいいのですが，雑にGitHubを運用しているため，[Release](https://github.com/kjmtks/LMS7/releases)から入手することを推奨します:

```
$ wget https://github.com/kjmtks/LMS7/archive/ver.1.0.zip
$ unzip ver.1.0.zip
$ cd LMS7-ver.1.0
```

# 起動方法

プロジェクトルートには `Makefile` と 複数の `docker-compose.*`がありますが， `make` コマンドを介して `docker-compose` を実行するように準備しています．
ローカル環境で動作させるだけであればなんらファイルをカスタマイズする必要はなく，そのまま

```
$ make development-up
```

を実行すればOKです．初回実行時にはイメージを生成するために時間がかかります．
しばらくすると以下のような出力が得られます:

```
...
app_1  | info: Microsoft.Hosting.Lifetime[0]
app_1  |       Now listening on: http://[::]:8080
app_1  | info: Microsoft.Hosting.Lifetime[0]
app_1  |       Application started. Press Ctrl+C to shut down.
app_1  | info: Microsoft.Hosting.Lifetime[0]
app_1  |       Hosting environment: Development
app_1  | info: Microsoft.Hosting.Lifetime[0]
app_1  |       Content root path: /app/ALMS.App/out
```

その後， `http://localhost:8080` にアクセスすると，以下の画像のようなページが表示されます.

![ss01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/ebc9621c-d334-a5ec-3d48-00e4d69d89f5.png)

# デモ用コンテンツの準備

## ログイン

上部メニューの右端にある **Login** をクリックし，ログイン画面に移動してください．

![ss02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/18d0901d-6cf2-09d9-70a9-dabb8fbb1da7.png)

初期ユーザーとして，管理者権限を持つ `admin` と，通常権限の `test001`, `test002`, ..., `test010` が登録されていますが，まずは管理者権限のあるユーザーで設定を行う必要があるので，`admin`でログインします．パスワードも`admin`です．

![ss03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/833c723b-e3d5-c726-fad8-a644bfff71bb.png)

ログインに成功すると，ホーム画面に自動的に遷移します．上部メニューに項目が増えているはずです．`Administration` は管理ページへのリンクで， `admin` は自分のページへのリンクです．`admin` をクリックしてください．

![ss04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/6252ecdc-6297-e1d0-30cb-abe5966aa8a0.png)

左側に自分の情報が表示されています． タグ型のラベルはそれぞれユーザーのもつ権限を表しています．
`Senior` 権限をもつユーザーは，講義や，サンドボックス環境を新たに作成することができます．システムのセキュリティ上重要な部分に関わるユーザーですので，管理者が信用できるユーザーのみに付与してください．
`Admin` 権限は `Senior` と同等の権限に加え，ユーザーの登録・編集・削除を行うことができるほか，システム全体の設定を行うことができます．

## サンドボックスの作成

まず，`Sandbox Settings` でプログラムを安全に実行するためのサンドボックス環境を作成します．
`+` のアイコンをクリックし，表示されたダイアログに必要な情報を入力します．

![ss05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/b7d0ae2a-7d81-ca18-ed8a-2873a614fe43.png)

ここでサンドボックス環境に講義で使用する処理系等をインストールするコマンドを記述するのですが，それを何もない状態から書くのは面倒です．
本システムでは Ruby と Python のインストールコマンドのテンプレートを用意しています（このテンプレートは管理ページで任意に作成することができます）．
デモ用コンテンツを動作させるには， Ruby の環境が必要ですので， `Use package?` より， `Ruby 2.7.0` を選択してください．`Name`, `Description`, `Setup Commands` 欄が自動的に埋められます．

![ss06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/c9c5a7ff-0123-21bc-bf6b-4e6a322cdfd0.png)

問題が無ければ，`OK` をクリックしてください．

![ss07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/7d773cfb-a077-8a74-508b-269311d5054f.png)

環境とRubyのインストールが始まり，状況がリアルタイムで表示されます．この処理には非常に時間がかかります．
Spinner が回転している間は処理中です．このページから移動したりブラウザを閉じずに，処理が終了するまで待機していてください．

![ss08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/e63da18a-a05f-cb8c-817c-82a475f99e94.png)


上の画像のような画面になればサンドボックスの構築は完了です．
`Command...` のところに任意のコマンドを入力して Enterキーを押下することで，実際にその環境でコマンドを走らせることができます．このとき，コマンドは root ユーザーで実行されますので，追加でセットアップしたいものがあればここで行うことができます．

## 講義の作成



次に，`Lecture Settings`[^1]で講義を作成します．`Lecture Settings` の `+` アイコンをクリックし，表示されたダイアログにて以下のように入力してください．

[^1]: 誤字．．．

![ss09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/343a882f-f5b4-c887-3492-d429eabf68d1.png)

`Name` には半角英数で講義の識別名を入力します． `Subject` には講義名を自由に入力します． `Description` にはメモを任意で書いておくことが出来ます．
`Teachers` および `Students` には，この講義に割り当てる教員および学生のアカウント名をスペースまたは改行区切りで記述します．いまはひとまず `Teachers` に `admin` が含まれているだけで充分です．
`Empty Repositories` のチェックを忘れずにつけておいてください．

以上の手続きで，画面が以下のようになっているはずです:

![ss10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/203f345c-afea-4806-8e8a-e1866941b005.png)


## コンテンツのアップロード

`Lecture Settings` の `demo` のリンクをクリックし， デモ講義の管理ページへ移動してください．

![ss11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/98e0f3d0-4f94-58de-d1d4-492e3ab45cd5.png)

本システムでは，講義コンテンツをGitを介してアップロードすることになっています．いまは何のコンテンツもないため， `Contents Repository` に Git のコマンド群が表示されています．後で使用しますので，このコマンド群をコピーしておきます．

ブラウザの操作は一度置いておき，シェルでの操作を行います．

```
$ cp -r samples/demo ~/demo
$ cd ~/demo
# 以下にコピーしておいたGitのコマンド群をはりつける
$ git init .
$ git remote add origin http://localhost:8080/api/git/lecture/admin/demo.contents.git
$ touch .keep
$ git add .
$ git commit -m "Initial Commit"
$ git push origin master
```

Basic認証では，アカウント，パスワードともに `admin` としてください[^2]．
以上の操作で，本システムにデモ用コンテンツが push (アップロード)されます:

[^2]: 本システムの認証情報を使用します．

```
...
Counting objects: 100% (13/13), done.
Delta compression using up to 4 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (13/13), 3.59 KiB | 3.59 MiB/s, done.
Total 13 (delta 0), reused 0 (delta 0)
To http://localhost:8080/api/git/lecture/admin/demo.contents.git
 * [new branch]      master -> master
```

ブラウザの画面を更新してください．コンテンツが確認できるようになっています．
また， `Parameters` にも複数のデータが登録されています．これは，本講義のコンテンツで利用可能なグローバル定数のようなものです．`~/demo/parameters.xml` を編集して再度 push することで内容を変更できます．

![ss12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/57b3dbda-ce27-ce1d-5243-d0d2e046eb62.png)

## 講義サンドボックスの作成

最後に，少し下にスクロールした先にある，`Sandbox Settings` より，講義にサンドボックスを登録します．

![ss13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/f21149f6-a92e-92ea-a69b-cadc6f53e509.png)

`+` アイコンをクリックし， `Name` に `ruby` と入力し， `Clone From` で `ruby` を選択し， `OK` をクリックしてください．

![ss14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/b7fd194d-ffa1-2e87-a16d-b9cd5bf758b1.png)

しばらく待つと，以下の画面のように `ruby` が追加されます．

![ss15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/43b14274-227c-b849-36c2-4c6a943106a6.png)

この操作は，事前に作成しておいた Ruby のサンドボックスをこの講義用に複製する手続きです．事前に作成するサンドボックスと区別するため，講義サンドボックスと呼んでいます．講義サンドボックスではこの講義に割り当てられた教員・学生が，実際のユーザーとして登録されています．具体的には， `/etc/passwd`, `/etc/group` にユーザーの情報が書き加えられているとともに，各ユーザーのホームがサンドボックス外にあるユーザーのためのディレクトリにマウントされています．その他は複製時の元のサンドボックスと完全に同一の状態です．

なぜサンドボックスと講義サンドボックスに分けて管理しているのかというと，複数の講義を管理する際に便利だからです．また，今後，同様にサンドボックスを用いる講義とは異なる機能を実装する予定で，それを見越した仕様になっています．

以上で準備は完了です．

# デモ用コンテンツの確認

![ss16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/94cd1b8d-0f30-80c4-c48a-35b0c7b5b92c.png)

ページ上部の `master` をクリックすることで，コンテンツページに移動することができます[^3]．

[^3]: 実はブランチに対応しています．学生はmasterブランチに基づくコンテンツのみを閲覧でき，一方，教員は任意のブランチのものを閲覧可能です．

![ss17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27705/b219af19-7bdd-5ed1-f788-7750783de217.png)

本システムの講義で扱う課題のことをアクティビティと呼びます．
アクティビティには，プログラムのコードの記述，単一・複数行のテキスト入力，ファイルのアップロード，フォームをサポートしており，デモ用コンテンツとしてそれぞれのサンプルを用意しています．また，これらのアクティビティは組み合わせることもでき，例えば，ファイルのアップロードとフォームへの入力の両方が必要なアクティビティなども作成可能です．

各アクティビティでは，Save, Run, Submit, Validate, Reset の各操作を行うことができます．それぞれの詳細は次の通りです:

* Save は現在のアクティビティへの入力状態をユーザーの個人ディレクトリに保存します．
* Run は，Save を行った上で，アクティビティに定義されたコマンドをサンドボックス環境で実行し，その結果を表示します．典型的にはユーザーの記述したプログラムを実行する目的で使用しますが，例えばフォームの内容を自動でチェックして合否を表示する，といった使い方もできます．
* Submit は，Save を行った上で，提出用ディレクトリに保存します．この操作をもって提出がされたと見做します．Submitを行う条件として，以下を任意に設定可能です．
    * 提出期限の間のみ提出可能
    * 次に述べる Validate で Accept となった場合にのみ提出可能
* Validate は，Save を行った上で，アクティビティに定義されたテストケースによる自動検証を行い，その合否(Accept/Reject)を出力します[^4]．
* Reset は，アクティビティを初期状態にリセットします．

[^4]: テストケースの合否を命題論理変数とした，命題論理式としてテストケースを記述することができます．

また，教員限定で，アクティビティに定義された解答をセットする Answer も利用可能です．

Save, Run, Submit, Validate については，実行の度にその記録がデータベースに残ります．
また，ユーザーのデータもGitで管理されており，いつどのような変更を行ったのかすべて調べることができます．

# 終了と削除

docker-compose が動いているだけなので，`Ctrl+C` で終了できます．

システムで生成したデータ（サンドボックスやデータベースの内容）を消去するには，

```
$ make developemnt-remove
```

を実行して下さい．

# おわりに

奇妙に思える仕様があると思いますが，元々，自分が使いやすければ良い，という考えで仕様を練っておりましたのでご容赦ください．

近日中にコンテンツの作り方やその他の機能について投稿する予定です．