サイトを移転し、「あるふネット」から「あるふネッツ」になりました!(2022年10月より)URLも変更しているのでブックマークされている方は更新をお願いします。なお、旧サイトからリダイレクトもしていますが、半年後を目安にリダイレクトをストップします。

振り返りフォームで学習を可視化する!これで新学習指導要領の評価も悩まない!【Google フォーム と Google スプレッドシート を GAS連携】

新学習指導要領の評価も悩まない!振り返りフォームで学習を可視化する!GAS連携!Google関連

Google フォーム と Google スプレッドシート を Google Apps Script(GAS)で連携して、授業の振り返りフォームを作成しました!これを使えば学習を可視化することができます!!

特に、「関心・意欲・態度」より変わった「学びに向かう力・人間性」の記録として非常に役立ちます。Google スプレッドシート を 振り返りフォーム として活用している学校もありますが、入力のしにくさ・ハードルを個人的に感じています

入力は Google フォーム、一覧表示は Google スプレッドシート とすることでよりスムーズに学習の記録を残していくことができます

2022年9月21日にバージョン2.0に更新し、複数授業対応、回答先のスプレッドシートと児童・生徒個別のスプレッドシートが連携されるようになりました(児童・生徒からの質問に回答・コメントしやすくなりました)。バージョン2.0では、児童・生徒がフォームに入力する前に、フォームの回答先のスプレッドシートを作成しておく必要があります。詳しくは後述の 16. 回答先のスプレッドシートの作成・選択 をご覧ください。

2022年9月24日に、児童・生徒の負担を減らすことを目的に「学んだことのアウトプット」を「振り返りの自由記述の入力」に統合した light版 を公開しました。light版 を活用したい方は、後述の 教員の使い方(セッティング) にて light版 を選択してください。

2022年10月26日に、バージョン2.1に更新し、複数の児童・生徒が同時に回答した際に、回答先スプレッドシートに追記する個別スプレッドシートへのリンクが行単位でずれる不具合を修正しました。また、ファイルを共有することができないメールアドレスを児童・生徒が入力した際に、振り返りフォームを設定したオーナー(教員)にメール通知が飛ぶ機能を追加しました。

できるようになること

  • Google フォーム で生徒自身が学びを振り返ることができる
  • Google フォーム で入力した振り返りを生徒毎の個別のスプレッドシートにためていくことができる
    • 「学びに向かう力・人間性」の記録ができるようになる
    • Google フォーム で入力したものをスプレッドシートで生徒自身が振り返ることができる
  • 生徒の振り返りを Google スプレッドシート を通して教員がコメントしたりチェックしたりすることができる

これらによって、「主体的に学習に取り組む態度」、「思考・判断・表現」、「知識・技能」の3観点での観点別評価における、「主体的に学習に取り組む態度」の評価の材料とすることができます!

「主体的に学習に取り組む態度」とは、“単に継続的な行動や積極的な発言等を行うなど、性格や行動面の傾向を評価するということではなく”、下記の二点の側面から評価する必要があると、文部科学省の『児童生徒の学習評価の在り方について(報告)』に記述されています。

① 知識及び技能を獲得したり、思考力、判断力、表現力等を身に付けたりすることに向けた粘り強い取組を行おうとする側面

② ①の粘り強い取組を行う中で、自らの学習を調整しようとする側面

文部科学省(2019)『児童生徒の学習評価の在り方について(報告)』

つまり、「粘り強く学習に取り組もうとしている」ことと「自らの学習を調整しようとしている」ことを評価できる仕組みが必要であり、それがこのツールを用いることである程度可能になります

GAS の仕様上、1ユーザーでの同時実行数が 30 までとなるため、30人前後のクラスで使用するようにしてください。1回の実行に10秒弱かかるため、よっぽどのことがない限りは30人少々のクラスでも問題なく動作します。40人あたりを超えてくる場合には、時間をずらして実行するようにしてください。また、他の先生の授業用に自分のアカウントで振り返りフォームを作成してトリガーを設定すると、制限に引っ掛かってしまう可能性が増えるので、「自分の授業の振り返りフォームのトリガーは自分のアカウントで設定する」ようにしてください(トリガーを設定したアカウント=実行するアカウント)。その他の制限については公式の下記を参照してください。
Google サービスの割り当て  |  Apps Script  |  Google Developers

生徒の使い方

1. 振り返りフォームを開く

先生が Google Classroom 等で配布した振り返りフォームのリンクをクリックして、振り返りフォームを開きます。

なお、この振り返りフォームは繰り返し使うことで振り返りを蓄積していくことができるので、生徒はブラウザでブックマークしておくことをオススメします。

2. メールアドレス(Google アカウント)を入力

学校で使用している Google アカウント であるメールアドレスを入力します。

生徒自身のメールアドレスは、Google にログインしている状態であれば画面上部に記載されているのでそちらをコピペしたり、ブラウザの入力補助機能を使用すれば入力の手間も省けるでしょう。

メールアドレスの入力

3. 学籍番号の入力

