4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

式の余興に「AWS+GASシステム」で超盛り上がった件 〜②GAS編〜

Posted at

式の余興に「AWS+GASシステム」で超盛り上がった件 〜①画面編〜の続きになります。
今回の記事は主に、バックエンド周り(GAS)の実装に焦点を当てています。
システム構成図-GAS.png

:large_blue_diamond: Google App Script

このシステムのメイン機能はすべてGASが担当しています。
無料でここまでできるなんて、すごいぞGoogle!:hugging:

サンプルコード

※以下のプログラム内の様々なIDはXXXXXXXXXXXXXに置き換えています

Gas-smileys-main
/** 画像格納フォルダID */
const FOLDER_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
/** スプレッドシートID */
const SHEET_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const A = 'A';
const B = 'B';
const C = 'C';
const D = 'D';

/** WebアプリケーションへのGETリクエストを処理する */
function doGet(e) {
    // HTMLテンプレートファイルを読み込み
    const template = HtmlService.createTemplateFromFile('src/Index');
    // HTMLを評価して出力する
    const htmlOutput = template.evaluate();
    // iFrame埋め込みを許可
    htmlOutput.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
    // メタタグの追加(レスポンシブデザイン対応)
    htmlOutput.addMetaTag('viewport', 'width=device-width, initial-scale=1');
    htmlOutput.addMetaTag('apple-mobile-web-app-capable', 'yes');
    htmlOutput.addMetaTag('mobile-web-app-capable', 'yes');
    return htmlOutput;
}

/** WebアプリケーションへのPOSTリクエストを処理する */
function doPost(e) {
    // 画像が選択されているかチェック
    if (e.parameters.image == '') {
        throw 'ファイルが選択されていません。';
    }
    // 画像データのデコード
    const imageData = Utilities.base64Decode(e.parameter.himage, Utilities.Charset.UTF_8);
    // Blobとしてファイルを作成
    const fileBlob = Utilities.newBlob(imageData, 'image/jpg', e.parameter.image);
    // 画像ファイルをドライブにアップロード
    const file = DriveApp.getFolderById(FOLDER_ID).createFile(fileBlob);
    // スプレッドシートに画像と日付を追加
    createSheet();
    // AIサーバに画像を送信し、データをシートに挿入
    insertToSheet(file, fileBlob);
    return;
}

/** スプレッドシートへのデータ挿入処理 */
function insertToSheet(file, blob) {
    // エンドポイントURLの取得
    const url = getEndpoint();
    // 現在の日付の取得
    const today = getDate();
    // スプレッドシートの該当シートを取得
    const mySheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(today);
    mySheet.activate();
    // シートの最終行を取得
    const lastRow = mySheet.getLastRow() + 1;
    // 日時、ファイルURL、ファイルの公開URL、笑顔指数をシートに記録
    mySheet.getRange(A + lastRow).setValue(getDateTime());
    mySheet.getRange(B + lastRow).setValue(file.getUrl());
    mySheet
        .getRange(C + lastRow)
        .setValue('https://lh3.googleusercontent.com/d/' + file.getId());
    const smile = fetchSmile(blob, url);
    mySheet.getRange(D + lastRow).setValue(smile);
    if (smile != '') {
        const rate = JSON.parse(smile);
        mySheet.getRange('E' + lastRow).setValue(rate[0][2]);
    }
}

/** エンドポイントURLの取得 */
function getEndpoint() {
    const mySheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('設定');
    const endpoint = mySheet.getRange(B + 1).getValue();
    return endpoint;
}

/** エンドポイントへのデータ送信と結果の取得 */
function fetchSmile(blob, url) {
    const params = {
        method: 'post',
        payload: {
            upload_file: blob
        }
    };
    const response = UrlFetchApp.fetch(url, params).getContentText();
    return response;
}

/** 必要に応じてスプレッドシートの新しいシートを作成 */
function createSheet() {
    const today = getDate();
    const ss = SpreadsheetApp.openById(SHEET_ID).getSheetByName(today);
    if (!ss) {
        const newSheet = SpreadsheetApp.openById(SHEET_ID).insertSheet();
        newSheet.setName(today);
    }
}

/** 日付の文字列形式を取得(年月日) */
function getDate() {
    const today = Utilities.formatDate(new Date(), 'JST', 'yyyyMMdd');
    return today;
}

/** 日付と時間の文字列形式を取得(年月日時分秒) */
function getDateTime() {
    const today = Utilities.formatDate(new Date(), 'JST', 'yyyy/MM/dd hh:mm:ss');
    return today;
}

Gas-smileys-back
/** スプレッドシートID */
var SHEET_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

/** HTTP GETリクエストを受け取り、スプレッドシートのデータをJSON形式で返す */
function doGet(e) {
    // JSON形式でレスポンスを生成
    var output = ContentService.createTextOutput(JSON.stringify(getJSONFromSheet()));
    // レスポンスのMIMEタイプをJSONに設定
    output.setMimeType(ContentService.MimeType.JSON);
    // JSON形式のレスポンスを返す
    return output;
}

