Google App ScriptとGoogle Sheetで英語学習の環境を作った

## はじめに
英語を勉強しなさいと言われて生きてきましたが正直どうやっていいのかわからんなと思い生きてます。

英語の問題集を解くときに適当な紙に回答だけ書いて終わったら回答の答え合わせをするようなやり方をやっていましたがどうにも
繰り返し回答を埋めて、間違えた問題の解説を読み返し、手作業で採点するのも非常に面倒になってきました。

人の手を極力介さず、問題を沢山解きたいという願いを叶えるには、紙ではなくキーボードでやるべきという結論に達しました。
願わくば外出先でもやりたいので、PC、スマホ、iPadから使えるGoogle Sheetで使う方法について考えてきました。

## 問題の自動採点
スマホと問題集があればとりあえず繰り返し解けるようにしたいなと思って、このやり方を試しました。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-46ccee45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/d383cb39-ffb2-ba4c-7598-5672dac7271a.png)


正答率がわかってしまいますがこんな感じになってます。

1回目の答え合わせする際に、正解列を埋めておけば繰り返せるようにしておきます。

あとは、簡単な数式を正否の所に埋めてやることで問題を解いたら即答え合わせができるようにしています。

```
=IF(B2=C2,"○","×")
```

あとは、試行回数を増やすたびに数式をコピーして少し手を加えれば何度でも使い回すことが出来る環境ができました。
最初の準備は慣れれば数秒で終わるのでその手間さえ惜しまなければいいんでないでしょうか。

## 英単語の記録
改めてやり始めると、恥ずかしながらわからん単語が結構あるのでそれを記録していきます。
高校生の頃は、わからん単語で単語帳を作って裏表に日本語と英語を書いたものですが、Google Sheetを使うと
英語だけ打ち込めば日本語も得られます。これは地味に便利で気にいいってます。

問題集を解いて、少しでも迷ったらとりあえず単語を埋めていきました。
後半は複数単語でもちゃんと変換されるのかなとか試してましたが、結構行けそうです。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-5579083a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/4e4ffefe-535e-5d8b-7b0b-4a5670212255.png)


C列にはこの数式を入れています。
```
=GOOGLETRANSLATE(A3)
```

なんとなく察しが付くと思いますが、Google翻訳がGoogle Sheetから使えます。
これで英単語を入力すれば、日本語訳が得られます。

ココまでやると、品詞の情報も欲しくなってきました。

## 英単語の品詞の取得
Google App Script使って、品詞を取得していきましょう。

形態素解析すれば、品詞も得られるはずなのでそれをやっていきます。
Google Sheet単体ではそこまではできないため、ココからはGASを使っていきます。

### GAS Hello World
まずはHello WorldとしてGASから書き込みを出来ることを確認します。
関数はこんな感じにしました。

```
function myFunction() {
  var s = SpreadsheetApp.getActiveSheet();
  var cell = s.getRange(2, 2);
  cell.setValue("Hello World");
}
```

実行前

![2022-05-10-google-app-scriptで英語学習の環境を作った話-8502664b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/313806d9-bfeb-802e-5b1d-a89d799daa85.png)

実行後

![2022-05-10-google-app-scriptで英語学習の環境を作った話-8f6f2e6a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/2850a57e-975b-dbb3-63bb-a105ba46a8a9.png)


### cellが更新されたら自動発火するようにする
続いて、単語が入力されたら自動で関数を叩いて値取得出来るようにしました。
単語入力するたびに、実行するのは大変ですからね。

onEdit(e)という名称で関数を作ります。
getActiveCell()で選択されているCellを取得して、その値をgetValue()で取得します。

どこのCellを更新して発火しても困るので、if文でＢ列の更新に絞っています。

```
function onEdit(e) {
  var s = SpreadsheetApp.getActiveSheet();
  var cell = s.getActiveCell();
  var g = cell.getValue();

  if(cell.getColumn()==2){ //B列が更新されたらTriggerをかける
    cell.setValue(g + "更新しました");
  }
}
```