全ての生徒で被らない、唯一無二(ユニーク)な番号を入力します。この番号がファイル名にも使用されます。

学籍番号の入力

4. 授業名の選択

授業名を選択します。なお、授業名の選択肢は教員によって変更することが可能です。

授業名の選択

5. 授業内容の入力

振り返りを行う授業の内容を入力します。

授業内容の入力

6. 3観点での振り返り

「主体的に学習に取り組む態度」、「思考・判断・表現」、「知識・技能」の3観点で選択して自己評価を行います。すべての授業で3観点すべてを扱っているわけではないのが現実だと思いますので、扱っていない場合は「ー:評価対象外」を選択します。

なお、“各項目の文言”、“評価の段階”、“評価の段階の文言” は先生によって変更することが可能です。観点を増やしたり減らしたりすることは、スクリプト(プログラム)を変更する必要があるので、プログラミングができる方や挑戦したい方は後述する Google Apps Script(GAS)をいじってみてください。

3観点での自己評価

7. 振り返りの自由記述の入力

振り返りの自由記述を入力します。なお、文言(デフォルトでは「本日の授業における自分の取り組みで良かった点や今後の学習への改善点を挙げてみよう。」)は教員によって変更することが可能です。

本日の授業における自分の取り組みで良かった点や今後の学習への改善点を挙げてみよう。

8. 学んだことのアウトプット

学んだことをアウトプットします。アウトプットすることで、学んだことの定着が図れると思い、このような項目を用意しました。なお、文言や説明文は教員によって変更することが可能です。

2022年9月24日に、児童・生徒の負担を減らすことを目的に、本入力項目を削除して 7 の「振り返りの自由記述の入力」に統合した light版 を公開しました。light版 を活用したい方は後述の 教員の使い方(セッティング) にて light版 を選択してください。

本日の授業で得た学びや感じたことを表現しよう。本日学んだキーワードを2つ以上使用すること

9. 質問の入力(任意)

質問がある児童・生徒は、質問することができます。こちらの設問の入力は任意となっています。

質問の入力(任意)

10. ファイルのアップロード(任意)

授業で作成したファイルをアップロードすることが可能です。これで授業の振り返りが成果物でも行うことができるようになります。1ファイル100MB、5ファイルまでアップロードすることが可能です。こちらの設問の入力は任意となっています。

ファイルのアップロード(任意)

11. 送信

「次へ」をクリックすると、確認画面が表示されます。間違いがない場合は「送信」ボタンをクリックします。

12. スプレッドシートで振り返り内容の確認

振り返りフォームのオーナーである教員と共有されたスプレッドシートが、生徒一人につき1ファイル作成されます(生徒のアカウントでは Google ドライブ 上の「共有アイテム」で開くことができます)。このファイルは一度作成されれば、次回以降は追記されていきますので、振り返りを蓄積していくことが可能となります。

振り返りスプレッドシート

また、2022年9月21日に更新したバージョン2.0より、授業名毎にシートが分かれるようになったため、授業毎に振り返りが蓄積されます。

授業名毎にシートが分かれる

なお、生徒はこのスプレッドシートを編集することが出来ないようにしています。コメントを付けたり、コメントを返信したりすることは可能です。

スプレッドシートには、振り返りフォームで提出したファイルのリンクが入力されているので、成果物を見て振り返ることも可能です。一点注意が必要なのが、提出したファイル(スプレッドシートのリンクからアクセスできるファイル)は、生徒側からは編集することができないようにしています。もちろん、生徒自身のマイドライブにあるオリジナルのファイルは、生徒自身で編集することできます。

振り返りスプレッドシートに記載されているファイルのリンク

教員の使い方(セッティング)

1. 先生用のGoogleアカウントでログイン

先生用の Google アカウントで Google にログインします。

2. Google フォーム を Google ドライブ に コピー

次のURLにアクセスして「コピーを作成」ボタンから Google フォーム を 自分(先生)の Google ドライブ にコピーします。
※ ここで「Apps Script ファイルを表示」ボタンクリックしないでください!

各校種で異なる点は、フォーム内での「授業名」の選択肢が異なるということだけです。また、「授業名」の選択肢は、各学校の実態に合わせて変更することが可能です。

なお、コピーした Google フォーム の名前、場所も任意のものに変更することが可能です。

3. ファイルのアップロード先のフォルダの復元(作成)

2でコピーした Google フォーム を開き、「ファイルのアップロード先のフォルダが見つかりません」で「復元」をクリックします。この操作で、振り返りフォームでアップロードできるファイルのアップロード先のフォルダ(デフォルトでのファイル名「授業振り返りフォーム(File responses)」)を作成することになります。

なお、作成されたフォルダの名前、場所は任意のものに変更することが可能です。

4. Google スプレッドシート のテンプレートを Google ドライブ に コピー

次のURLにアクセスして Google スプレッドシート のテンプレートを 自分(先生)の Google ドライブ にコピーします。

5. コピーした Google スプレッドシート の名前の変更

