OpenProcessing+Google Apps Script

こちらは[Processing Advent Calendar 2023](https://adventar.org/calendars/8532) 12/5の記事になります。

# ProcessingとGoogle Apps Scriptの連携
Google Apps Scriptを使ってOpenProcessingの作品を一覧表示できるシステムを作ったというお話になります。

# 開発の背景
長岡造形大学のデザイン学科では1年生の選択授業で「プログラミングI」という授業があります。プログラミング初学者のための授業という位置付けです。さまざまなデザイン分野でコードを書く知識は必要になります。高校までの教育でプログラミングが取り入れらて注目度が高く選択の授業ですが100名を超す学生が履修してました。学生みんなに興味を持ってもらい履修者が多いのは嬉しいのですが、提出作品の管理、学生へのフィードバックをどのようにするかという問題が出てきました。
そこでGoogle Apps Scriptを使い学生の提出作品閲覧システムを作成しました。
# 前提
言語・環境としてProcessing（p5.js)を使いました。デスクトップ版のProcessingを使っても良いのですが、ダウンロード、インストールの手間を考え、OpenProcessingを使うこととしました。macOS、Windowsどちらでも使えブラウザもSafari,Chromeで問題なく使えます。また学生は授業支援システムとしてGoogleクラスルームなどのGoogleのシステムは使い慣れているので、他のサービスを使わずにそれらを活用することにしました。
# 授業の進め方
授業時間は90分ですが30分程度を提出作品へのフィードバックと前回の振り返り。その後の30分でその回の解説レクチャー。残りの30分を各自の制作時間と質問受付時間としていました。今回紹介するのは提出作品の表示に関するシステムとなります。
# 作品提出の流れ
授業では毎回、お題を出してそのお題に沿った作品を制作して提出してもらいます。各学生はOpenProcessingのアカウントを作成してお題に沿った作品を作成します。お題いは「顔」とか「マウスに反応して目が動く顔」とかそんな感じです。Embedのデフォルトサイズが400x400pxなので画面サイズもそれに合わせて
### コード作成
まず学生はOpenProcessingのサイトでコードを書きます。
![スクリーンショット 2023-12-03 7.40.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225580/5a5bc008-67f5-3357-4aee-b0e1bc191cf9.png)
そして普通に保存します。
### Embedのタグをコピー
ShareのEMBEDタグをコピーします。
![スクリーンショット 2023-12-03 7.40.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225580/a6a293da-88c3-93c9-97b7-a7dffc88cab1.png)
### Googleフォームで提出
学生はフォームを開き学籍番号、名前、Embedのリンクをペーストします。
![スクリーンショット 2023-12-03 7.45.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225580/8d577e50-6a27-d89d-3141-506f327d071c.png)
コメントを記入できるようにしました。ここに学生が制作意図や工夫した点などを記入してもらってました。このコメント欄が授業を運営する上で役に立ちました。理解しにくい点を書いてきた学生には次回の授業で補足の説明をするといったフィードバックも行えました。また「本当はこうしたかったけど、できなかった」と書いてきた作品に対してはコードをForkして改良して「こうすればできる」と実演してみせたりといったやり方を行なっていました。
OpenProcessingで特定のタグをつけて学生の作品をまとめて表示といったこともできますが、閉じた環境で提出作品を共有する仕組みを取り入れたことにより学生が気軽にコメントを記入し、それを拾い上げて授業に反映させるといったことがしやすくなりました。

# Google Apps Script
上記のフォームで提出されたデータはGoogleスプレッドシートに入力されます。スプレッドシートを読み込みwebページで表示する仕組みをGoogle Apps Scripで作成しました。Google Apps Scriptの詳細については別のドキュメントなどを参照してください。大雑把に話をすると以下のような画面で開発を行います。
![スクリーンショット 2023-12-03 10.15.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225580/7b57b15d-e067-df76-5217-e3ff7caf1f4a.png)
「コード.gs」がメインの処理。webブラウザの表示は「index.html」が担当します。index.htmlの中のJavaScriptからコード.gsの処理を呼び出して処理するような流れになります。
コード.gsとindex.htmlの中身は以下のようになります。一部、不要なコードも入ってますが、とりあえずこんな感じで...

コード.gsの中身
``` JavaScript
function doGet(){
  //return HtmlService.createTemplateFromFile("index").evaluate;
  //return HtmlService.createTemplateFromFile("index").evaluate();
  var output = HtmlService.createTemplateFromFile('index');
  //return output.evaluate();
  var html = output.evaluate();
  return html;
}
  //return "-";

//ユーザーの名前（loginしたメールアドレス)を取得して返す
function getmyName(e){
   var objUser = Session.getActiveUser();
   var name = objUser.getEmail();
  return name;
}


function getWorkListData(){
  var returnWorkList = [];
  // 指定したシートからデータを取得

  var ssId = '1SL2mmnwMih5M212Vb_9eMhAKgxiuq2i5Dks0av19w7E';// SSIDからスプレッドシートの取得
  var ss = SpreadsheetApp.openById(ssId);
  var sheet = ss.getSheetByName('フォームの回答 1');// 指定されたシート名からシートを取得して返却
 // var values = sheet.getDataRange().getValues();
  
  //console.log("ログ出力です！");
  //console.log(values);

  var workList = sheet.getDataRange().getValues();//シートの情報を全部読み込み
  //console.log(workList);
  for (var i = 1 ; i<workList.length ; i++){
      console.log(workList[i]);
      //学籍番号、氏名、embedLink、コメント
      returnWorkList[i]=[workList[i][2],workList[i][3],workList[i][4],workList[i][5]];
  }
  return returnWorkList;
}
  ```