#### 実行結果
B列を更新したら「更新しました」と付与しています。
動画にしたのでYoutubeにあげておきました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/4xYRHRWCH6U" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


### Google Cloud Platform を使った形態素解析

#### Cloud Natural LanguageをGASから呼び出す

[Google Cloud](https://cloud.google.com/) を開いて登録します。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-ce64ef68.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/9f508700-f693-ed4b-4942-a4b56b32079e.png)


https://cloud.google.com/natural-language/docs

Cloud Natural Language APIを有効にしておきます。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-a703341f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/1433d5e6-2536-507f-8f5f-0afcc4ab47f3.png)



#### プロジェクトの作成

[DASHBOARD](https://console.cloud.google.com/home/dashboard?)
こちらから、新規でプロジェクトを作ります。
認証情報を左メニューから選びAPI KEYを作成します。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-6403b7aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/4286b6ee-bcaa-95e9-118f-c29e9ea48aed.png)


認証情報を作成をクリックして準備完了です。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-181548fb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/a94a4f8f-923b-05db-05c3-d5dfbead494f.png)


API キーが作れますのでその値をコピーします。

### Google App Scriptで品詞取得関数を作成する
Google SheetからApps Scriptを開きます。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-7af35d57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/62565985-b352-7312-d23d-963b2488921d.png)


コード編集画面に到達したら準備完了です。

#### コード全文
せっかちな人のためにひとまず動かしているコードはこんな感じです。
シート名を単語に設定して、A列に英単語を入れたらB列に表示されるようになっています。

API_KEYの所に、取得したキーを入れます。
今は適当なやつ入れてます。

```
// Google Cloud Platform API KEY
var API_KEY = "APIajhasjkfhajbscjkasjhgfajkhfakjh";

function onChange() {
  Logger.log("LoggerLog start");
  var s = SpreadsheetApp.getActiveSheet();
  var cell = s.getActiveCell();
  var g = cell.getValue();

  Logger.log(cell);
  Logger.log(g);
  Logger.log(s.getName());

  if (s.getName() === '単語' && cell.getColumn() === 1) {
      var cells = s.getRange(cell.getRow(),2);
      cells.setValue(analyzeText(g));
  }
}

function replacePartOfSpeech(str){
  console.log(str);

  var data = {
        "NOUN": "名詞(一般名詞 or 固有名詞)",
        "VERB": "動詞",
        "ADJ": "形容詞",
        "ADV": "副詞",
        "PRON": "代名詞",
        "CONJ": "接続詞",
        "DET": "限定詞",
        "ADP": "接置詞",
        "UNKNOWN": "不明",
        "NUM": "数詞",
        "PRT": "接辞",
        "AFFIX": "接辞",
        "PUNCT": "句読点",
        "X": "その他",
  }

console.log(data[str]);
return data[str]
}

function analyzeText(g) {
  var text = String(g);
  var url = ['https://language.googleapis.com//v1/documents:analyzeSyntax?key=', API_KEY].join(
    ''
  );

  var data = {
    document: {
      language: 'en',
      type: 'PLAIN_TEXT',
      content: text,
    },
    encodingType: 'UTF8',
  };

  var params  = {
    contentType: 'application/json',
    method: 'post',
    payload: JSON.stringify(data),
  };

  Logger.log(text);

  var response = UrlFetchApp.fetch(url, params);
  Logger.log(response);

  var data = JSON.parse(response);
  Logger.log(data);

  var tag = data["tokens"][0]["partOfSpeech"]["tag"]
  Logger.log(tag);

  return replacePartOfSpeech(tag);
}
```


### onChange()関数
Eventを受けて動く関数です。
シートと、セルを指定して動くようになっています。

```
function onChange() {
  Logger.log("LoggerLog start");
  var s = SpreadsheetApp.getActiveSheet();
  var cell = s.getActiveCell();
  var g = cell.getValue();

  Logger.log(cell);
  Logger.log(g);
  Logger.log(s.getName());

  if (s.getName() === '単語' && cell.getColumn() === 1) {
      var cells = s.getRange(cell.getRow(),2);
      cells.setValue(analyzeText(g));
  }
}
```

