App Engineから読み取ったデータの書き込みと、スプレッドシートのUIで表示する処理

2013年11月28日(木)
清野 克行

今回は、App Engine側で読み取ったスプレッドデータのDatastore書き込み、およびそれぞれのデータ読み取り表示をスプレッドシート風のUI表示で行う処理内容について見ていきます。前回からJavaのプログラミングに入っていますが、今回もJavaによるスプレッドデータの処理が中心となります。

図1:Spread Sheet 年月別店舗売上(グリッド表示)(クリックで拡大)

図1のように、スプレッドデータをApp Engineから読み込んで、また簡易形式で画面表示する部分は前回の連載で見てきましたが、今回は読み取ったスプレッドデータをDatastoreに書き込んでみます。またDrive上およびDatastoreに書き込まれたスプレッドデータをグリッド表示するサンプルを紹介します。

スプレッドデータの表示形式では前回は簡易表示でしたが、今回はスプレッドシートの表示を、ActiveWidgetsを使用したサンプルとParamQuery gridを使用したサンプルの2種類紹介します。

1. Drive上のスプレッドデータをApp Engineから読み込みDatastoreに書き込む

最初はスプレッドデータのDatastore書き込みです。前回はスプレッドデータをApp Engineから読み込む処理について解説しましたが、今回は更にそのデータをDatastoreに書き込む処理について見ていきます。

ここで、スプレッドデータのDatastore書き込みでは、App EngineのLow-Level APIを使用しますが、Low-Level APIについてはThinkITにも下記の連載で解説していますので参考にしてください。

Think IT連載『Google App EngineのLow-Level APIを極める』

1.1 サーブレットのコード記述

リスト1 サーブレット(SpreadServlet.java)

package com.google.gdata;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.*;
@SuppressWarnings("serial")
public class SpreadServlet extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    resp.setContentType("text/plain");
      resp.setContentType("text/html; charset=utf-8");
      PrintWriter out = resp.getWriter();
      String mode = req.getParameter("mode"); 
      String sheet = req.getParameter("sheet");
      SpreadBean ss = new SpreadBean(); 
      ////////////////////////////////////////////////////////////////////
      //  For Sales data
      if (mode.equals("getdsallshopym")) {
        String rv = ss.getDsAllShopYm(sheet);		//(1)
        out.println(rv);
      } else if (…) {
        :
      }
  }
  
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    resp.setContentType("text/plain");
    resp.setContentType("text/html; charset=utf-8");
    PrintWriter out = resp.getWriter();
    String mode = req.getParameter("mode"); 
    String sheet = req.getParameter("sheet");
    SpreadBean ss = new SpreadBean(); 
    ////////////////////////////////////////////////////////////////////
    //  For Sales data
    if (mode.equals("addspreadallshoptodsym")) {
      String rv = ss.addSpreadAllShopToDsYm(sheet);		//(2)
      out.println(rv);
    } else if (…) {
      :
    }
  }
}

サーブレットの処理は連載前回のリスト2への追加で行われており、(1)のgetDsAllShopYmメソッドではDatastoreに書き込まれたスプレッドデータの読み取り、(2)のaddSpreadAllShopToDsYmメソッドではDriveから読み込んだスプレッドデータのDatastore書き込み処理を行っています。

1.2 ビーンズコード記述

リスト2 addSpreadAllShopToDsYmメソッド(SpreadBean.java)

