Googleスプレッドシートで作る用語集アプリ

2019年2月23日
systemuser

GoogleスプレッドシートではGoogle Apps Script(GAS)という言語を実装することによってWebアプリが簡単に作れてしまうというお話しの続きです。先日はチャットアプリを作ってみましたが、今回は用語集をWebアプリで公開してみたいと思います。

用語集アプリとは
今回実装してみたのは、Googleスプレッドシートに用語とその意味をひたすら登録しておいて、それを検索ボックスからサーチするというものです。例えば企業の中にあるノウハウや専門用語をマスター管理しておいて、新入社員に覚えさせるような局面で重宝しそうですね。またもう少し拡張させて色々なスプレッドシートやドライブ内の情報と統合すれば、社内の情報が全て探せてしまう便利なツールになる可能性もあります。今回作ったアプリでは、私が昨年まで通っていたグロービス経営大学院で実施した試験のために覚えた経営用語集約300語を使って、以下のような利用用途を想定してみました。
・スプレッドシート内の用語集をWeb画面で表示させる
・キーワードで検索して絞り込む

今回も1画面だけ使って、とても簡単なアプリになりました。それではご紹介しましょう。まずはスプレッドシートに以下のような用語集が沢山並んでいます。Excelからコピペすればスプレッドシートに書くのは全く簡単な作業です。もっとも教科書から用語集を抽出するのが大変でしたが^^;

そしてGASで作ったアプリで上記のシートを表示させたのが以下です。上から経営戦略、マーケティング、人材マネジメント、アカウンティング、ファイナンスと沢山の用語が並んでいますね。

この状態だと300個の言葉があり、意味を探す際にちょっと使いずらいです。そのため上の検索ボックスで絞込みをしてみましょう。そうですね。経営における株とは何なのかを調べてるために検索ボックスに「株」と入れてみましょう。

そうすると下のスナップショットのように用語が絞られました。
アカウンティングとファイナンスで株に関していくつが関連する用語があるのですね。

少し遊びで検索ボックスの隣に日時を表示させてみましたので、ナマで見るとそこの動きが面白かったりします。笑

用語集アプリの仕組み
それではこのアプリも仕組みを見ていきましょう。前回チャットと同様に2つのみで、表示用と検索の処理からなっています。ただし表示処理で検索も行っているところがチャットアプリと少し異なります。

①表示処理
・スプレッドシートの内容を画面に表示する
・検索用語が指定されている場合は、それで絞り込む&マーキングする

チャットアプリも似たような構造ではありますが、今回は更新処理がないために表示処理の中で全て行うようにしています。ただし作るファイルは2つです。これは処理を制御する部分と、UIとGASの機構が分かれているためです。
・search.gs
・inde.html
それぞれについて、どんな処理を行っているのかを見ていきましょう。因みにそれぞれのスクリプトを自分の環境にどのようにどのように持ってくるのかは前回のチャットアプリのテーマの中で説明していますので、ご興味のある方はそちらを参考に試してみてください。

①スクリプトファイル:search.gs
スクリプトの方はブラウザから送られてきたURLを処理する部分となっています。変数の初期化の他、URLのパラメータごとに処理は入れていますが、どちらもindex.htmlに処理をスルーしています。下の方にgetSearch()という関数が出てきますが、画面ファイルの方に受け取った変数を渡すためですね。これがあることによって画面を生成するindex.htmlの処理の中で、動的な制御が可能になってきます。

search = "";
var id ='[スプレッドシートのURL]';
var sheet = SpreadsheetApp.openById(id).getSheetByName("用語集");

function doPost(e){
    doGet(e);
    return HtmlService.createTemplateFromFile("index").evaluate(); 
}
function doGet(e) {
  search = e.parameter.searchWord;
  if (e.parameter.name == undefined) {
    //初期表示はビューのみ
    return HtmlService.createTemplateFromFile("index").evaluate(); 
  }
  if (e.parameter.searchWord !== undefined) {
    //検索時も同じ画面に返します
    return HtmlService.createTemplateFromFile("index").evaluate(); 
  }
}
function getSearch(){
    return search;
}

②HTMLファイル:index.html
続いて画面を生成するhtmlファイルです。チャットアプリの時と比べると少し長いですが、やっていることは簡単で
・スプレッドシートから全てのデータを読み込む
・検索キーワードがある場合はそれでキーワードを絞り込む
・検索キーワードがない場合は全件表示する
・これらを補完するためのいくつかの関数やメッセージの制御
となっています。これ以外はUIを制御するスタイルシートがほとんどですので、やはり実際に処理している部分はとても少なく実装出来ています。

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
  <style>
  #header-fixed{
    border: 5px solid #fff;    /* 表示領域を白枠で囲う */
    position: fixed;            /* ヘッダーの固定 */
    padding:10px 0 10px;       /* 上10px、下20pxをあける */
    top:  0px;                   /* 位置(上0px) */
    left: 10px;                  /* 位置(右0px) */
    width: 100%;                /* 横幅100% */
    height:50px;                /* 縦幅50px */
    background-color:#FFF      /* バックの色 */
  }
  #content{
    top:  70px;                   /* 位置(上0px) */
    left: 10px;                  /* 位置(右0px) */
    padding:60px 0 0px;
    width: 100%;                /* 横幅100% */
    background-color: "#000000" /* バックの色 */
  }
  #menu{
    font-size : 15px;
    border : 1px;
    padding : 10px 20px 0px 0px;
  }
  table.search {
    border: solid 1px #000000;
    border-collapse: collapse;
  }