### function analyzeText(g)関数
WEB APIを叩いています。
var dataの中のcententにテキストを渡して、UrlFetchAppでPostしてJSONを取得しています。
今回欲しいのはTAG情報なのでこのように取得しています。
```
var tag = data["tokens"][0]["partOfSpeech"]["tag"]
```

コード全文はこんな感じです。
```
function analyzeText(g) {
  var text = String(g);
  var url = ['https://language.googleapis.com//v1/documents:analyzeSyntax?key=', API_KEY].join(
    ''
  );

  var data = {
    document: {
      language: 'en',
      type: 'PLAIN_TEXT',
      content: text,
    },
    encodingType: 'UTF8',
  };

  var params  = {
    contentType: 'application/json',
    method: 'post',
    payload: JSON.stringify(data),
  };

  Logger.log(text);

  var response = UrlFetchApp.fetch(url, params);
  Logger.log(response);

  var data = JSON.parse(response);
  Logger.log(data);

  var tag = data["tokens"][0]["partOfSpeech"]["tag"]
  Logger.log(tag);

  return replacePartOfSpeech(tag);
}
```

### replacePartOfSpeech関数
受け取ったものの、名詞や動詞と英語で言われてもピンと来ないので、置換するための関数を用意しました。

```
function replacePartOfSpeech(str){
  console.log(str);

  var data = {
        "NOUN": "名詞(一般名詞 or 固有名詞)",
        "VERB": "動詞",
        "ADJ": "形容詞",
        "ADV": "副詞",
        "PRON": "代名詞",
        "CONJ": "接続詞",
        "DET": "限定詞",
        "ADP": "接置詞",
        "UNKNOWN": "不明",
        "NUM": "数詞",
        "PRT": "接辞",
        "AFFIX": "接辞",
        "PUNCT": "句読点",
        "X": "その他",
  }

console.log(data[str]);
return data[str]
}
```

### ハマりポイント

#### UrlFetchApp.fetchを呼び出す権限がありません
ログの見方全然わからずハマりました。

基本ですが画像のように、左のメニューから実行結果のログを一覧で見ることができます。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-c222cd60.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/d2b16ae3-7c1d-6891-74d0-4004bf5c23c3.png)


```
2022/05/13 0:51:50	エラー	Exception: UrlFetchApp.fetch を呼び出す権限がありません。必要な権限: https://www.googleapis.com/auth/script.external_request
    at analyzeText(コード:73:30)
    at onEdit(コード:18:22)
```

このエラーはマニフェストファイルを編集することで解決できます。
マニフェストファイルは左メニューの歯車から選んで、エディタに表示するようにしたら見れます。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-342405ee.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/03b84760-56ed-2b33-837d-642528454fbc.png)


チェックを入れると、appsscript.jsonが追加されているので、下記のように修正します。

```
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "MYSELF"
  },
   "oauthScopes": [
   "https://www.googleapis.com/auth/script.external_request"
  ]
}
```

ちなみに、onEdit()関数がよくTriggerに使われるのですが、UrlFetchAppが動かないので自前でトリガーを作る必要があります。

#### トリガー通知の作成
左メニューから時計マークを開いて、トリガーを作ります。
画像のように作ればいいです。

![2022-05-10-google-app-scriptで英語学習の環境を作った話-b5809fb5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76909/a25d7eaa-c2e5-7d5e-056c-838ffa43dc37.png)


#### 実行結果の確認
ここまで作れば動くものができているはずです。

<iframe width="469" height="834" src="https://www.youtube.com/embed/MYv88mTLR3I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## まとめ
英語学習する上で、品詞が大切と聞きました。
文法を少しかじって、ここに形容詞が入るんだろうなぁって思ったとしてもどれが形容詞だ？ってなるのは自明なので、どこまで正確なのかはさておき　おおよそでも品詞が分かると便利だろうなと思い用意しました。

月5000回は無料でAPI叩けますが、真面目にやり始めると足りないかもですね。

[心のデブを信じろ](https://kenpos.dev/)　というブログに同じ話を書きましたので良ければアクセスしてください。