4でコピーしたファイルを右クリックして、「名前の変更」を選択し、冒頭の文字列である「コピー 〜 」を削除してファイル名を「template_classSelfFeedbackv2.0」にします。もしくは、4でコピーしたファイルを開いて、画面左上のファイル名の冒頭の文字列である「コピー 〜 」を削除してファイル名を「template_classSelfFeedbackv2.0」にします。なお、light版の場合は末尾に「_light」が付加されます。

6. スプレッドシート の保存先のフォルダを Google ドライブ 上に作成

生徒一人につき作成されるスプレッドシートの保存先のフォルダを 自分(先生)の Google ドライブ 上に作成します。作成する場所とフォルダ名は任意です。自分が整理しやすい場所、フォルダ名にすると良いでしょう。

Google ドライブ 上でのフォルダの作成

7. 保存先フォルダのフォルダIDの取得

6で作成したスプレッドシートの保存先のフォルダのフォルダIDを取得します。フォルダIDは、フォルダを開いたURLの “folders/” 以降となります。メモ帳や Google Keep など、任意の場所にコピペして退避させておきます。なお、下記の画像内でのフォルダ名は「情報Iの振り返り」となっていますが、現在は複数科目・授業にも対応できるようになっているため、ご自身や先生方がわかりやすいフォルダ名にしていただいて構いません。

Google ドライブ 上でのフォルダのIDの取得

8. テンプレートファイルを保存先フォルダに移動

4でコピーした(つまり、5で名前を変更した)スプレッドシートのテンプレートファイルを6で作成した(つまり、7でファイルIDを取得した)フォルダに移動します。

テンプレートファイルを保存先フォルダに移動

9. スクリプトを開く

2でコピーした Google フォーム(振り返りフォーム)を開いて、右上の縦三点リーダーからスクリプトエディタを開きます。なお、“スクリプト エディタ” という選択肢は、ある程度画面(ウィンドウ)が大きくないと表示されないので、表示されない方は、画面(ウィンドウ)幅を広げてみてください。

スクリプトエディタを開く

10. スクリプトにフォルダIDをセッティング

7で取得したフォルダIDをスクリプトに貼り付け、「プロジェクタを保存」のボタンをクリックします。

スクリプトエディタでフォルダIDをセットしてプロジェクトを保存

11. スクリプトのトリガー画面を開く

スクリプト画面の左メニューから「トリガー」をクリックします。

スクリプト画面の左メニューから「トリガー」をクリック

12. 「トリガーを追加」画面を開く

画面右下の「トリガーを追加」ボタンをクリックします。

画面右下の「トリガーを追加」ボタンをクリック

13. トリガーの設定

「イベントの種類を選択」にて “フォーム送信時” 、「エラー通知設定」にて “1週間おきに通知を受け取る” を選択し、保存ボタンをクリックします。

「イベントの種類を選択」にて “フォーム送信時” 、「エラー通知設定」にて “1週間おきに通知を受け取る” を選択し、保存ボタンをクリック

14. Google アカウントの選択

スクリプトを実行する Google アカウント を選択します。つまり、自分(先生)のアカウントを選択します。

スクリプトを実行する Google アカウント を選択

15. スクリプトの追加の許可

今回のようなお手製のスクリプトを使用する場合、スクリプトの追加の許可が必要となります。ドメイン(職場の環境)によっては本手順は不要な場合があります。その場合は、本手順をスキップしてください。

14のあと、「このアプリは Google で確認されていません」という警告ウィンドウが表示されるので、左下の「詳細」をクリックします。

「このアプリは Google で確認されていません」という警告ウィンドウが表示されるので、左下の「詳細」をクリックする

次に「FormToSpreadsheet(安全ではないページ)に移動」をクリックします。

「FormToSpreadsheet(安全ではないページ)に移動」をクリックする

最後に、「FormToSpreadsheet が Google アカウントへのアクセスをリクエストしています」というウィンドウにて一番下の「許可」ボタンをクリックします。

「FormToSpreadsheet が Google アカウントへのアクセスをリクエストしています」というウィンドウにて一番下の「許可」ボタンをクリックする

16. 回答先のスプレッドシートの作成・選択

2022年9月21日に本ツールがバージョン2.0に更新されたことによって、この作業が必要となりました。やることは至って簡単です。児童・生徒が回答する前に、振り返りフォームの編集画面の「回答」タブから緑色のスプレッドシートのアイコンをクリックし、回答先のスプレッドシートを作成・選択(リンク)するだけとなります。

振り返りフォームの編集画面の「回答」タブから緑色のスプレッドシートのアイコンをクリックし、回答先のスプレッドシートを作成・選択(リンク)する

この作業をしておくと、回答先のスプレッドシートの一番右の列に児童・生徒個別のスプレッドシートのリンクが追加されることになり、児童・生徒から質問があった際にコメント・回答しやすくなります。

回答先のスプレッドシートの一番右の列に児童・生徒個別のスプレッドシートのリンクが追加される

回答先のスプレッドシートの「授業名」の列でフィルタをかければ授業ごとに質問の有無を確認したり、自己評価を確認したりすることもできます。成績を算出する時期には ピボットテーブル を活用するのも良いかもしれません。

