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に更新し、複数の児童・生徒が同時に回答した際に、回答先スプレッドシートに追記する個別スプレッドシートへのリンクが行単位でずれる不具合を修正しました。また、ファイルを共有することができないメールアドレスを児童・生徒が入力した際に、振り返りフォームを設定したオーナー(教員)にメール通知が飛ぶ機能を追加しました。
2023年5月20日に、バージョン2.2に更新しました(v2.1 からの乗り換え方法はv2.1からv2.2への移行方法から)。
・デフォルトで、ログインしているアカウントのメールアドレスを取得するように変更(メールアドレスの入力が不要)
・GAS がフォームからデータを取得できない場合、リトライする機能を追加
・エラーの文言を修正・更新
できるようになること
- 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 にログインしている状態であれば画面上部に記載されているのでそちらをコピペしたり、ブラウザの入力補助機能を使用すれば入力の手間も省けるでしょう。
デフォルトで、ログインしているアカウントのメールアドレスを取得するようにしました(2023年5月20日)。その代わり、チェックボックスにチェックする必要があります。また、ログインしているアカウントが学校のメールアドレスと異なる場合は「アカウントを切り替える」必要があります(チェックボックスの説明文がわかりにくいかもしれませんが、こちらは Google フォーム の定型の文言であるため設定で変更することができません。。)。

3. 学籍番号の入力
全ての児童・生徒で被らない、唯一無二(ユニーク)な番号を入力します。この番号がファイル名にも使用されるので、児童・生徒は毎回間違えずに入力する必要があります。

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

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

6. 3観点での振り返り
「主体的に学習に取り組む態度」、「思考・判断・表現」、「知識・技能」の3観点で選択して自己評価を行います。すべての授業で3観点すべてを扱っているわけではないのが現実だと思いますので、扱っていない場合は「ー:評価対象外」を選択します。
なお、“各項目の文言”、“評価の段階”、“評価の段階の文言” は先生によって変更することが可能です。観点を増やしたり減らしたりすることは、スクリプト(プログラム)を変更する必要があるので、プログラミングができる方や挑戦したい方は後述する Google Apps Script(GAS)をいじってみてください。

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

8. 学んだことのアウトプット
学んだことをアウトプットします。アウトプットすることで、学んだことの定着が図れると思い、このような項目を用意しました。なお、文言や説明文は教員によって変更することが可能です。
2022年9月24日に、児童・生徒の負担を減らすことを目的に、本入力項目を削除して 7 の「振り返りの自由記述の入力」に統合した light版 を公開しました。light版 を活用したい方は後述の 教員の使い方(セッティング) にて light版 を選択してください。

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 ドライブ 上に作成します。作成する場所とフォルダ名は任意です。自分が整理しやすい場所、フォルダ名にすると良いでしょう。

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

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

9. スクリプトエディタを開く
2でコピーした Google フォーム(振り返りフォーム)を開いて、右上の縦三点リーダーからスクリプトエディタを開きます。

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

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

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

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

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

15. スクリプトの追加の許可
今回のようなお手製のスクリプトを使用する場合、スクリプトの追加の許可が必要となります。ドメイン(職場の環境)によっては本手順は不要な場合があります。その場合は、本手順をスキップしてください。
14のあと、「このアプリは Google で確認されていません」という警告ウィンドウが表示されるので、左下の「詳細」をクリックします。

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

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

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

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