public String addSpreadAllShopToDsYm(String sheet)  { 
  // このアプリケーションの名称。任意の名前を設定
  String appliName = "cyberspace-SpreadsheetSearch-1";
  String username = "xxxxxxxxxxxxxxxxxxxxxxxxx";
  String password = "xxxxxxxxxxxxxxx";
  try {
    /////////////////////////////////////////////////////////
    //  [I] Driveにあるスプレッドデータを読み取る
    //  認証処理
    SpreadsheetService spreadsheetservice = new SpreadsheetService(appliName);
    spreadsheetservice.setUserCredentials(username, password);
    // 検索対象のスプレッドシートを特定
    FeedURLFactory feedurlfactory = FeedURLFactory.getDefault();
    SpreadsheetQuery spreadsheetquery = 
    new SpreadsheetQuery(feedurlfactory.getSpreadsheetsFeedUrl());
    spreadsheetquery.setTitleQuery(sheet); // 検索対象のスプレッドシート名を指定している
    SpreadsheetFeed spreadFeed =
      spreadsheetservice.query(spreadsheetquery, SpreadsheetFeed.class);
    SpreadsheetEntry spreadsheetentry = spreadFeed.getEntries().get(0);
    // 検索対象のワークシートを特定
    WorksheetEntry worksheetentry = spreadsheetentry.getDefaultWorksheet();
    //  クエリでワークシート内を検索
    ListQuery listquery = new ListQuery(worksheetentry.getListFeedUrl()); 
    ListFeed listfeed = spreadsheetservice.query(listquery, ListFeed.class); 
    /////////////////////////////////////////////////////////
    //     [II] 読み取ったスプレッドデータをDatastoreに書き込む
    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();     		//(1)
    int i = 0;
    for (ListEntry listentry : listfeed.getEntries()) { 
      CustomElementCollection customelementcollection = listentry.getCustomElements();
                                                   	//(2)
      DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
      String moddate = df.format(new Date()); 
      Entity sales = null; 
      if (!customelementcollection.getValue("店舗名").equals("売上合計")) {   	//(3)
        Key saleskey = KeyFactory.createKey								//(4)
        ("salesym", sheet + customelementcollection.getValue("店舗名")); 
        sales = new Entity(saleskey);									  	//(5)
        sales.setProperty("seq", i++); 
      } else {
        Key saleskey = KeyFactory.createKey
        ("salesymtotal", sheet + customelementcollection.getValue("店舗名"));	 //(6)
        sales = new Entity(saleskey);										//(7)
      }
      sales.setProperty("shop", customelementcollection.getValue("店舗名"));		//(8)
      sales.setProperty("yyyymm", sheet);
      sales.setProperty("food", customelementcollection.getValue("食品"));
      sales.setProperty("electric", customelementcollection.getValue("家電"));
      sales.setProperty("bedding", customelementcollection.getValue("寝具")); 
      sales.setProperty("other", customelementcollection.getValue("その他"));
      sales.setProperty("moddate", moddate); 
      ds.put(sales);       													//(9)
    }
    return "スプレッドデータのDatastore登録成功"; 
  } catch (AuthenticationException e) {
    e.printStackTrace();
    return "スプレッドデータのDatastore登録不成功 :" + e;
  } catch (IOException e) {
    e.printStackTrace();
    return "スプレッドデータのDatastore登録不成功 :" + e;
  } catch (ServiceException e) {
    e.printStackTrace();
    return "スプレッドデータのDatastore登録不成功 :" + e;
  }
}

[I] Driveにあるスプレッドデータを読み取る

Drive上のスプレッドデータ読み取りは、前回記事のリスト3と同じですので省略します。

[II] 読み取ったスプレッドデータをDatastoreに書き込む

(1)DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
App EngineのDatastoreにアクセスする場合は、最初にDatastoreServiceのインスタンス(ここではds)を生成します。

(2)次に、読み込んだスプレッドデータを1行ずつ読み取っての繰り返し処理に入ります。

(3)if~else文
・読み取ったデータが売上合計でない場合はif文内の(4)でキーを作成した後、kind名が「salesym」のエンティティをDatastore書き込み用に生成します(5)。
・読み取ったデータが売上合計の場合はelse以下で処理しており、(6)でキーを作成した後、(7)で「salesymtotal」のエンティティを生成しています。
その後は(8)以下で書き込むエンティティ(sales)にプロパティ項目をセットし、(9)のputメソッドで書き込み処理を行います。

2.Datastoreに書き込まれたスプレッドデータをApp Engineから読み取る

リスト3 getDsAllShopYmメソッド (SpreadBean.java)