少々イケていない点は、児童・生徒個別のスプレッドシートへのリンクは、スプレッドシートファイルの各シートのリンクではなく、あくまで「スプレッドシートファイル」へのリンクとなるため、シートタブで該当の授業をクリックする必要があります。これは、Google の仕様上、各シートへのリンクは生成できないために生じる手間となりますが、複数授業対応になったメリットを考慮すれば微々たるものではないかと考えています。

制限

Google Wordspace の管理コンソールにおいて、「Drive SDK API 経由での Google ドライブへのアクセスをユーザーに許可する」の設定が OFF になっている場合には、本ツールを使用することができないことが判明しました(デフォルトでは ON)(2022年11月23日)。

手順通り実施してもうまくいかない場合には、校内や職場のシステム管理者に確認してください。

なお、本ツールに限らず、Google Drive を操作する GAS(スクリプト)やサードパーティ製の拡張機能等でも同様の制限があります。

今後の予定

YouTube にて多くのコメントをいただいているのでバージョンアップしようと考えています。まずは質問してくれた児童・生徒のスプレッドシートにすぐアクセスできるような仕組みの実装を予定しています。2022年9月21日にバージョン2.0を公開し、回答先のスプレッドシートから簡単に児童・生徒のスプレッドシートにアクセスできるようになりました。また、複数授業にも対応できるようにしました。

ぜひ、使ってみた感想や追加での要望等があればお気軽にコメント・問い合わせ等でご連絡いただけると嬉しいです。

〜バージョン1の動画〜

〜バージョン2の動画〜

〜Light版の動画〜

コードの公開

通常版

/*
Copyright (c) 2022 alfnets
Released under the MIT license
*/

// フォルダIDとテンプレートファイル名を定義(指定)
const FOLDER_ID = 'スプレッドシートを保存したいフォルダIDを入力してください';    // 振り返りのスプレッドシートを保存したいフォルダのID
const TEMPLATE_FILE_NAME = "template_classSelfFeedbackv2.0";   // テンプレートファイルは8行目のフォルダ内に格納すること

