0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GAS×Discord】英単語テストを自動作成・通知するシステムを作ってみた⑦

0
Last updated at Posted at 2025-12-08

この記事は、GAS(Google Apps Script)とDiscordを連携させた英単語テスト自動作成システムの開発記録・第7回です。

前回の記事では、saiten 関数の実装②について説明し、基本機能の実装を終えました。

今回は、新しく「苦手問題全問テスト」機能の実装を始めていきます。

make_nigate関数の流れ

以下のような順序で処理を実行していく関数を作る。

  • 「苦手問題」シートを一度空にする
  • 苦手問題の定義を決めておき、それに適する問題を選び出す
  • 苦手問題がなかったときに中断する
  • フォームを作成
  • 先ほど選んだ問題をフォームに設定する
  • Discordに作成した英単語テストのURLを送信

make_nigate関数作成①

まず関数make_nigateを定義する。

function make_nigate() {

}

スプレッドシートの準備

「苦手問題」シートは未編集だったので、以下のように変更する。

image.png

このシートには、1つ前の苦手問題全問テストの回答データを記録する。
苦手問題のみ別途復習しやすくするため、このようにしている。

スプレッドシートなどの取得

make_nigate関数内で使うスプレ類を取得しておく。

//スプレッドシート取得
const ss = SpreadsheetApp.getActiveSpreadsheet();


//スプレッドシートのシート取得
const questionSheet = ss.getSheetByName("テストに出す英単語用");
const answerSheet = ss.getSheetByName("回答収集用");
const weakSheet = ss.getSheetByName("苦手問題");
const urlSheet = ss.getSheetByName("フォームのリンクと結果");


//「苦手問題」シートの最終列・行を取得
const weakLastRow = weakSheet.getLastRow();
const weakLastCol = weakSheet.getLastColumn();


//「回答収集用」シート1列目の最後の行を取得
const answerLastRow = answerSheet.getRange(answerSheet.getMaxRows(), 1).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();

//「回答収集用」シート10列目の最後の行を取得
const lastRowJ = answerSheet.getRange(1, 10).getDataRegion(SpreadsheetApp.Dimension.ROWS).getLastRow();
  • 「苦手問題」シートの最終列・行は、1行目以外のデータを削除する際に使用
  • 「回答収集用」シートの10列目の最終行は、正答率を順に見ていく際に使用

「苦手問題」シートを初期化

//最初の行以外をクリア
if (weakLastRow > 1) {
  weakSheet.getRange(2, 1, weakLastRow - 1, weakLastCol).clearContent();
}

前回のテストの回答が記録されているため、削除する。

苦手問題を抽出

苦手問題として出題する問題を選んでいく。今回は正答率が50%未満かつ出題回数が5回以上である問題を苦手問題に選んだ。苦手問題の基準を変えたいときは、If文の条件を変えれば良い。

//「回答収集用」シートの正答率などが記録されている範囲の情報を取得
const accuracyRange = answerSheet.getRange(2, 10, lastRowJ, 3);
const accuracyData = accuracyRange.getValues();

//苦手問題を入れておく配列
const weakQuestions = [];

//一個ずつ苦手問題かどうか見ていく
for (let i = 0; i < accuracyData.length; i++) {
  //正答率
  const accuracy = accuracyData[i][1];
  //出題数
  const count = accuracyData[i][2];
  //正答率が50%未満かつ出題回数が5回以上のものを苦手問題に追加
  if (accuracy < 0.5 && count >= 5) {
    weakQuestions.push(questionSheet.getRange(i + 2, 1, 1, 5).getValues().flat());
  }
}

コード補足

weakQuestions.push(questionSheet.getRange(i + 2, 1, 1, 5).getValues().flat());

この部分が複雑なため説明していく。

  • .getRnage().getValues()を使用し、「テストに出す英単語用」シートから苦手問題の問題情報(type、正解など)を得る
  • .flat()で二次元配列を一次元配列に直す(.getValues()で取得しているため元は二次元配列)
  • .push()weakQuestionsに追加