public String getDsAllShopYm(String yyyymm) throws IOException, ServiceException { 
  try {
    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();				//(1)
    Filter ymFilter = new FilterPredicate("yyyymm", FilterOperator.EQUAL, yyyymm);	//(2)
    Query q = new Query("salesym").setFilter(ymFilter).addSort("seq", SortDirection.ASCENDING);
                                                                                      //(3)
    PreparedQuery pq = ds.prepare(q);												    //(4) 
    String rv = "[";																    //(7)
    for (Entity res : pq.asIterable()) {												//(5)
      String shop = (String) res.getProperty("shop");								    //(6)
      String food = (String) res.getProperty("food");
      String electric = (String) res.getProperty("electric");
      String bedding = (String) res.getProperty("bedding");
      String other = (String) res.getProperty("other");
      rv 	+= "[\"" + shop + "\"," 
          + "\"" + food + "\"," 
          + "\"" + electric + "\"," 
          + "\"" + bedding + "\"," 
          + "\"" + other + "\"],";  					  //(7)
    } 
    rv = rv.substring(0, rv.length() - 1) + "]";										//(7)
    return rv;																	    //(8)
  } catch (Exception e) {
    e.printStackTrace();
    return "参照不成功 Exception:" + e;
  }
}

Datastoreに書き込まれたスプレッドデータを読み取る処理は、よりシンプルなコード記述になります。

(1)で前と同様にDatastoreServiceのインスタンス(ds)を生成し、(3)でDatastore検索用のQueryを生成しています。Queryはフィルタやソート条件を追加して検索データの絞り込みや、並べ替えを指定することができますが、ここでは(2)のフィルタ指定で年月指定を行い、ソート条件でseq(シークエンス)プロパティを昇順で並べ替える指定を行っています。

次に、(4)prepareで実行するクエリを作成し、(5)ではasIterableで(4)で作成したクエリを実行し、 Entity項目を Iterable(反復可能)として取得します。その後(6)以下のgetPropertyでプロパティ名に対応した値を取得し、(7)でWebクライアントでの処理に対応したJSONの配列形式にフォーマット後(8)でクライアントへ送ります。

3.DriveおよびDatastoreのスプレッドデータをUI表示する

3.1 ブラウザ上の操作と表示

次に、ここまで見てきたスプレッドデータのサーバ側処理結果を、ブラウザでUI表示する処理について見ていきますが、最初に画面操作と表示内容を見てみます。
画面操作の参照用サイト

図2:Spreadsheetデータの登録・参照画面(リンク)(クリックで拡大)

図2は、Spreadsheetデータ登録・参照の初期表示画面です。この画面から、ここまで見てきた

  1. スレッド登録のaddSpreadAllShopToDsYmメソッド
  2. Datastoreに書き込まれたスプレッドデータ読み取りのgetDsAllShopYmメソッド
  3. 前回紹介したDrive上のスプレッドデータ読み取りのgetSpreadAllShopYmメソッド

をサーブレット経由で呼び出して表示処理を行います。

3.2 Drive上スプレッドデータのDatastore登録

図3:スプレッドデータのDatastore登録(クリックで拡大)

図3はスプレッドデータのDatastore登録処理表示で、年月指定によってSpreadsheetデータを(前回の連載で見てきた)addSpreadAllShopToDsYmメソッドによって読み取り、次にDatastoreへの登録処理を行っています。

3.3 Drive上のスプレッドデータ参照

図4:Spreadsheetデータの登録・参照およびDatastore書き込みデータの参照画面(クリックで拡大)

図2の画面で年月指定後、「スプレッド参照」ボタンをクリックするとDrive上のスプレッドデータが図4の画面のようにグリッド形式で表示されます。Drive上スプレッドデータを簡易形式で表示するサンプルは前回の記事で紹介しており、サーバ側ビーンズの処理コードは前回のものと全く同じです。

また図4のようなスプレッド風画面表示では、ここで使用しているActicewidgetsのgridコントロールの他にも幾つかあってjQueryの追加プラグインなどもあり、その内のParamQuery gridについて最後に紹介します。

3.4 Datastoreスプレッドデータの参照

図5:Datastoreスプレッドデータの参照(クリックで拡大)

次に図5は、図3の処理でDatastoreに書き込まれたスプレッドデータを参照しています。書き込みは5月度のデータですので図3の参照データとは値が異なっているのが確認できます。

3.5 ActiveWidgetsが提供するWebコンポーネント