回答先のスプレッドシートの「授業名」の列でフィルタをかければ授業ごとに質問の有無を確認したり、自己評価を確認したりすることもできます。成績を算出する時期には ピボットテーブル を活用するのも良いかもしれません。
少々イケていない点は、児童・生徒個別のスプレッドシートへのリンクは、スプレッドシートファイルの各シートのリンクではなく、あくまで「スプレッドシートファイル」へのリンクとなるため、シートタブで該当の授業をクリックする必要があります。これは、Google の仕様上、各シートへのリンクは生成できないために生じる手間となりますが、複数授業対応になったメリットを考慮すれば微々たるものではないかと考えています。
17. 学校アカウントでのログインを必須にする
必ず設定しなければならないものではありませんが、学校アカウントでのログインを必須にすることをオススメします。必須にしなかった場合、学校外のアカウントで本ツールを使用すると下記のどちらかが発生します(特別なエラーが発生しない限り生徒が送信した振り返り内容が消えるようなことはありません)。
- 学校が学校外のアカウントとのドキュメント等の共有を禁止している場合
- 学校以外のアカウントと共有できなかったというエラー通知が先生に飛ぶ
- 学校が学校外のアカウントとのドキュメント等の共有を禁止していない場合
- 学校外のアカウントと共有できてしまう
上記の事態を回避するためには、フォームの設定タブから 「[学校や自治体名] と信頼できる組織のユーザーに限定する」を ON にします。こうすることで、振り返りフォームに回答するためには学校アカウントでログインすることを強制できます(公開授業や研修等で、外部の方(学校外のアカウント)に利用していただく際には本設定を OFF にする必要があります)。
なお、この設定は個人アカウントでは表示されませんので、検証等で試してみたい方はご注意ください。
![フォームの設定タブから、「[学校や自治体名] と信頼できる組織のユーザーに限定する」を ON にする](https://alfnets.info/wp-content/uploads/dbe12fee004c147518c6ef643d806f61-1-1024x796.jpg)
ログインしているアカウント以外のメールアドレスでも回答できるようにしたい場合
こちらは好みの設定となります。(v2.1以前のように)ログインしているアカウント以外のメールアドレスでも回答できるようにするためには、フォームの回答タブから「メールアドレスを収集する」を “回答者からの入力” に変更すればOKです。

制限
Google Wordspace の管理コンソールにおいて、「Drive SDK API 経由での Google ドライブへのアクセスをユーザーに許可する」の設定が OFF になっている場合には、本ツールを使用することができないことが判明しました(デフォルトでは ON)(2022年11月23日)。
手順通り実施してもうまくいかない場合には、校内や職場のシステム管理者に確認してください。
なお、本ツールに限らず、Google Drive を操作する GAS(スクリプト)やサードパーティ製の拡張機能等でも同様の制限があります。
今後の予定
YouTube にて多くのコメントをいただいているのでバージョンアップしようと考えています。まずは質問してくれた児童・生徒のスプレッドシートにすぐアクセスできるような仕組みの実装を予定しています。2022年9月21日にバージョン2.0を公開し、回答先のスプレッドシートから簡単に児童・生徒のスプレッドシートにアクセスできるようになりました。また、複数授業にも対応できるようにしました。
ぜひ、使ってみた感想や追加での要望等があればお気軽にコメント・問い合わせ等でご連絡いただけると嬉しいです。
〜バージョン1の動画〜
〜バージョン2の動画〜
〜Light版の動画〜
v2.1からv2.2への移行方法
1. コードの差し替え
スクリプトエディタを開き、下記のコードに差し替えてください。なお、FOLDER_ID は既存のコードのものを設定(コピペ)してください。なお、通常版とlight版でお間違いないようご注意ください。
2. ログインアカウントのメールアドレスを使用するように変更
手順1のみでも本ツールを利用することはできますが、お好みで、ログインアカウントのメールアドレスを使用するように変更できます。これにより、メールアドレスの入力の代わりにチェックボックスにチェックするのみとなり、メールアドレスの入力を省いて誤入力を防ぐことができます。
フォームの設定タブから、「メールアドレスを収集する」を “確認済み” に変更します。

こうすることで、児童・生徒の入力画面が若干変わり、ログインしているアカウントが学校のメールアドレスと異なる場合は「アカウントを切り替える」必要がありますので、児童・生徒にも伝えておいた方が良いでしょう(チェックボックスの説明文がわかりにくいかもしれませんが、こちらは Google フォーム の定型の文言であるため設定で変更することができません。。)。

また、上記の設定をした上で学校外のアカウントで本ツールを使用しようとした場合、下記のどちらかが発生します。
- 学校が学校外のアカウントとのドキュメント等の共有を禁止している場合
- 学校外のアカウントと共有できなかったというエラー通知が先生に飛ぶ
- 学校が学校外のアカウントとのドキュメント等の共有を禁止していない場合
- 学校外のアカウントと共有できてしまう
上記のような事態を回避するのであれば、追加で次項の設定を追加してください。
なお、学校外のアカウントで本ツールを使用しようしても、特別なエラーが発生しない限り児童・生徒がフォームから送信した振り返り内容が消えるようなことはありません。
3. 学校アカウントでのログインを必須にする
フォームの設定タブから 「[学校や自治体名] と信頼できる組織のユーザーに限定する」を ON にします。こうすることで、振り返りフォームに回答するためには学校アカウントでログインすることを強制できます(公開授業や研修等で、外部の方(学校外のアカウント)に利用していただく際には本設定を OFF にする必要があります)。
なお、この設定は個人アカウントでは表示されませんので、検証等で試してみたい方はご注意ください。
![フォームの設定タブから、「[学校や自治体名] と信頼できる組織のユーザーに限定する」を ON にする](https://alfnets.info/wp-content/uploads/dbe12fee004c147518c6ef643d806f61-1-1024x796.jpg)
なお、これらの設定は、BYODであったり自宅のPCから本ツールを利用しようとした際に、学校のアカウントに切り替える手間が発生するかもしれないという点がデメリットとなります。しかし、これまでのお問い合わせ内容からも、メールアドレスの誤入力を防ぐ方がメリットが大きいと思われます。
コードの公開
通常版
/*
gas-reactionform2gsheet
Version 2.2.3
2023/05/30
Copyright (c) 2023 alf
Released under the MIT license
*/
// フォルダIDとテンプレートファイル名を定義(指定)
const FOLDER_ID = 'スプレッドシートを保存したいフォルダIDを入力してください'; // 振り返りのスプレッドシートを保存したいフォルダのID
const TEMPLATE_FILE_NAME = "template_classSelfFeedbackv2.0"; // テンプレートファイルは8行目のフォルダ内に格納すること
const maxRetryCount = 5; // 最大リトライ回数
const retryInterval = 2000; // リトライ間隔(ミリ秒)
function sendSpredsheet(event) {
// フォームからの送信データ取得
const emailaddress = retryWithLimit(() => event.response.getRespondentEmail());
const formResponses = retryWithLimit(() => event.response.getItemResponses());
const timeStamp = retryWithLimit(() => event.response.getTimestamp());
const formId = retryWithLimit(() => event.source.getId());
// フォームの回答結果の整理
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("");
}
// 操作フォルダーとファイル一覧の取得等
let forder;
try {
folder = DriveApp.getFolderById(FOLDER_ID); // 操作フォルダー
} catch (e) {
throw new Error(`フォルダーが見つかりませんでした。本プログラムで定義されているFOLDER_IDが正しいものか確認してください。もしくは、DriveAppを使ったツールがドメイン管理者(システム管理者)によって無効化されています。\n詳細: ${e.message}`);
}
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;
}
}
// スプレッドシートファイルの作成
if (check === 0) { // ファイルがなければテンプレートファイルから複製してファイルを作成する
const templateFiles = DriveApp.getFolderById(FOLDER_ID).getFilesByName(TEMPLATE_FILE_NAME); // テンプレートファイルをIteratorオブジェクトとして取得
let templateFile;
try {
templateFile = templateFiles.next(); // .next()でIteratorオブジェクトからtemplateファイルを取得
} catch (e) {
throw new Error(`テンプレートファイルが見つかりませんでした。テンプレートファイルがFOLDER_IDのフォルダに保存されていることと、本プログラムで定義されているTEMPLATE_FILE_NAMEとテンプレートファイル名が一致していること、Excelファイルではなくスプレッドシートであることを確認してください。\n詳細: ${e.message}`);
}
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('提出されたファイルに共有設定をすることができませんでした。')
}
}
// スプレッドシートの閲覧権限の付与
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);
}
}
// 個別のスプレッドシートへのリンクを回答一覧のスプレッドシートに追記
addIndividualSsLinkToAnswerSs(targetFile, formId, timeStamp, studentNumber);
}
/* 個別のスプレッドシートへのリンクを回答一覧のスプレッドシートに追記 */
function addIndividualSsLinkToAnswerSs(targetFile, formId, timeStamp, studentNumber) {
const downloadUrl = targetFile.getUrl(); // 個別のスプレッドシートのリンクを取得
const answerSheet = retryWithLimit(() => getSheet(formId));
let retryCount = 0;
let timeStamps, studentNumbers, targetRow, lastColumn;
while (!targetRow && retryCount < maxRetryCount) {
retryCount++;
Utilities.sleep(retryInterval);
timeStamps = answerSheet.getRange("A:A").getValues(); // A列(タイムスタンプ)の値を配列で取得
studentNumbers = answerSheet.getRange("C:C").getValues(); // C列(学籍番号)の値を配列で取得
targetRow = getTargetRow(timeStamps, studentNumbers, timeStamp, studentNumber) // 回答を記録した行を取得
}
if (!targetRow) {
throw new Error(`回答一覧のスプレッドシートに ${studentNumber} さんの回答が見つからなかったため、個別のスプレッドシートへのリンクを回答一覧のスプレッドシートに追記できませんでした。`)
} else {
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を追加しておく必要がある)
let answerSs;
try {
answerSs = SpreadsheetApp.openById(form.getDestinationId()); // フォームとリンクしている回答スプレッドシートを取得
} catch (e) {
throw new Error(`フォームとリンクしている回答先スプレッドシートが見つかりませんでした。フォームの回答タブから回答先スプレッドシートを作成・もしくは既存のものを選択してください。\n詳細: ${e.message}`);
}
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);
}
/* リトライ処理用関数 */
function retryWithLimit(func) {
let retryCount = 0;
let result;
while (retryCount < maxRetryCount) {
try {
result = func();
if (!result) {
throw new Error("データ取得に失敗しました。");
}
break;
} catch (e) {
retryCount++;
console.error(`リトライします: ${retryCount}回目(データ取得に失敗しました: ${e.message})`);
Utilities.sleep(retryInterval);
}
}
if (!result) {
throw new Error("データ取得の最大リトライ回数を超えました。処理を終了します。");
}
return result;
}
light版(自由記述を統合したバージョン)
/*
gas-reactionform2gsheet_light
Version 2.2.3
2023/05/30
Copyright (c) 2023 alf
Released under the MIT license
*/
// フォルダIDとテンプレートファイル名を定義(指定)
const FOLDER_ID = 'スプレッドシートを保存したいフォルダIDを入力してください'; // 振り返りのスプレッドシートを保存したいフォルダのID
const TEMPLATE_FILE_NAME = "template_classSelfFeedbackv2.0_light"; // テンプレートファイルは8行目のフォルダ内に格納すること
const maxRetryCount = 5; // 最大リトライ回数
const retryInterval = 2000; // リトライ間隔(ミリ秒)
function sendSpredsheet(event) {
// フォームからの送信データ取得
const emailaddress = retryWithLimit(() => event.response.getRespondentEmail());
const formResponses = retryWithLimit(() => event.response.getItemResponses());
const timeStamp = retryWithLimit(() => event.response.getTimestamp());
const formId = retryWithLimit(() => event.source.getId());
// フォームの回答結果の整理
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("");
}
// 操作フォルダーとファイル一覧の取得等
let forder;
try {
folder = DriveApp.getFolderById(FOLDER_ID); // 操作フォルダー
} catch (e) {
throw new Error(`フォルダーが見つかりませんでした。本プログラムで定義されているFOLDER_IDが正しいものか確認してください。もしくは、DriveAppを使ったツールがドメイン管理者(システム管理者)によって無効化されています。\n詳細: ${e.message}`);
}
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;
}
}
// スプレッドシートファイルの作成
if (check === 0) { // ファイルがなければテンプレートファイルから複製してファイルを作成する
const templateFiles = DriveApp.getFolderById(FOLDER_ID).getFilesByName(TEMPLATE_FILE_NAME); // テンプレートファイルをIteratorオブジェクトとして取得
let templateFile;
try {
templateFile = templateFiles.next(); // .next()でIteratorオブジェクトからtemplateファイルを取得
} catch (e) {
throw new Error(`テンプレートファイルが見つかりませんでした。テンプレートファイルがFOLDER_IDのフォルダに保存されていることと、本プログラムで定義されているTEMPLATE_FILE_NAMEとテンプレートファイル名が一致していること、Excelファイルではなくスプレッドシートであることを確認してください。\n詳細: ${e.message}`);
}
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('提出されたファイルに共有設定をすることができませんでした。')
}
}
// スプレッドシートの閲覧権限の付与
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);
}
}
// 個別のスプレッドシートへのリンクを回答一覧のスプレッドシートに追記
addIndividualSsLinkToAnswerSs(targetFile, formId, timeStamp, studentNumber);
}
/* 個別のスプレッドシートへのリンクを回答一覧のスプレッドシートに追記 */
function addIndividualSsLinkToAnswerSs(targetFile, formId, timeStamp, studentNumber) {
const downloadUrl = targetFile.getUrl(); // 個別のスプレッドシートのリンクを取得
const answerSheet = retryWithLimit(() => getSheet(formId));
let retryCount = 0;
let timeStamps, studentNumbers, targetRow, lastColumn;
while (!targetRow && retryCount < maxRetryCount) {
retryCount++;
Utilities.sleep(retryInterval);
timeStamps = answerSheet.getRange("A:A").getValues(); // A列(タイムスタンプ)の値を配列で取得
studentNumbers = answerSheet.getRange("C:C").getValues(); // C列(学籍番号)の値を配列で取得
targetRow = getTargetRow(timeStamps, studentNumbers, timeStamp, studentNumber) // 回答を記録した行を取得
}
if (!targetRow) {
throw new Error(`回答一覧のスプレッドシートに ${studentNumber} さんの回答が見つからなかったため、個別のスプレッドシートへのリンクを回答一覧のスプレッドシートに追記できませんでした。`)
} else {
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を追加しておく必要がある)
let answerSs;
try {
answerSs = SpreadsheetApp.openById(form.getDestinationId()); // フォームとリンクしている回答スプレッドシートを取得
} catch (e) {
throw new Error(`フォームとリンクしている回答先スプレッドシートが見つかりませんでした。フォームの回答タブから回答先スプレッドシートを作成・もしくは既存のものを選択してください。\n詳細: ${e.message}`);
}
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);
}
/* リトライ処理用関数 */
function retryWithLimit(func) {
let retryCount = 0;
let result;
while (retryCount < maxRetryCount) {
try {
result = func();
if (!result) {
throw new Error("データ取得に失敗しました。");
}
break;
} catch (e) {
retryCount++;
console.error(`リトライします: ${retryCount}回目(データ取得に失敗しました: ${e.message})`);
Utilities.sleep(retryInterval);
}
}
if (!result) {
throw new Error("データ取得の最大リトライ回数を超えました。処理を終了します。");
}
return result;
}
GitHubでもコードを公開しています。ライセンスはMITとなりますのでご自由にお使いください。
情報教育を中心に教育関連のことを発信していますので 他の記事 や Twitter、YouTube もご覧いただけると嬉しいです。また、情報教育教材のまとめサイト も作成しています。教材だけでなく、入試情報や便利ツールの一覧集などのページもありますのでぜひご覧ください。
コメント
こんにちは。とても興味深く拝見させていただきました。
いくつか要望があり、コメントさせていただきます。
私は大阪府立高校の家庭科教諭をしております。
生徒が学習を振り返られるスプレッドシートをフォームの入力で簡単にできるのは素晴らしいと思いました。
できれば、単元ごとにポートフォリオが完成するものをつくりたいと思っており、単元を貫く問いに対して学習する前の考えを記入する欄。
また、単元の学習が終わってから、改めて同じ単元を貫く問いに対する考えや、その考えに至った経
緯や今後の行動や意識についてを記入する欄があると嬉しいと思いました。
セクションを変えて学習前、学習中、学習後などに分けることで、集まるデータをポートフォリオのようなものにすることは可能でしょうか?
また、可能でしたらぜひともそのようなシステムを教えていただきたいです。
コメントありがとうございます!
毎授業ではなく、単元毎に使うのもとても有効な方法ですね。また、学習の前後での変遷を残していくことは私もとても良いことだと思っています。
ミヤタケさんがやりたいことを本ツールで行うのであれば、各回答項目の文言は変更しても問題ありませんので、そちらで十分であればそのようにお使いください。回答項目を増やしたり減らしたりする場合は、GAS(プログラム)をいじらないといけないので、少々プログラミングスキルが必要となります。
「セクションを変えて学習前、学習中、学習後などに分ける」というのは、どうやってデータとして残していくかによります(単元ごとにシートをわけるのかどうするのかなど)。たとえば、一行に一単元の振り返りを並べた場合、その単元での前後の変化は見にくいでしょうし、「学習前、学習中、学習後」で3行にすると、他の単元がその前後に並んだ際にみづらくなるでしょう。そうであれば単元ごとにシートをわけるのが良さそうですが、項目も含めて4行のシートが単元ごとに増えていくのも微妙な気がしています。
つまり、スプレッドシートのような表計算ソフトだと少々厳しいのではないかというのが個人的な考えです。
似たようなことをスライドを用いてやっている先生もいらっしゃいますので、単元ごとにスライドを作成するのも良いと思います(単元での振り返りシート・プリントのイメージ)。
システム化しようとするのであればWebシステムで作成するのも良さそうですが、今は仕事の片手間で行っている活動ですので、私一人では時間的にもスキル的にも厳しいというのが現状です。
回答になっていますでしょうか。ミヤタケさんのお力に少しでもなれば幸いです。