function sendSpredsheet(event) {
  const emailaddress = event.response.getRespondentEmail(); // Emailアドレスを取得
  // フォームの回答結果の整理
  const formResponses = event.response.getItemResponses();  // 回答結果を取得
  const studentNumber = formResponses[0].getResponse();     // 学籍番号
  const classname = formResponses[1].getResponse();         // 授業名
  const content = formResponses[2].getResponse();           // 授業内容
  const selfchecks = formResponses[3].getResponse();        // 自己評価
  const actfeedback = formResponses[4].getResponse();       // 取り組みに対する振り返り
  const contentfeedback = formResponses[5].getResponse();   // 授業内容に対する振り返り
  const question = formResponses[6].getResponse();          // 質問
  let files = [];
  try {
    files = formResponses[7].getResponse();           // アップロードファイル
  } catch (e) {
    files = new Array("");
  }

  // 操作フォルダーとファイル一覧の取得等
  const folder = DriveApp.getFolderById(FOLDER_ID);   // 操作フォルダー
  const filelist = folder.getFiles();   // 操作フォルダーに格納されているファイル一覧をIteratorオブジェクトとして取得
  const fileName = `【振り返りシート】${studentNumber}`   // 振り返りシートの名前
  let check = 0;    //  既にファイルが存在しているかのチェックフラグ

  // 既にファイルが存在しているかの確認
  while (filelist.hasNext()) {          // .hasNext()でファイルがまだ残っているかどうかチェック
    const file = filelist.next();     // .next()で順番にファイルを取り出す
    if (file.getName() === fileName) {  // 既にファイルがあったならばチェックフラグを立てる
      check = 1;
      Logger.log("exist!");
    }
  }

  // スプレッドシートファイルの作成
  if (check === 0) {    // ファイルがなければテンプレートファイルから複製してファイルを作成する
    const templateFiles = DriveApp.getFolderById(FOLDER_ID).getFilesByName(TEMPLATE_FILE_NAME);   // テンプレートファイルをIteratorオブジェクトとして取得
    const templateFile = templateFiles.next();   // .next()でIteratorオブジェクトからtemplateファイルを取得
    templateFile.makeCopy(fileName, folder);    // templateファイルをコピー
  }

  // スプレッドシート情報の取得
  const targetFiles = DriveApp.getFolderById(FOLDER_ID).getFilesByName(fileName);   // 操作するスプレッドシートをIteratorオブジェクトとして取得
  const targetFile = targetFiles.next();  // .next()でIteratorオブジェクトからファイルを取得
  const targetss = SpreadsheetApp.openById(targetFile.getId()); // .getIdでID名を指定してスプレッドシートとしてファイルを取得し直す
  const targetsheets = targetss.getSheets();    // すべてのシートを取得

  // 既に同じ授業名のシートが存在しているかの確認
  const sheet_names = [];
  targetsheets.forEach(sheet => {
    sheet_names.push(sheet.getName());
  });
  if (sheet_names.includes(classname)) {  // 既に同じ授業名のシートが存在している場合
    var targetsheet = targetss.getSheetByName(classname);  // 授業名のシートを取得    
  } else {
    const templatesheet = targetss.getSheetByName("template");  // テンプレートシートを取得
    var targetsheet = templatesheet.copyTo(targetss);   // テンプレートシートをコピー
    targetsheet.setName(classname);   // シート名を授業名に変更
    targetsheet.activate();   // 新しいシートをアクティブにする
    targetss.moveActiveSheet(1);   // 新しいシートを一番左に移動
  }

  // スプレッドシートへの入力
  const dataRange = targetsheet.getRange("B:B").getValues();  // B列の値を配列で取得
  const lastRow = dataRange.filter(String).length;  // 空白の要素を除いた配列の長さを取得
  const d = new Date();   // 現在時刻を取得
  targetsheet.getRange(lastRow + 1, 2).setValue(`${d.getMonth() + 1}月${d.getDate()}日`);   // 日付を入力
  targetsheet.getRange(lastRow + 1, 3).setValue(content);         // 授業内容を入力
  targetsheet.getRange(lastRow + 1, 4).setValue(selfchecks[0]);   //  主体的な態度の評価を入力
  targetsheet.getRange(lastRow + 1, 5).setValue(selfchecks[1]);   //  思考・判断・表現の評価を入力
  targetsheet.getRange(lastRow + 1, 6).setValue(selfchecks[2]);   //  知識・技能の評価を入力
  targetsheet.getRange(lastRow + 1, 7).setValue(actfeedback);     //  取り組みに対する振り返りを入力  
  targetsheet.getRange(lastRow + 1, 8).setValue(contentfeedback); //  授業内容に対する振り返りを入力
  targetsheet.getRange(lastRow + 1, 9).setValue(question);        //  質問内容を入力

  if (files[0] != "") {
    let fileLink = '';
    let counter = 0;
    files.forEach(fileId => {
      counter++;
      fileLink = `https://drive.google.com/file/d/${fileId}`;
      targetsheet.getRange(lastRow + 1, 9 + counter).setValue(fileLink);  //  アップロードファイルのリンクを入力
    });

    // アップロードファイルの閲覧権限の付与(生徒用)
    try {
      files.forEach(fileId => {
        Drive.Permissions.insert(
          {
            'role': 'reader', //権限タイプを選ぶ (owner, organizer, fileOrganizer, writer, reader)
            'type': 'user', //アカウントタイプを選ぶ (user, group, domain, anyone)
            'value': emailaddress
          },
          fileId,
          {
            'sendNotificationEmails': 'false' //true=通知ON, false=通知OFF
          }
        );
      });
    } catch (e) {
      console.error('提出されたファイルに共有設定をすることができませんでした。')
    }
  }

  // 個別のスプレッドシートへのリンクを回答結果のスプレッドシートに追記
  addIndividualSsLinkToAnswerSs(targetFile, event, studentNumber);

  // スプレッドシートの閲覧権限の付与
  let scheck = 0;   // 権限があるかのフラグ
  const sviewers = targetFile.getViewers();    // 閲覧権限のアカウントを取得
  sviewers.forEach(viewer => {   // 今回のアカウントに既にコメント権限が付与されているかチェック
    if (viewer.getEmail() === emailaddress) {
      scheck = 1;   // アカウントにコメント権限が付与されているならフラグを立てる
    }
  });
  if (scheck === 0) {    // アカウントが付与されていなければ付与
    try {
      targetFile.addCommenter(emailaddress);
    } catch (e) {
      const error_str = `${studentNumber} さんが入力したメールアドレス(${emailaddress})は、共有を許可されていないなどの問題があります。入力に誤りがあるかなどの確認をしてください。なお、${studentNumber} さん個別の振り返りスプレッドシートに振り返り内容は転送されていますが、スプレッドシートが ${studentNumber} さんと共有されていない可能性があります。また、ファイルが提出されている場合には、提出されたファイルが${studentNumber}さんと共有設定されていません。提出されたファイルの共有設定を確認して、共有されていない場合には「閲覧者(コメント可)」で手動で共有してください。

${studentNumber} さん個別の振り返りスプレッドシート: ${targetFile.getUrl()}
`
      console.error(error_str)
      const exec_emailAddress = Session.getActiveUser().getUserLoginId();
      sendMailWithOption(exec_emailAddress, studentNumber, error_str);
    }
  }
}

/* 個別のスプレッドシートへのリンクを回答結果のスプレッドシートに追記 */
function addIndividualSsLinkToAnswerSs(targetFile, event, studentNumber) {
  const timeStamp = event.response.getTimestamp();            // フォームに送信したタイムスタンプを取得
  const downloadUrl = targetFile.getUrl();                    // 個別のスプレッドシートのリンクを取得
  const answerSheet = getSheet(event.source.getId());         // 回答シートを取得
  const timeStamps = answerSheet.getRange("A:A").getValues();  // A列(タイムスタンプ)の値を配列で取得
  const studentNumbers = answerSheet.getRange("C:C").getValues();  // C列(学籍番号)の値を配列で取得
  const targetRow = getTargetRow(timeStamps, studentNumbers, timeStamp, studentNumber) // 回答を記録した行を取得
  const lastColumn = answerSheet.getRange(1, 1).getNextDataCell(SpreadsheetApp.Direction.NEXT).getColumn() + 1;
  answerSheet.getRange(targetRow, lastColumn).setValue(downloadUrl);  // 個別のスプレッドシートのリンクを入力
}

/* スプレッドシートのIDを取得する */
function getSheet(formId) {
  const form = FormApp.openById(formId);    // フォームを取得(スクリプトエディタでDrive APIを追加しておく必要がある)
  const answerSs = SpreadsheetApp.openById(form.getDestinationId());    // フォームとリンクしている回答スプレッドシートを取得
  const formUrl = form.getEditUrl().replace('/edit', '');   // フォームのURL兼IDを取得
  const destinationSheet = answerSs.getSheets().find(sheet =>   // 回答スプレッドシートの各シートにおいて、リンクしているフォームがformUrlと一致するシートを探す
    sheet.getFormUrl()?.replace('/viewform', '') === formUrl
  );
  return destinationSheet;
}

/* 回答した行を取得 */
function getTargetRow(timeStamps, studentNumbers, timeStamp, studentNumber) {
  for(var i = 1; i < timeStamps.length; i++){
    if(timeStamps[i] + studentNumbers[i] === timeStamp + studentNumber){
      return i + 1;
    }
  }
}

/* メールアドレスを間違えていたりして共有設定できなかった際のメール通知 */
function sendMailWithOption(address, studentNumber, message) {
  const subject = `【振り返りフォームエラー】${studentNumber}さんが入力したメールアドレスに問題がある`; // メールの件名
  // const options = { /*オプションの内容*/ };
  const body = `${message}`;
  MailApp.sendEmail(address, subject, body);
}

light版(自由記述を統合したバージョン)

/*
Copyright (c) 2022 alfnets
Released under the MIT license
*/

// フォルダIDとテンプレートファイル名を定義(指定)
const FOLDER_ID = 'スプレッドシートを保存したいフォルダIDを入力してください';    // 振り返りのスプレッドシートを保存したいフォルダのID
const TEMPLATE_FILE_NAME = "template_classSelfFeedbackv2.0_light";   // テンプレートファイルは8行目のフォルダ内に格納すること

function sendSpredsheet(event) {
  const emailaddress = event.response.getRespondentEmail(); // Emailアドレスを取得
  // フォームの回答結果の整理
  const formResponses = event.response.getItemResponses();  // 回答結果を取得
  const studentNumber = formResponses[0].getResponse();     // 学籍番号
  const classname = formResponses[1].getResponse();         // 授業名
  const content = formResponses[2].getResponse();           // 授業内容
  const selfchecks = formResponses[3].getResponse();        // 自己評価
  const actfeedback = formResponses[4].getResponse();       // 振り返り(自由記述)
  const question = formResponses[5].getResponse();          // 質問
  let files = [];
  try {
    files = formResponses[6].getResponse();           // アップロードファイル
  } catch (e) {
    files = new Array("");
  }

  // 操作フォルダーとファイル一覧の取得等
  const folder = DriveApp.getFolderById(FOLDER_ID);   // 操作フォルダー
  const filelist = folder.getFiles();   // 操作フォルダーに格納されているファイル一覧をIteratorオブジェクトとして取得
  const fileName = `【振り返りシート】${studentNumber}`   // 振り返りシートの名前
  let check = 0;    //  既にファイルが存在しているかのチェックフラグ

  // 既にファイルが存在しているかの確認
  while (filelist.hasNext()) {          // .hasNext()でファイルがまだ残っているかどうかチェック
    const file = filelist.next();     // .next()で順番にファイルを取り出す
    if (file.getName() === fileName) {  // 既にファイルがあったならばチェックフラグを立てる
      check = 1;
      Logger.log("exist!");
    }
  }

  // スプレッドシートファイルの作成
  if (check === 0) {    // ファイルがなければテンプレートファイルから複製してファイルを作成する
    const templateFiles = DriveApp.getFolderById(FOLDER_ID).getFilesByName(TEMPLATE_FILE_NAME);   // テンプレートファイルをIteratorオブジェクトとして取得
    const templateFile = templateFiles.next();   // .next()でIteratorオブジェクトからtemplateファイルを取得
    templateFile.makeCopy(fileName, folder);    // templateファイルをコピー
  }

  // スプレッドシート情報の取得
  const targetFiles = DriveApp.getFolderById(FOLDER_ID).getFilesByName(fileName);   // 操作するスプレッドシートをIteratorオブジェクトとして取得
  const targetFile = targetFiles.next();  // .next()でIteratorオブジェクトからファイルを取得
  const targetss = SpreadsheetApp.openById(targetFile.getId()); // .getIdでID名を指定してスプレッドシートとしてファイルを取得し直す
  const targetsheets = targetss.getSheets();    // すべてのシートを取得

  // 既に同じ授業名のシートが存在しているかの確認
  const sheet_names = [];
  targetsheets.forEach(sheet => {
    sheet_names.push(sheet.getName());
  });
  if (sheet_names.includes(classname)) {  // 既に同じ授業名のシートが存在している場合
    var targetsheet = targetss.getSheetByName(classname);  // 授業名のシートを取得    
  } else {
    const templatesheet = targetss.getSheetByName("template");  // テンプレートシートを取得
    var targetsheet = templatesheet.copyTo(targetss);   // テンプレートシートをコピー
    targetsheet.setName(classname);   // シート名を授業名に変更
    targetsheet.activate();   // 新しいシートをアクティブにする
    targetss.moveActiveSheet(1);   // 新しいシートを一番左に移動
  }

  // スプレッドシートへの入力
  const dataRange = targetsheet.getRange("B:B").getValues();  // B列の値を配列で取得
  const lastRow = dataRange.filter(String).length;  // 空白の要素を除いた配列の長さを取得
  const d = new Date();   // 現在時刻を取得
  targetsheet.getRange(lastRow + 1, 2).setValue(`${d.getMonth() + 1}月${d.getDate()}日`);   // 日付を入力
  targetsheet.getRange(lastRow + 1, 3).setValue(content);         // 授業内容を入力
  targetsheet.getRange(lastRow + 1, 4).setValue(selfchecks[0]);   //  主体的な態度の評価を入力
  targetsheet.getRange(lastRow + 1, 5).setValue(selfchecks[1]);   //  思考・判断・表現の評価を入力
  targetsheet.getRange(lastRow + 1, 6).setValue(selfchecks[2]);   //  知識・技能の評価を入力
  targetsheet.getRange(lastRow + 1, 7).setValue(actfeedback);     //  振り返り(自由記述)を入力  
  targetsheet.getRange(lastRow + 1, 8).setValue(question);        //  質問内容を入力

  if (files[0] != "") {
    let fileLink = '';
    let counter = 0;
    files.forEach(fileId => {
      counter++;
      fileLink = `https://drive.google.com/file/d/${fileId}`;
      targetsheet.getRange(lastRow + 1, 8 + counter).setValue(fileLink);  //  アップロードファイルのリンクを入力
    });

    // アップロードファイルの閲覧権限の付与(生徒用)
    try {
      files.forEach(fileId => {
        Drive.Permissions.insert(
          {
            'role': 'reader', //権限タイプを選ぶ (owner, organizer, fileOrganizer, writer, reader)
            'type': 'user', //アカウントタイプを選ぶ (user, group, domain, anyone)
            'value': emailaddress
          },
          fileId,
          {
            'sendNotificationEmails': 'false' //true=通知ON, false=通知OFF
          }
        );
      });
    } catch (e) {
      console.error('提出されたファイルに共有設定をすることができませんでした。')
    }
  }

  // 個別のスプレッドシートへのリンクを回答結果のスプレッドシートに追記
  addIndividualSsLinkToAnswerSs(targetFile, event, studentNumber);

  // スプレッドシートの閲覧権限の付与
  let scheck = 0;   // 権限があるかのフラグ
  const sviewers = targetFile.getViewers();    // 閲覧権限のアカウントを取得
  sviewers.forEach(viewer => {   // 今回のアカウントに既にコメント権限が付与されているかチェック
    if (viewer.getEmail() === emailaddress) {
      scheck = 1;   // アカウントにコメント権限が付与されているならフラグを立てる
    }
  });
  if (scheck === 0) {    // アカウントが付与されていなければ付与
    try {
      targetFile.addCommenter(emailaddress);
    } catch (e) {
      const error_str = `${studentNumber} さんが入力したメールアドレス(${emailaddress})は、共有を許可されていないなどの問題があります。入力に誤りがあるかなどの確認をしてください。なお、${studentNumber} さん個別の振り返りスプレッドシートに振り返り内容は転送されていますが、スプレッドシートが ${studentNumber} さんと共有されていない可能性があります。また、ファイルが提出されている場合には、提出されたファイルが${studentNumber}さんと共有設定されていません。提出されたファイルの共有設定を確認して、共有されていない場合には「閲覧者(コメント可)」で手動で共有してください。

${studentNumber} さん個別の振り返りスプレッドシート: ${targetFile.getUrl()}
`
      console.error(error_str)
      const exec_emailAddress = Session.getActiveUser().getUserLoginId();
      sendMailWithOption(exec_emailAddress, studentNumber, error_str);
    }
  }
}