ActiveWidgets社(http://www.activewidgets.com/)は図4~図5のようなグリッドコンポーネントの他にも、図6のようなさまざまなWebコンポーネントを提供しており、これ等を使用することによってWebUIのリッチ化を行うことができます。

図6:ActiveWidgetsが提供するGrid以外のWebコンポーネント(クリックで拡大)

ところでActivewidgetsについては、筆者は以前にもThink ITに下記のような連載記事を書いています。この連載では、サーバ側で DWR(Direct Web Remoting)を使用した場合のActivewidgetsクライアントとの連携や表計算機能の追加、グラフ表示などについても解説していますので、興味のある方は是非参照してみてください。

Think IT連載『読んで試して!ActiveWidgets!』

3.6 Spreadsheetデータの登録・参照スクリプト

リスト3はSpreadsheetデータ登録・参照のHTMLおよびJavaScript記述でこの記述で図3~図5の表示処理すべてを行っています。

リスト4 Spreadsheetデータの登録・参照スクリプト(modDsSpreadAllShopYm.htm)

  <!DOCTYPE html>
  <html>
  <head>
  <meta charset="utf-8"/>
  <title> modDsSpreadAllShopYm </title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <link rel="stylesheet" href="../css/aw.css"/>
  <style type="text/css"/>
  #sgrid .aw-grid-row {height: 20px; border-bottom: 1px solid #ccc}
  #sgrid .aw-alternate-even {background: #fff;}
  #sgrid .aw-alternate-odd {background: #ddf;}
  #sgrid .aw-alternate-even .aw-column-0 {background: #ddf;}
  #sgrid .aw-alternate-odd .aw-column-0 {background: #ccf;}
  #sgrid .aw-rows-selected {background: #316ac5;}
  #sgrid .aw-rows-selected .aw-column-1 {background: #316ac5;}
  #sgrid .aw-column-0 {width: 75px; text-align: center; border-right: 1px dotted #ccc;}
  #sgrid .aw-column-1 {width: 65px; text-align: center; border-right: 1px dotted #ccc;}
  #sgrid .aw-column-2 {text-align: right}
  #sgrid .aw-column-3 {text-align: right}
  #sgrid .aw-column-4 {text-align: right}
  #sgrid .aw-column-5 {text-align: right}
  #sgrid .aw-column-6 {text-align: right}
  #sgrid .aw-mouseover-cell {color: red;} 
  #sgrid .aw-grid-row .aw-cells-selected {background: #316ac5;}
  #sgrid .aw-grid-headers {color: blue}
  #sgrid .aw-grid-headers .aw-column-1 {font-weight: bold}
  #sgrid .aw-mouseover-header {color: red;}
  #sgrid .aw-mousedown-header {color: yellow;}
  #sgrid .aw-header-1 {background: #def}
  #sgrid .aw-row-selector {width: 20px; text-align: center}
  #sgrid .aw-row-2 .aw-row-selector {font-weight: bold}
  #sgrid .aw-mouseover-row .aw-row-selector {color: red;}
  #sgrid .aw-mouseover-selector {background: green;}
  #sgrid .aw-mousedown-selector {background: yellow;}
  #sgrid {font-size: 10pt}
  </style>
  <script type="text/javascript" src="../js/jquery-2.0.3.min.js"></script>
  <script type="text/javascript" src="../js/aw.js"></script>
  <script type="text/javascript">
  $(function() {
    $("#addDs").click(function() {								//(1)スプレッド登録
      $("#dsp").html("<div id='stat'></div>")
      var query = {};
      query["mode"] = "addspreadallshoptodsym";
      query["sheet"] = $("#year").val() + $("#month").val();
      $.post("/spread", query, function(res) {
        $("#stat").text(res);
      });
    });
    $("#revSp").click(function() {					//(2)ドライブのスプレッドデータ参照
      getdata("getspreadallshopym");
    });
    $("#revDs").click(function() {					//(3)Datastoreのスプレッドデータ参照
      getdata("getdsallshopym");
    }); 
    function getdata(mode) {						//(4) 参照処理関数
      $("#dsp").html("<div id='sgrid'></div>")
      var query = {};
      query["mode"] = mode;
      query["sheet"] = $("#year").val() + $("#month").val();
      $.get("/spread", query, function(resp) {
        var r = $.parseJSON(resp);		//(5)受信したJSONデータをjavaScriptオブジェクトに変換  			var rows = 16;
        var cols = 7;
        var cdat = new Array(rows); 
        for (j = 0; j < rows; j++) { 
          cdat[j] = new Array(cols);			//(6)表示データ格納用に2次元配列生成
        }
        cdat[0][0] = "関東地区";
        cdat[5][0] = "近畿地区";
        cdat[10][0] = "中国地区";
        if (mode == "getspreadallshopym") {		//(7)Driveスプレッドデータの配列セット
          for (var i = 0; i < r.wsdat.length ; i++) {
            var idat = r.wsdat[i].data.split(",");		//(8)
            for (var j = 0; j < idat.length; j++) {
            var vdat = idat[j].split(":")[1];		//(9)
              cdat[i][j+1] = vdat;				//(10)
            }
          }
        } else if (mode == "getdsallshopym") {	//(11)Datastore格納スプレッドの配列セット
          for (var i = 0; i < r.length; i++) { 
            var rtotal = 0;
            for (var j = 0; j < r[i].length; j++) {
              if (j > 0) { 
                rtotal += parseInt(r[i][j]);     		//(11)	行合計の加算 
              }
              cdat[i][j+1] = r[i][j];					//(12)セル値の配列セット 
            } 
          cdat[i][j+1] = rtotal;							//(13)行合計の集計値セット
          }
          cdat[15][1] = "売上合計"; 
          for (var i = 2; i < cols; i++) { 		//(14)列合計値の計算
              var ctotal = 0;
              for (var j = 0; j < rows - 1 ;j++) {
                  ctotal += parseInt(cdat[j][i]); 
              } 
              cdat[rows -1][i] = ctotal;		//(15)列合計の集計値セット
            }
          }
          var headers = ["地域名", "店舗名", "食品", "家電", "寝具", "その他", "合計"]; 
                                              //(16) 
          var grid = new AW.Grid.Extended;
          grid.setId("sgrid");
          var gridw = 28 + 93 * cols;
          var gridh = 28 + 26 * rows;
          grid.setControlSize(gridw, gridh);      	// グリッド幅と高さ指定
          grid.setCellText(cdat);               	//表示データの配列を指定
          grid.setCellEditable(true);            	//グリッドのセルを編集可能に指定
          grid.setHeaderText(headers);         	//ヘッダ表示の配列を指定
          grid.setRowCount(rows);             	// 行数指定
          grid.setColumnCount(cols);           	// 列数指定
          grid.setSelectorText(function(i){return this.getRowPosition(i);}); // 行ヘッダ表示
          grid.setSelectorVisible(true);          	//行ヘッダの表示指定
          document.getElementById("sgrid").innerHTML = grid;   	//(17) 
        });
      }
    });
    </script>
    </head>
    <body>
    <h3>Spreadsheet データ登録・参照</h3>
    年<select id="year">
      <option value="">=年選択=</option>
      <option value="2013">2013年</option>
      <option value="2012">2012年</option>
      <option value="2011">2011年</option>
    </select>
    月<select id="month">
        <option value="">=月選択=</option>
      <option value="01">1月</option>
      <option value="02">2月</option>
      <option value="03">3月</option>
      <option value="04">4月</option>
      <option value="05">5月</option>
      <option value="06">6月</option>
      <option value="07">7月</option>
      <option value="08">8月</option>
      <option value="09">9月</option>
      <option value="10">10月</option>
      <option value="11">11月</option>
      <option value="12">12月</option>
    </select>
    <input type="button" id="addDs" value=" スプレッド登録  " />
     <input type="button" id="revSp" value=" スプレッド参照  " />
    <input type="button" id="revDs" value="データストア参照 " />
    <hr />
    <div id="dsp"></div>
    </body>
  </html>

リスト4で、(1)のスプレッド登録は一般的なjQueryの$.post通信なので問題ないでしょう。(2)と(3)はドライブとDatastoreのスプレッドデータの参照処理で、(4)のgetdataメソッドを、処理モード(”mode”)を引数に指定して呼び出しています。

処理内容はどちらも同様で、受信したスプレッドデータをセル項目毎に分割し、2次元配列にセットした後に表示処理(レンダリング)を行います。ただし、受信するデータフォーマットがDriveからとDatastoreからでは異なるため、配列へデータをセットする部分のコード記述が異なっています。

[Driveからのスプレッドデータを配列にセット]

Driveからのスプレッドデータの場合は、各セル項目がカンマ区切り(CSV)形式でセットされているため、(8)においてカンマ区切りでsplit処理したデータを配列idatにセットし、更に”セル名:セル値”になっている配列idatの各項目から(9)でセル値を取り出して、(10)で配列にセットしています。

[Datastoreからのスプレッドデータを配列にセット]

Datastoreからのスプレッドデータの場合は、リスト2の(7)でクライアントに送信されるJSONデータが2次元配列形式にフォーマットされているので、(5)でJavaScriptオブジェクトに変換された配列値を(11)で行合計値を加算した後、個別のセル値を(12)で配列にセットし、集計の終わった行合計値を(13)でセットしています。

その後、列合計の集計を(14)の繰り返し処理で行い、(15)で集計値をセットしています。ここで行合計の欄も列合計の計算で集計されるので、総合計値が行合計欄の最後の項目として配列にセットされます。

[グリッド表示の編集]

(16)のヘッダ項目指定以下は、グリッドの表示形式を設定するメソッドが並んでおり、最後に(17)のinnerHTMLでグリッドのWeb画面への表示(レンダリング)が行われます。

[ParamQuery gridでSpreadsheetデータを表示]

ここではActivewidgetsを使用したグリッド表示について紹介しましたが、もちろんグリッド表示をサポートしているJavaScriptライブラリは他に幾つもあります。例えばjQueryの追加プラグインとして公開されているものもありますが、ここではその中からParamQuery gridを紹介します。

図7:ParamQuery公式サイト(リンク)(クリックで拡大)

ParamQuery gridはオープンソースのjQuery グリッドプラグインで、Ajaxアプリケーションで表データの表示や更新処理などで使用することができます。メモリの効率的使用と仮想表示のアーキテクチャを使用して百万のレコードでも10レコードの場合と同様のパフォーマンスを得ることができ、大量データの高速表示で威力を発揮します。

図8:ParamQuery gridを使用したスプレッドデータ表示(リンク)(クリックで拡大)

図8はParamQuery gridを使用したスプレッドデータの登録・表示画面で、画面操作等は3の「DriveおよびDatastoreのスプレッドデータをUI表示する」で見てきたActiveWidgetsを使用したケースと全く同じです。

リスト5 ParamQuery gridを使用したSpreadsheetデータの登録・参照(modDsSpreadAllShopYmPq.htm)

  <!DOCTYPE html>
  <html>
  <head>
  <meta charset="utf-8"/>
  <title> modDsSpreadAllShopYmPq </title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/themes/base/jquery-ui.css" />
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> 
  <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script>
  <link rel="stylesheet" href="../css/pqgrid.min.css" />
  <script type="text/javascript" src="../js/pqgrid.min.js"></script> 
  <script type="text/javascript">
  $(function() {
    $("#addDs").click(function() {									//(1)スプレッド登録
      $("#dsp").html("<div id='stat'></div>")
      var query = {};
      query["mode"] = "addspreadallshoptodsym";
      query["sheet"] = $("#year").val() + $("#month").val();
      $.post("/spread", query, function(res) { 
        $("#stat").text(res);
      });
    });
    $("#revSp").click(function() {							//(2)ドライブのスプレッドデータ参照
      getdata("getspreadallshopym");
    });
    $("#revDs").click(function() {							//(3)Datastoreのスプレッドデータ参照
      getdata("getdsallshopym");
    }); 
    function getdata(mode) {								//(4)参照処理関数
      $("#dsp").html("<div id='sgrid'></div>")
      var query = {};
      query["mode"] = mode;
      query["sheet"] = $("#year").val() + $("#month").val();
      $.get("/spread", query, function(resp) {				//(5)
        var r = $.parseJSON(resp);
        var rows = 16;
        var cols = 7;
        var data = new Array(rows);
        for (j = 0; j < rows; j++) {
          data[j] = new Array(cols);				//(6)表示データ格納用に2次元配列生成
        }
        data[0][0] = "関東地区";
        data[5][0] = "近畿地区";
        data[10][0] = "中国地区";
        if (mode == "getspreadallshopym") {			//(7)Driveスプレッドデータの配列セット 
          for (var i = 0; i < r.wsdat.length ; i++) {
            var idat = r.wsdat[i].cdat.split(",");	//(8)
            for (var j = 0; j < idat.length; j++) {
              var vdat = idat[j].split(":")[1];			//(9)
              data[i][j+1] = vdat;					//(10)
            }
          }
        } else if (mode == "getdsallshopym") {	//(11)Datastore格納スプレッドの配列セット 
          data[15][1] = "売上合計";
          for (var i = 0; i < r.length; i++) { 
            var rtotal = 0;
            for (var j = 0; j < r[i].length; j++) {
              if (j > 0) { 
                rtotal += parseInt(r[i][j]);   			//(11)	行合計の加算 
              }
              data[i][j+1] = r[i][j];						//(12)セル値の配列セット
            } 
            data[i][j+1] = rtotal;							//(13)行合計の集計値セット
          } 
          for (var i = 2; i < cols; i++) { 						//(14)列合計値の計算
            var ctotal = 0;
            for (var j = 0; j < rows - 1 ;j++) {
              ctotal += parseInt(data[j][i]); 
            } 
            data[rows - 1][i] = ctotal;						//(15)列合計の集計値セット
          } 
        } 
        var obj = {};
        obj.width = 635;											//(16)
        obj.height = 435;
        obj.colModel = [{title:"地域名", width:80, dataType:"string"},	//(17)
        {title:"店舗名", width:80, dataType:"string"},
        {title:"食品 ", width:80, dataType:"integer", align:"right"},
        {title:"家電 ", width:80, dataType:"integer", align:"right"},
        {title:"寝具 ", width:80, dataType:"integer", align:"right"},
        {title:"その他 ", width:80, dataType:"integer", align:"right"},
        {title:"合計", width:80, dataType:"integer", align:"right"}];
        obj.dataModel = {data:data};
        $("#sgrid").pqGrid(obj);							//(18)レンダリング
      });
    }
  });
  </script>
  </head>
  <body>
  <h3>Spreadsheet データ登録・参照</h3>
  年<select id="year">
    <option value="">=年選択=</option>
    <option value="2013">2013年</option>
    <option value="2012">2012年</option>
    <option value="2011">2011年</option>
  </select>
  月<select id="month">
      <option value="">=月選択=</option>
    <option value="01">1月</option>
    <option value="02">2月</option>
    <option value="03">3月</option>
    <option value="04">4月</option>
    <option value="05">5月</option>
    <option value="06">6月</option>
    <option value="07">7月</option>
    <option value="08">8月</option>
    <option value="09">9月</option>
    <option value="10">10月</option>
    <option value="11">11月</option>
    <option value="12">12月</option>
  </select>
  <input type="button" id="addDs" value=" スプレッド登録  " />
   <input type="button" id="revSp" value=" スプレッド参照  " />
  <input type="button" id="revDs" value="データストア参照 " />
  <hr />
  <div id="dsp"></div>
  </body>
  </html>

リスト5は、図8の画面表示と操作を行うParamQuery gridを使用した場合のWebクライアントコードです。リスト5での処理の流れは、(1)から(15)までActiveWidgetsを使用したリスト4の場合と同じで(16)から後の部分だけが異なっています。

(16)ではスプレッドの表示サイズ、(17)以下では各列のタイトルと表示書式を指定し、(18)の.pqGrid関数でレンダリングを行っています。

今回紹介したActiveWidgetとParamQueryの両方ともパラメータ指定で、グリッドのセルを表示オンリーから編集可能に設定することができ、Excelと同様にグリッドのセル単位で表示データの編集を行うこともできるようになっています。もちろん編集済みのデータをDatastoreに再登録する形でスプレッドデータの更新処理を行うことも可能です。

今回はDrive上スプレッドデータのDatastore書き込みと、スプレッドシート風の画面表示について見てきました。次回は連載最終回となりますが、これまでとは逆の流れで、Datastoreに書き込まれたスプレッドデータを読み取ってDrive上のSpreadsheet上に再現する処理、およびこれまで見てみたDrive上のスプレッドデータのDatastore書き込みを自動化する処理について見ていきます。

※サンプルサイトでの操作では、特に最初の参照などで時間がかかる場合がありますが、これはApp Engineインスタンス初期起動等での時間消費と考えられます。

有限会社サイバースペース
慶應義塾大学工学部電気科卒。日本IBM、日本HPなどにおいて、製造装置業を中心とした業務系/基幹業務系システムのSE/マーケティングや、3階層C/Sアーキテクチャによる社内業務システム開発などに携わる。現在は、Ajax/Web 2.0関連のセミナー講師/コンサルティング、書籍執筆などを行っている。情報処理学会会員。http://www.at21.net/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています