これを書いているのは2023年11月。そろそろ高校で教えている情報Ⅰでは、プログラミングの実習を行う時期です。

僕が高校の授業で扱っている言語はPythonです。大学入学共通テストに出題される擬似言語と見た目が似ているのと、CやJavaと比べて記述が楽だなと思ったのが採用している理由です。

あとはできれば、3学期に行う統計にも役立ててみたいなという目論見もあります。

Pythonの学習や、課題の進め方、課題の提出方法などは、Scrapboxを使ってすでにまとめてあり、生徒は自宅からもスマホからも見られるようになっています。

2023年度で言語をPythonに切り替えて3年目になりますが、これまで生徒が提出した課題のコードの扱いに困っていました。

ロイロノートでPythonのコードを提出させた様子

これが以前までやっていた、課題提出方式です。ロイロノート・スクールを利用して、ソースコードを貼り付けてもらう形で提出させていました。

ロイロノートの提出機能を使うことで、そのクラスの生徒がどんな内容で提出してきてくれたのかを、クラス単位でまとめて表示することができます。

これは授業中に、みんなが出してきてくれた内容を随時確認しながら進めていくときに大変便利です。

しかし、僕が用意したPythonの課題は全部で18個あり、しかも6クラスあるので、僕はこの提出した課題一覧を生徒数x6クラスx18個みることになります。もう成績処理の時期は死にそうでした。

それに、授業についていけない生徒たちは、クラスのLINEなどを使ってコードを共有していたようです。それを貼り付けてくるもんだから余計にタチが悪い。

Pythonという言語はコピペと少し相性が悪くて、余計な空白やインデントを含むと途端にエラーを吐きます。だから僕もロイロノートでforやif付近が全部左に揃っているコードについては再提出という扱いにしていました。

ロイロノートでコードを提出させるとなると、そのコードで実際にどう動くのかを目視で確認しなければならないのが面倒なところです。

なんとか楽をしてPythonのコードを採点したい。そう思って、2023年度からは別の仕組みを用意しようと思いました。

ラズパイ4でPythonを自動採点

今回用意したのはRaspberry Pi 4です。メモリの容量は8GBにしました。アクセスの規模がそこまで大きくなく、授業は1クラスずつ行うので、きっと4GB版でも動作すると思います。

このラズパイに、LMSの代表格であるmoodleをインストールして、その中の小テストとして問題を出題する際に、CodeRunnerを使うことで、Pythonのコードを実行させて動作確認を行います。

Dockerのインストール

まずはラズパイ4にDockerをインストールしておきます。

$sudo apt install docker.io
$sudo usermod -aG docker $USER
$sudo reboot

これでDockerが利用できる環境が整いました。

moodleのコンテナを用意する

Docker上でmoodle環境を整えます。

DockerHubにあるbitnami/moodleのイメージを使います。

docker pull bitnami/moodle

そして、僕がGitHubで公開しているリポジトリから、docker-compose.ymlとDockerfileをCloneしてきます。ついでにdocker-compose upもしときます。

git clone https://github.com/jun3010me/docker-moodle-for-RaspberryPi4
docker-compose up -d

はい。これでラズパイ4上のDockerでmoodleが立ち上がりました。管理ユーザーは username:user password:bitnami です。

ちなみにこの2つのファイルはそれぞれ別のサイトで公開されてきたものを利用させていただいています。

詳しくはGitHubのREADMEに書きました。

jobe serverを用意する

moodleで小テストを出題する際に、プログラミングのコードで回答する問題タイプを利用するためには、CodeRunnerプラグインをインストールする必要があります。

このプラグインの導入そのものは簡単です。公式サイトに公開されているCodeRunnerのプラグインをダウンロードして、moodleで追加するだけ。

準備が必要なのは、CodeRunnerでコードを回答させる時の、動作チェック機能の方です。moodleやCodeRunnerプラグインそのものにはPythonのコードを実行する機能が実装されていません。

ここで必要なのが、moodleのページ上に入力されたコードを実行する専用サーバです。名前は「jobe server」と言います。「jobe」とは「Job Engine」の略です。

これもDocker上で動作するようにコンテナを用意します。arm64用のイメージが公開されていなかったので、ビルドするところからやります。

git clone https://github.com/trampgeek/jobeinabox
cd jobeinabox/
sudo docker build . -t my/jobeinabox --build-arg JP="Asia/Tokyo"
sudo docker run -d -p 4000:80 --name jobe my/jobeinabox

試しにjobe serverのコンテナに入って、動作確認をしてみます。

$ docker exec -it jobe /var/www/html/jobe/testsubmit.py --perf
Measuring performance in c
1 parallel submits: OK. 1 jobs/sec
2 parallel submits: OK. 4 jobs/sec
4 parallel submits: OK. 7 jobs/sec
8 parallel submits: OK. 7 jobs/sec
16 parallel submits: OK. 6 jobs/sec
32 parallel submits: OK. 7 jobs/sec
64 parallel submits: OK. 7 jobs/sec
128 parallel submits: FAIL.
Maximum burst handled with no errors = 64 jobs
Checking maximum sustained throughput over 30 sec window
Testing with rate of 2 jobs/sec: OK
Testing with rate of 3 jobs/sec: OK
Testing with rate of 4 jobs/sec: OK
Testing with rate of 5 jobs/sec: OK
Testing with rate of 6 jobs/sec: OK
Testing with rate of 7 jobs/sec: OK
Testing with rate of 8 jobs/sec: OK
Testing with rate of 9 jobs/sec: OK
Testing with rate of 10 jobs/sec: Failed
Sustained throughout rate: 9 jobs/sec

このコマンドは動作確認兼ベンチマークのようなもので、僕の環境では64ジョブで止まりました。まぁラズパイ4の性能では十分でしょう。

これでラズパイ4上にDockerでmoodle+CodeRunnerと、動作確認用のjobe serverが用意できました。

moodleとjobe serverのコンテナが起動したら、moodleでjobe serverを利用する設定を行います。

jobe serverのアドレスの指定場所

サイト管理→プラグイン→問題タイプ→CodeRunnerを開くと、jobe serverのアドレスを入力する欄があります。

Docker上で4000番ポートで起動しているので、アドレスとポート番号を指定しておきます。僕の環境ではmoodleに設定したドメイン名と同一にしてあります。

4000番ポートの許可

そしてもう1つ。サイト管理→一般→セキュリティ→HTTPセキュリティの「cURL許可済みポート一覧」に、jobe serverとのやりとりに利用するポート番号4000を追記します。

この設定をやらないと、moodleとjobe serverがいくら同じラズパイで動いているとしてもやりとりができません。Dockerでネットワークを作ってもうまくいきませんでした。ここの設定に辿り着くのにかなり時間がかかりました。

以上で環境の構築が終わりました。あとは適宜、生徒のユーザーを追加したり、コースを設定することで、学習の用意が整うと思います。

出題方法のコツ

全てを手順にするとかなりの分量になってしまうので、CodeRunnerを使ってPythonの課題を出題する際の要点だけまとめます。

例えば、西暦の4桁の数値を入力し、その年が閏年かどうかを判定するプログラムを出題したとします。

僕が書いたコードはこんな感じ。西暦が4で割り切れるなら閏年、でも100で割り切れたら閏年ではない、例外として400で割り切れたのなら閏年。

seireki = int(input())
if seireki % 4 == 0:
	uruu = 1
	if seireki % 100 == 0:
		uruu = 0
		if seireki % 400 == 0:
			uruu = 1
else:
	uruu = 0

if uruu == 1:
	print("Uruu doshi!")
else:
	print("Not Uruu doshi!")

CodeRunnerでは、まず言語を指定します。

CodeRunnerの設定場所

それと、コードを貼り付ける場所の行数も指定できます。ここで言う行数は、あくまで表示上の行数であって、実際にはそれ以上の行数のコードも入力が可能です。ですが視覚的効果もあって、「理想はn行以内で書くことだよ」とアドバイスするときに役立つと思います。

問題文を入力する場所

続いて、出題する際のテキストを入力しいます。解答欄の上に表示される部分の文章をここで入力します。

模範解答を入力する場所

次のAnswerで、模範解答となるコードを入力しておきます。これと全く同じコードでなくても正解にできますが、動作確認用として登録します。

Test Caseを設定する場所

ここが重要、Test Caseです。ここのInputとOutputで、コードを実行した際にjobe server上でプログラムを動かした際にinput関数に渡すものや、printで出力される内容を指定します。

画像の例で言うと、2024と入力した際に、最後にprint関数で出力される判定結果を入れておくわけです。閏年と判定される例ですね。

ここでのチェック項目に合わせて、問題の文章などにどんな文字列を出力させるのかを書いておくわけです。

特にInputさせる内容がなくて、実行結果だけを出力させるだけのプログラムなら、Outputの欄だけの入力で設定が終わります。

このTest Caseにどんなパターンを入れるのか。これが出題者が工夫するポイントだと思います。どんな出力結果が出たら動作OKとするのか。個人的にはなかなか楽しい作業でした。

動作している様子

動作チェックの様子

実際に動作しているスクショがこんな感じです。敢えて間違えたコードを貼り付けてみました。西暦の数値が400で割れたら閏年という部分を削除したら、他の数値では正しい判定が出ているのに、1箇所だけ通らない様子が確認できました。

正しい出力例と、現状の出力例の比較ができるので、指導の参考にもなります。

自前で用意できるAtCoder

ラズパイ4があれば、情報ⅠのPythonのコードの採点が自動化できる。個人的にはかなり画期的だと思います。

情報Ⅰの教材サービスとして、多くの業者が自動で採点できますよとか、効率よく学習できますよと営業されてきます。

でもそれらは当然有料サービスですし、生徒からお金を徴収しなければなりません。言わば問題集と同じです。

ですが今回の仕組みを使えば、ラズパイさえ用意すればAtCoderのような仕組みを自分で用意できるわけです。

個人情報などの管理が心配なら、学内のコンピュータ室のLANにぶら下げるのも手だと思います。

ちなみにラズパイをmoodle専用機にするのなら、moodleboxというイメージも有名です。

これはラズパイ4本体をWIFIルータとして機能させて、生徒用の端末からWIFI接続し、moodleにアクセスさせるという仕組みをイメージ化したものです。

Raspberry Pi Imagerにも登録されているので、専用SDさえ用意すれば気軽に利用できます。

ローカル環境での学習となりますが、先生が持っているラズパイの周りに生徒が集まって、課題を提出する形になるので、逆に学校ではアリなのかなと思います。宿題を面と向かって提出するイメージで操作できますから。

ともあれこれで、Pythonの課題を生徒に課して、提出されたコードが動くかどうかもチェックするまでを完全に自動化することに成功しました。

元のLMSはmoodleを利用しているので、提出状況や進捗などは教員アカウントから確認できますし、結果をCSVで吐き出すこともできるので、成績処理が楽ちんです。

書いたコードが動くか動かないのか、それを確かめる仕事は人間がやることじゃないな。コンピュータに任せられるのなら、どんどん自動化して、空いた時間は対面の生徒指導に回したいと思います。

参考になりましたら幸いです。