/** スプレッドシートからデータを取得し、JSON形式で整形する */
function getJSONFromSheet() {
    // スプレッドシートを開く
    var ss = SpreadsheetApp.openById(SHEET_ID);
    // 本日の日付に対応するシートを取得
    var sheet = ss.getSheetByName(getDate());
    // 第1列目から3列目、1行目から最終行までのデータを取得
    var values = sheet.getRange(1, 3, sheet.getLastRow(), 2).getValues();
    // 結果を格納する配列を初期化
    var res = new Array(sheet.getLastRow());
    // 取得したデータをJSONオブジェクトに変換
    for (var i = 0; i < sheet.getLastRow(); i++) {
        res[i] = {
            url: values[i][0],
            smile: values[i][1]
        };
    }
    // 変換したデータをコンソールに出力(デバッグ用)
    console.log(res);
    // JSONオブジェクトを返す
    return res;
}

/** 文字列形式の日付を作成 */
function getDate() {
    // 現在の日付を 'yyyyMMdd' 形式で取得
    var today = Utilities.formatDate(new Date(), "JST", "yyyyMMdd");
    return today;
}

Gas-smileys-end

/** スプレッドシートID */
var SHEET_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

/** WebアプリケーションへのGETリクエストを処理し、JSON形式でレスポンスを返す */
function doGet(e) {
    // スプレッドシートのデータをソート
    sort();
    // JSON形式でレスポンスを生成
    var output = ContentService.createTextOutput(JSON.stringify(getJSONFromSheet()));
    // レスポンスのMIMEタイプをJSONに設定
    output.setMimeType(ContentService.MimeType.JSON);
    // JSON形式のレスポンスを返す
    return output;
}

/** スプレッドシートからデータを取得し、JSON形式で整形する */
function getJSONFromSheet() {
    // スプレッドシートを開く
    var ss = SpreadsheetApp.openById(SHEET_ID);
    // 本日の日付に対応するシートを取得
    var sheet = ss.getSheetByName(getDate());
    // 第1行目から3列目、1行目から2列分のデータを取得
    var values = sheet.getRange(1, 3, 1, 2).getValues();
    // 取得したデータをJSONオブジェクトに変換
    var res = [
        {
            url: values[0][0],
            smile: values[0][1]
        },
    ];
    // 変換したデータをコンソールに出力(デバッグ用)
    console.log(res);
    // JSONオブジェクトを返す
    return res;
}

/** スプレッドシートのデータをソートする */
function sort() {
    // スプレッドシートを開く
    var ss = SpreadsheetApp.openById(SHEET_ID);
    // 本日の日付に対応するシートを取得
    var sheet = ss.getSheetByName(getDate());
    // ソートする範囲を指定(第1行目から最終行まで、5列分)
    var data = sheet.getRange(1, 1, sheet.getLastRow(), 5);
    // 第5列(列E)を基準に降順でソート
    data.sort({ column: 5, ascending: false });
}

/** 文字列形式の日付を作成 */
function getDate() {
    // 現在の日付を 'yyyyMMdd' 形式で取得
    var today = Utilities.formatDate(new Date(), "JST", "yyyyMMdd");
    return today;
}

このGASは大きく3つの役割を持っていて、それぞれファイルへ分けています。

  1. スマホから投稿を行う処理
  2. スクリーンから定期的に呼ばれる一覧取得処理
  3. 結果発表処理

①スマホから投稿を行う処理(Gas-smileys-main)

スマホから画像が送られてくるので、その画像をGoogleドライブへ保存します。
同時に保存された画面をGoogleスプレッドシートへログ形式で書き込んでいきます。
Googleドライブから取得した文字列をB列へ、HTMLに埋め込み可能URLをC列へ格納します。
※以下はGoogleスプレッドシートのイメージ(この時点では笑顔数値であるD列/E列は空です)
スクリーンショット 2023-12-31 16.14.06.png

そして後述するAWS上に構築した笑顔数値APIへフックし、取得した笑顔数値を書き込んで処理は完了します。

ちなみに

AWSのエンドポイントURLは、EC2インスタンスを起動する度に変更されるため、スプレッドシートから読み取る仕様にしました。
※EC2インスタンス起動のたびに、GASを再デプロイするのが面倒だったので。
スクリーンショット 2023-12-31 16.07.28.png
これ気づくのが終盤で、EC2起動のたび大変だった。。。早めに気づけばよかった...

②スクリーンから定期的に呼ばれる一覧取得処理(Gas-smileys-back)

前回記事で詳しく説明しましたが、会場内に表示したスクリーン画面(JavaScript)は5秒ごとにポーリングしています。
そのポーリング先として、本処理が呼び出されることになります。

処理はものすごく単純で、Googleスプレッドシートの一覧を全件取得してJSON形式で返却するといったものです。
※一覧の件数が肥大すると処理に時間がかかると思われるので、5秒という時間では間に合わなくなってくるかもです

③結果発表処理(Gas-smileys-end)

スクリーン画面のENDボタンを押すことで、最も笑顔数値が高い写真が表示する必要があります
Googleスプレッドシートにて笑顔数値(E列)を降順にソートし、一番上にある行を取得し返却するということを行なっています。

あとがき

次回はいよいよAWS周りについてお話ししていきます。
このサービスを一通り作って思ったのですが、GASではなくすべてAWSで作成したらもっと楽できた気がします。:upside_down:
(来月AWS Certified Cloud Practitioner認定を受験してきます。頑張ります:writing_hand:)

4
2
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
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?