苦手問題がなかった時の中断処理

苦手問題に該当する問題が0だった場合、そのままフォームの作成などに取りかかると以下のようなエラーが出る。
image.png

そのため、もしweakQuestionsが空だったらポップアップウィンドウが表示され、処理が中断されるようにする。

if (weakQuestions.length === 0) {
  Browser.msgBox("苦手な問題がありません。OKを押して閉じてください。", Browser.Buttons.OK);
  return;
}

この処理を書いておくことで、苦手問題がない場合も以下のようになり、エラーが出ない。

image.png

スプレッドシートに記録

「苦手問題」のシートに苦手問題として抽出されたものを記録しておく。
こうすることで苦手問題のみ復習したり、振り返ったりすることが容易になる。

weakSheet.getRange(2, 1, weakQuestions.length, 5).setValues(weakQuestions);

フォーム作成

make_test関数と同じ流れでフォームを作成していく。

const formTitle = Utilities.formatDate(new Date(), "JST", "yyyy/MM/dd_hh:mm:ss_英単語テスト・苦手問題すべて");
const form = FormApp.create(formTitle);

form.setIsQuiz(true);
form.setLimitOneResponsePerUser(true);
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());

//フォームの説明文を追加
form.setDescription("苦手全問テストだよ!");

//フォームIDを取得して保存
const formId = form.getId();
PropertiesService.getScriptProperties().setProperty('FORM_ID', formId);
  • フォームタイトル設定&フォーム作成
  • 各種フォームの設定(クイズモードON・回答回数1回に制限・スプレッドシートと連携)
  • フォームの説明文として「苦手全問テストだよ!」という文章を追加(saiten関数で利用するため)

フォームに問題追加

//問題番号を保存する配列
const questionOrder = [];


//苦手問題として抽出した問題をフォームに追加していく
for (let j = 0; j < weakQuestions.length; j++) {
  const [title, type, required, correct, number] = weakQuestions[j];
  createItem(form, title, type, required);
  questionOrder.push(number);
  const range = answerSheet.getRange(answerLastRow + j + 1, 1, 1, 2);
  range.setValues([[correct, number]]);
}

//問題番号を保存
PropertiesService.getScriptProperties().setProperty('PROBLEM_NUMBER', JSON.stringify(questionOrder));

ここでは、「抽出した苦手問題をフォームに追加し、後で参照するために問題番号を保存する」 処理を行っている。

コード解説(for文)

1. weakQuestionsの情報を受け取る

const [title, type, required, correct, number] = weakQuestions[j];

ここでは、要素が多くそれぞれ変数を宣言して代入するのが大変なので、分裂代入を利用している。
weakQuestions1行には5つの要素が入っているので、それらを各変数title, type, required, correct, numberに割り当てている。

※行ごとに要素数が異なる場合は、今回のように分裂代入しようとするとエラーが出るので注意。

2. フォームに問題追加

createItem(form, title, type, required);
  • make_test関数でも使用したcreateItemを使う。

3. 問題番号保存

questionOrder.push(number);

4. 回答収集用シートに記録

const range = answerSheet.getRange(answerLastRow + j + 1, 1, 1, 2);
range.setValues([[correct, number]]);

setValuesするときは1行でも2次元配列とする点に注意

DiscordでフォームのURLを送信

この部分のコードはmake_testと同じなので、説明は割愛する。

//フォームURLを取得してスプレッドシートの「フォームのリンクと結果」シートに書き込む
const formUrl = form.getPublishedUrl();
urlSheet.getRange(1, 2, 2, 1).setValues([[formUrl], [""]]);

//フォームのURLをDiscordに送信
const payload = { "content": formUrl };
const options = { "method": "post", "payload": payload };
UrlFetchApp.fetch(WEBHOOK_URL, options);

さいごに

今回はmake_nigate関数の実装について解説しました。

次回はこの関数の挙動確認と、補助機能の追加をしていきます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?