</style>
  <div id='header-fixed'>
    <form name="serchForm" action="https://script.google.com/macros/s/AKfycbxwmCh5QjIEIlctb8mlDSk81X4dbBxl6vLwTbI6rA/exec?name=search">
    <a id='menu' href="https://script.google.com/macros/s/AKfycbxwmCh5QjIEIlctb8mlDSk81X4dbBxl6vLwTbI6rA/exec">全件表示</a>
    検索文字:<input type="text" width="30%" name="searchWord" required>
    <input type="submit">
    <font size="1px">      現在の時刻:<span id="clock_time"></span></font>
    </form>
<script>
function clock()
{
    // 現在日時を表すインスタンスを取得
    var now = new Date();
    var dateTime =  now.getTime();
    var year = now.getFullYear();
    var month = now.getMonth()+1;
    var day = now.getDate(); 
    var h = now.getHours();
    var mi = now.getMinutes();
    var s = now.getSeconds();
    document.getElementById("clock_time").innerHTML = year + "年" + month + "月" + day + "日 " + h + "時" + mi + "分" + s + "秒";
}
setInterval(clock, 1000);
</script>
  </div>
  <div id='content'>
    <ul>
      <?
      //  シートから用語集を表示する
       var id ='[スプレッドシートのURL]';
       var mySheet = SpreadsheetApp.openById(id).getSheetByName("用語集");
        var endrow = mySheet.getLastRow();
        var myData = mySheet.getRange(2, 1 , endrow-1 , 4).getValues();
        myMail = Session.getActiveUser().getEmail();
        search = getSearch();
        output.append('検索文字='+ escape_html(search));
        output.append('<table class="search" border="1">');
        output.append('<tr bgcolor="#aaccff"><th>カテゴリ</th><th>用語</th><th>内容</th></tr>');
        y=0; //検索結果のカウンタ
        for(var i=0;i<myData.length;i++){
          category = myData[i][0];
          word = myData[i][1];
      content = myData[i][2];
          url = myData[i][3];
          if(search !== undefined){
            if (word.indexOf(search) !== -1){
              replace = word.replace(search, '<font color="#ff0000" size="2px"><b>' + search + '</b></font>');
              output.append('<tr>');
              output.append('<td width="10%" valign="top"><font color="#000000" size="2px">'+ category +'</font></td>');
              output.append('<td width="20%" align="left" valign="top"><font size="2px">'+ replace +'</font></td>');
              output.append('<td valign="top"><font style="bold" size="2px" color="#000000">'+ content +'</font></td>');
              output.append('</tr>');
              y ++;
            }
          }else{
            output.append('<tr>');
            output.append('<td width="10%" valign="top"><font color="#000000" size="2px">'+ category +'</font></td>');
            output.append('<td width="20%" align="left" valign="top"><font color="#000000" size="2px">'+ word +'</font></td>');
            output.append('<td valign="top"><font style="bold" size="2px" color="#000000">'+ content +'</font></td>');
            output.append('</tr>');
          }
        }
        output.append('</table>');
        if (y == 0){
          output.append('検索できませんでした。キーワードを変えてもう一度検索してください。');
        }
      ?>
      <?
        function escape_html (string) {
          if(typeof string !== 'string') {
            return string;
          }
          return string.replace(/[&'`"<>]/g, function(match) {
            return {
            '&': '&',
            "'": ''',
            '`': '`',
            '"': '"',
            '<': '<',
            '>': '>',
            }[match]
          });
        }
      ?>
    </ul>
    </div>
</body>
</html>

因みに、上記の処理の下の方にescape_html(string)という記号ばかりが書いてある処理があります。これは、検索ボックスから入力された文字列にhtmlを制御する文字コードがあった場合、何もしないと画面が崩れたり情報が乗っ取られたりすることを防ぐために入れているエスケープ処理というものです。全てのWebシステムにはこういう処理が入っていますが、このようにして大切なデータを守っているのですね。

終わりに
今回の用語集アプリ、いかがでしたでしょうか。検索が中心でしたが、もちろん管理者向けの登録画面を用意したり、自動でリンクを張る、と言ったことも簡単に出来てしまいます。
そもそもチャットといい、用語集といい、その世界で便利なものは世の中にはたくさんあると思いますが、企業や組織にとって必要なパーツを少しずつ作っていくと、企業ポータルとして必要なアプリを全社員で共有出来たり、個人ごとにインストール出来たり、それこをドライブ内の色んなアプリと連携させることで色んな活用方法が考えられそうですね。出来るだけコストをかけず、リテラシーの低い方でも使えるシステムが作れるか。そのような観点で今後もアプリをご紹介していきたいと思います。

2 Comments

いおり
Reply

こんにちは
上記のスクリプトをコピーして使用してみたのですが
URLから開くと下記のエラーが起こってしまいました 

SyntaxError: Invalid or unexpected token(行 13、ファイル「search」)

またスクリプトも下記のようなエラーコードが出ております
TypeError: Cannot read properties of undefined (reading ‘parameter’)
doGet @ search.gs:10

どのようにすればエラーを解消して作動することができますでしょうか

ご確認よろしくお願いいたします。

2023年4月9日
ふらっと株式会社
Reply

いおり様
当ページを参照頂き、誠にありがとうございます。
またエラーが生じているとのこと、ご不便をおかけいたします。

いずれもパラメータ未定義のエラーのように見えるのですが、
ブラウザから呼び出されていますでしょうか。
search.gsのスクリプトをそのまま実行しても、HTTPリクエストパラメーターがないと
スクリプト自体がエラーとなってしまいます。

AppScriptをデプロイし、そこで表示されるWebアプリのURLを実行することで
画面が表示されるようになっております。

お手数をおかけしますが、ご確認宜しくお願い致します。

ふらっと株式会社
宮地

2023年4月12日

Leave a comment