index.htmlの中身
``` html
<!DOCTYPE html>
<html>
  <head>
    <title>プログラミングI 提出作品</title>
    <meta charset="UTF-8">
    <base target="_top">
     <style>
        p{
            color: black;
        }
        body{
            background-color:whitesmoke;
        }

        
        div.container{
          display: flex;
          background-color:white;
          padding:20px;
          margin:30px 10px;
        }

        div.opWork{
            width:420px;
            margin:5px;
        }
    
        
        div.name{
            margin:5px;
            padding:20px;
        }

        div.std_id{
            margin:5px;
            padding:20px;
        }

        div.comment{
            margin:5px;
            padding:20px;
        }

    </style>
  </head>


<body>
    <h1>プログラミング1</h1>
    
<p>2023/7/24</p>
<p>提出作品一覧</p>
<div id ="interface">
<button onclick="prevButton()">前の10作品</button>
<button onclick="nextButton()">次の10作品</button>
</div>

<div id = "myListBlockID"></div>

<div id ="interface_bottom">
<button onclick="prevButton()">前の10作品</button>
<button onclick="nextButton()">次の10作品</button>
</div>

</body>

 <script>
    var workCount;//表示を始める作品の番号、ここからカウントダウンしていく
    var workData;

    google.script.run.withSuccessHandler(drawWork).getWorkListData();

    function drawWork(workList){
      //alert('function drawWork');
      document.getElementById('interface').style.backgroundColor = 'skyblue';
      //console.log(workList);
     // workData = workList;
     workData = workList.slice();
      workCount=workList.length-1;
      workDraw();
    }

    function nextButton() {
      //alert('ボタンが押されました');
      document.getElementById('interface').style.backgroundColor = 'skyblue';
      workCount = workCount - 10;
      workDraw();
    }

    function prevButton() {
      //alert('ボタンが押されました');
      document.getElementById('interface').style.backgroundColor = 'skyblue';
      workCount = workCount + 10;
      if (workCount > workData.length) workCount = workData.length;
      workDraw();
    }


    //作品の描画、10個づつ表示する。
    function workDraw(){
      var max = workCount;
      var min = workCount - 10;
      if (min<0) min = 0;

      var outputHTML = "";
      for(var i=max; i>min; i--){
          console.log(i,workData[i]);
          //作品の表示

          outputHTML += '<div class="container">';

          outputHTML += '<div class="opWork">';
          outputHTML += workData[i][2];
          outputHTML += '</div>';

          outputHTML += '<div class="info">';

          outputHTML += '<div class="std_id">';
          outputHTML += workData[i][0];
          outputHTML += "</div>";

          outputHTML += '<div class="name">';
          outputHTML += workData[i][1];
          outputHTML += "</div>";

          outputHTML += '<div class="comment">';
          outputHTML += workData[i][3];
          outputHTML += "</div>";

          outputHTML += "</div>";//info
          outputHTML += "</div>";//container
        }//for loop
        console.log(outputHTML);
        document.getElementById('myListBlockID').innerHTML = outputHTML;
    }
  
</script>

</html>
```
スプレッドシートのセルを読み取りそれをwebページに表示していきます。コメントにタグが入るとそれもそのまま解釈して表示してしまうので、問題ありですが、その処理も組み込んだ方が良いでしょう。
授業開始時期には100名分の作品をズラーっと並べていたのですが、繰り返しや配列を使ったコードを使ったあたりから処理が重くなってまともに動かなくなってきたので10作品づつの表示に変更しました。また読み込みは最後の提出から表示するようにしています。そうすることで最初のページに表示される内容が提出ごとに変わるので、提出した感がありました。

このスクリプトを複製して毎週課題提出の受付を行い授業を行いました。
フォームと課題閲覧のためのURLはGoogleクラスルームで授業のたびに学生に告知してアクセスしてもらいました。コロナ禍で一気に活用が始まったGoogleクラスルームですが、すごい活用しています。

# 表示ページ
提出作品は一覧で表示されます。学籍番号、氏名、コメントが作品と同時に表示されます。作品サイズは400 x 400pxに固定していました。400pxにしたのはOpenProcessingのデフォルトのEmbedサイズがそのサイズだったためです。（embedコードの書き換えが必要ない）

# まとめ
というわけでProcessingの学習のためのGoogle Apps Scriptの活用事例の紹介でした。とってもニッチな内容でした！授業でワークショップでやってみたい！という方はコードをご自由にお使いください。その時にはこんな活用しました！とお聞かせいただけると嬉しいです。連絡先 X（Twitter) @mkbtm