【イベントレポート】ローコードでここまでできる！Google Apps Script × 生成AI #RPALT

いつも記事を読んでいただきありがとうございます！
モブエンジニア（[@mob-engineer](https://qiita.com/mob_engineer))です！

今回は2025.03.31（月）に開催した**ローコードでここまでできる！Google Apps Script × 生成AI**へ参加しましたので、アウトプットとしてイベントレポートを執筆しました。

初見の方でもサクッと読めるように平易な表現で執筆しておりますので、お気軽に読んでいただければ幸いです。

:::note info
誤字脱字、わかりづらい表現に関しては極力なくすように心がけていますが、リアルタイムで執筆しておりますので、誤字脱字があるかもしれません。
:::

#### イベントページ

https://rpacommunity.connpass.com/event/349621/

# 目次
- Google Apps Script（GAS）と生成AIの基本的な知識
- GAS開発に生成AIを活用する場合の注意点
- GASコードを自動生成するツールのご紹介

#### （参考）Google Apps Script（GAS）

https://developers.google.com/apps-script?hl=ja

## Google Apps Script（GAS）と生成AIの基本的な知識
- 前提条件
    - GASについて改めて学びなおす目的で開催している
- 自己紹介
    - 吉積情報所属の副社長の方
    - 経営側の業務が中心だが開発も行っている
- なぜ、GASを学ぶ必要があるのか
    - 企業活動の活用するといった目的がある
    - （個人的考察）企業活動に絡めるのはおっしゃる通り
    - あくまで、[美しいコードを執筆する]ではなく[業務改善を行う]といったことがある
- GASを学ぶメリット
    - 個人視点：繰り返し作業を泥臭くやること自体はスマートではない
    - 効率化を通じて得た時間を価値最大化に使うことが大切
        - （個人的考察）うなずく内容だなぁ
- GASが企業のDX推進に必要な理由
    - 労働人口が減少している・労働生産性が他国と比較して低い
    - （個人的考察）AI利活用でも同じ話が言えそう
    - マッキンゼーレポートによると労働生産性を2.5倍に上げないと成長率を維持できない
    - 日本の労働時間を見ると大半が反復可能な作業だった
- GASについて
    - Googlサービスをプログラムから操作可能
    - JavaScriptベースのため学習コストをそこまでかけなくていい
        - （個人的考察）JavaScriptベースであればTypeScript（IaC）へも転用可能ですな
    - ブラウザオブジェクトに関しては操作できない
    - 開発するための環境として**Googleアカウント＋ブラウザ＋ネットワーク**がある
    - GASの種類
        - スタンドアロン型：ドライブからGASエディタを開く場合
            - ざっくり言えば、単独で実行できるプログラム
        - コンテナバインド：特定ファイルに紐づいたスクリプトを開く場合
            - ざっくり言えば、パラメータシートなどの特定用途で利用するファイル
- GASの応用的な利用方法
    - Webアプリケーション開発
    - 外部APIとの連携
    - アドオンによる組織内での連携
        - （個人的考察）アドオンで連携できるのであれば開発に関して知見がない方でもうまく処理できそうですね

## GAS開発に生成AIを活用する場合の注意点

- GASと生成AIの利用事例
    - Geminiを利用することでセットアップが楽になる
    - （個人的考察）やはり生成AIを利用するのがベターですよね
- 生成AIを利用することのリスク
    - コードの正確性・完全性
        - AIが生成するコードが常に正しいとは限らない
        - 文法的な誤りがあったりする
        - 人間によるレビューが必須

## GASコードを自動生成するツールのご紹介
- GASpark
    - GAS開発を促進するアプリケーション
    - 生産性向上させるためのAIアプリケーション
- 全体のまとめ
    - 日本企業の生産性向上を上げる
    - GASを利用することで単純作業はいい感じで自動化できる

## 質疑応答
- 生成AIによる補完機能はあるのか？
    - 補完機能ついてはない
- ジェミニとジェミナイどちらが正しいのか？
    - Googleだとジェミナイと言ってるのが多い
- 公式でテンプレートのようなものは出ているのか
    - 有志からは出ているが公式からは出ていないと思う

## まとめ

GASについて知らなかったので、本セッションを通じてどのようなメリットがあるのか理解することができました。また、LT中に簡単なアプリケーションも作成できたので、今後の技術キャッチアップとして使ってみたいと思いました。

#### 余談

LT中に作成した天気予報アプリのコードを作成したので共有します。
※APIキーを追記すればうまくスプレッドシートへ反映できるかと思います。

:::note info
あらかじめ、OpenWeatherでAPIキーを取得してください。
https://openweathermap.org/
::::

:::note alert
挙動については一応チェックしていますが、利用に関しては自己責任でお願いします。
:::

:::note info
（2025.04.01）
ハードコーディングしない方式に修正しました。
スクリプトプロパティへAPI情報を登録することで情報取得可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/fa60e215-83bd-4b73-aeec-0914a279f25a.png)
:::