/* 個別のスプレッドシートへのリンクを回答結果のスプレッドシートに追記 */
function addIndividualSsLinkToAnswerSs(targetFile, event, studentNumber) {
  const timeStamp = event.response.getTimestamp();            // フォームに送信したタイムスタンプを取得
  const downloadUrl = targetFile.getUrl();                    // 個別のスプレッドシートのリンクを取得
  const answerSheet = getSheet(event.source.getId());         // 回答シートを取得
  const timeStamps = answerSheet.getRange("A:A").getValues();  // A列(タイムスタンプ)の値を配列で取得
  const studentNumbers = answerSheet.getRange("C:C").getValues();  // C列(学籍番号)の値を配列で取得
  const targetRow = getTargetRow(timeStamps, studentNumbers, timeStamp, studentNumber) // 回答を記録した行を取得
  const lastColumn = answerSheet.getRange(1, 1).getNextDataCell(SpreadsheetApp.Direction.NEXT).getColumn() + 1;
  answerSheet.getRange(targetRow, lastColumn).setValue(downloadUrl);  // 個別のスプレッドシートのリンクを入力
}

/* スプレッドシートのIDを取得する */
function getSheet(formId) {
  const form = FormApp.openById(formId);    // フォームを取得(スクリプトエディタでDrive APIを追加しておく必要がある)
  const answerSs = SpreadsheetApp.openById(form.getDestinationId());    // フォームとリンクしている回答スプレッドシートを取得
  const formUrl = form.getEditUrl().replace('/edit', '');   // フォームのURL兼IDを取得
  const destinationSheet = answerSs.getSheets().find(sheet =>   // 回答スプレッドシートの各シートにおいて、リンクしているフォームがformUrlと一致するシートを探す
    sheet.getFormUrl()?.replace('/viewform', '') === formUrl
  );
  return destinationSheet;
}

/* 回答した行を取得 */
function getTargetRow(timeStamps, studentNumbers, timeStamp, studentNumber) {
  for(var i = 1; i < timeStamps.length; i++){
    if(timeStamps[i] + studentNumbers[i] === timeStamp + studentNumber){
      return i + 1;
    }
  }
}

/* メールアドレスを間違えていたりして共有設定できなかった際のメール通知 */
function sendMailWithOption(address, studentNumber, message) {
  const subject = `【振り返りフォームエラー】${studentNumber}さんが入力したメールアドレスに問題がある`; // メールの件名
  // const options = { /*オプションの内容*/ };
  const body = `${message}`;
  MailApp.sendEmail(address, subject, body);
}

GitHubでもコードを公開しています。ライセンスはMITとなりますのでご自由にお使いください。

情報教育を中心に教育関連のことを発信していますので 他の記事TwitterYouTube もご覧いただけると嬉しいです。また、情報教育教材のまとめサイト も作成しています。教材だけでなく、入試情報や便利ツールの一覧集などのページもありますのでぜひご覧ください。

コメント

  1. ミヤタケ より:

    こんにちは。とても興味深く拝見させていただきました。
    いくつか要望があり、コメントさせていただきます。
    私は大阪府立高校の家庭科教諭をしております。
    生徒が学習を振り返られるスプレッドシートをフォームの入力で簡単にできるのは素晴らしいと思いました。
    できれば、単元ごとにポートフォリオが完成するものをつくりたいと思っており、単元を貫く問いに対して学習する前の考えを記入する欄。
    また、単元の学習が終わってから、改めて同じ単元を貫く問いに対する考えや、その考えに至った経
    緯や今後の行動や意識についてを記入する欄があると嬉しいと思いました。
    セクションを変えて学習前、学習中、学習後などに分けることで、集まるデータをポートフォリオのようなものにすることは可能でしょうか?
    また、可能でしたらぜひともそのようなシステムを教えていただきたいです。

    • あるふ より:

      コメントありがとうございます!
      毎授業ではなく、単元毎に使うのもとても有効な方法ですね。また、学習の前後での変遷を残していくことは私もとても良いことだと思っています。

      ミヤタケさんがやりたいことを本ツールで行うのであれば、各回答項目の文言は変更しても問題ありませんので、そちらで十分であればそのようにお使いください。回答項目を増やしたり減らしたりする場合は、GAS(プログラム)をいじらないといけないので、少々プログラミングスキルが必要となります。

      「セクションを変えて学習前、学習中、学習後などに分ける」というのは、どうやってデータとして残していくかによります(単元ごとにシートをわけるのかどうするのかなど)。たとえば、一行に一単元の振り返りを並べた場合、その単元での前後の変化は見にくいでしょうし、「学習前、学習中、学習後」で3行にすると、他の単元がその前後に並んだ際にみづらくなるでしょう。そうであれば単元ごとにシートをわけるのが良さそうですが、項目も含めて4行のシートが単元ごとに増えていくのも微妙な気がしています。

      つまり、スプレッドシートのような表計算ソフトだと少々厳しいのではないかというのが個人的な考えです。
      似たようなことをスライドを用いてやっている先生もいらっしゃいますので、単元ごとにスライドを作成するのも良いと思います(単元での振り返りシート・プリントのイメージ)。

      システム化しようとするのであればWebシステムで作成するのも良さそうですが、今は仕事の片手間で行っている活動ですので、私一人では時間的にもスキル的にも厳しいというのが現状です。
      回答になっていますでしょうか。ミヤタケさんのお力に少しでもなれば幸いです。

タイトルとURLをコピーしました