```JavaScript
function getWeather() {
  // OpenWeatherMap APIキーをスクリプトプロパティから取得
  var apiKey = PropertiesService.getScriptProperties().getProperty('OPENWEATHERMAP_API_KEY');
  if (!apiKey) {
    Logger.log("APIキーが設定されていません。");
    return;
  }

  // 都道府県リスト（都道府県庁所在地を追加）
  var prefectures = [
    '札幌市,北海道', '青森市,青森県', '盛岡市,岩手県', '仙台市,宮城県', '秋田市,秋田県', '山形市,山形県', '福島市,福島県',
    '水戸市,茨城県', '宇都宮市,栃木県', '前橋市,群馬県', 'さいたま市,埼玉県', '千葉市,千葉県', '新宿区,東京都', '横浜市,神奈川県',
    '新潟市,新潟県', '富山市,富山県', '金沢市,石川県', '福井市,福井県', '甲府市,山梨県', '長野市,長野県', '岐阜市,岐阜県',
    '静岡市,静岡県', '名古屋市,愛知県', '津市,三重県', '大津市,滋賀県', '京都市,京都府', '大阪市,大阪府', '神戸市,兵庫県',
    '奈良市,奈良県', '和歌山市,和歌山県', '鳥取市,鳥取県', '松江市,島根県', '岡山市,岡山県', '広島市,広島県', '山口市,山口県',
    '徳島市,徳島県', '高松市,香川県', '松山市,愛媛県', '高知市,高知県', '福岡市,福岡県', '佐賀市,佐賀県', '長崎市,長崎県',
    '熊本市,熊本県', '大分市,大分県', '宮崎市,宮崎県', '鹿児島市,鹿児島県', '那覇市,沖縄県'
  ];

  // スプレッドシートの取得
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();

  // ヘッダー行の追加
  sheet.appendRow(['都市名', '天気', '気温', '湿度']);

  // 各都市の天気情報を取得してスプレッドシートに出力
  for (var i = 0; i < prefectures.length; i++) {
    var city = prefectures[i];
    var encodedCity = encodeURIComponent(city); //都市名をエンコード
    var apiUrl = 'https://api.openweathermap.org/data/2.5/weather?q=' + encodedCity + '&appid=' + apiKey + '&lang=ja&units=metric';

    try {
      var response = UrlFetchApp.fetch(apiUrl, {muteHttpExceptions: true}); //エラーレスポンスの詳細を取得
      var json = JSON.parse(response.getContentText());

      if (json.cod == 200) {
        var weather = json.weather[0].description;
        var temp = json.main.temp;
        var humidity = json.main.humidity;
        sheet.appendRow([city, weather, temp, humidity]);
      } else {
        sheet.appendRow([city, '取得失敗: ' + json.message, '', '']); //エラーメッセージも出力
      }
    } catch (e) {
      sheet.appendRow([city, 'エラー: ' + e.message, '', '']); //エラーメッセージも出力
    }

    //APIの無料版は1分間に60回までなので、1秒待機する。
    Utilities.sleep(1000);
  }
}
```

取得結果として以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/2eb5dd91-697a-45de-a0c0-0f89d0353478.png)