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)

01package com.google.gdata;
02import java.io.IOException;
03import java.io.PrintWriter;
04import javax.servlet.http.*;
05@SuppressWarnings("serial")
06public class SpreadServlet extends HttpServlet {
07  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
08    resp.setContentType("text/plain");
09      resp.setContentType("text/html; charset=utf-8");
10      PrintWriter out = resp.getWriter();
11      String mode = req.getParameter("mode");
12      String sheet = req.getParameter("sheet");
13      SpreadBean ss = new SpreadBean();
14      ////////////////////////////////////////////////////////////////////
15      //  For Sales data
16      if (mode.equals("getdsallshopym")) {
17        String rv = ss.getDsAllShopYm(sheet);       //(1)
18        out.println(rv);
19      } else if (…) {
20        :
21      }
22  }
23   
24  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
25    resp.setContentType("text/plain");
26    resp.setContentType("text/html; charset=utf-8");
27    PrintWriter out = resp.getWriter();
28    String mode = req.getParameter("mode");
29    String sheet = req.getParameter("sheet");
30    SpreadBean ss = new SpreadBean();
31    ////////////////////////////////////////////////////////////////////
32    //  For Sales data
33    if (mode.equals("addspreadallshoptodsym")) {
34      String rv = ss.addSpreadAllShopToDsYm(sheet);     //(2)
35      out.println(rv);
36    } else if (…) {
37      :
38    }
39  }
40}

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

1.2 ビーンズコード記述

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

01public String addSpreadAllShopToDsYm(String sheet)  {
02  // このアプリケーションの名称。任意の名前を設定
03  String appliName = "cyberspace-SpreadsheetSearch-1";
04  String username = "xxxxxxxxxxxxxxxxxxxxxxxxx";
05  String password = "xxxxxxxxxxxxxxx";
06  try {
07    /////////////////////////////////////////////////////////
08    //  [I] Driveにあるスプレッドデータを読み取る
09    //  認証処理
10    SpreadsheetService spreadsheetservice = new SpreadsheetService(appliName);
11    spreadsheetservice.setUserCredentials(username, password);
12    // 検索対象のスプレッドシートを特定
13    FeedURLFactory feedurlfactory = FeedURLFactory.getDefault();
14    SpreadsheetQuery spreadsheetquery =
15    new SpreadsheetQuery(feedurlfactory.getSpreadsheetsFeedUrl());
16    spreadsheetquery.setTitleQuery(sheet); // 検索対象のスプレッドシート名を指定している
17    SpreadsheetFeed spreadFeed =
18      spreadsheetservice.query(spreadsheetquery, SpreadsheetFeed.class);
19    SpreadsheetEntry spreadsheetentry = spreadFeed.getEntries().get(0);
20    // 検索対象のワークシートを特定
21    WorksheetEntry worksheetentry = spreadsheetentry.getDefaultWorksheet();
22    //  クエリでワークシート内を検索
23    ListQuery listquery = new ListQuery(worksheetentry.getListFeedUrl());
24    ListFeed listfeed = spreadsheetservice.query(listquery, ListFeed.class);
25    /////////////////////////////////////////////////////////
26    //     [II] 読み取ったスプレッドデータをDatastoreに書き込む
27    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();            //(1)
28    int i = 0;
29    for (ListEntry listentry : listfeed.getEntries()) {
30      CustomElementCollection customelementcollection = listentry.getCustomElements();
31                                                    //(2)
32      DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
33      String moddate = df.format(new Date());
34      Entity sales = null;
35      if (!customelementcollection.getValue("店舗名").equals("売上合計")) {    //(3)
36        Key saleskey = KeyFactory.createKey                             //(4)
37        ("salesym", sheet + customelementcollection.getValue("店舗名"));
38        sales = new Entity(saleskey);                                       //(5)
39        sales.setProperty("seq", i++);
40      } else {
41        Key saleskey = KeyFactory.createKey
42        ("salesymtotal", sheet + customelementcollection.getValue("店舗名"));   //(6)
43        sales = new Entity(saleskey);                                       //(7)
44      }
45      sales.setProperty("shop", customelementcollection.getValue("店舗名"));       //(8)
46      sales.setProperty("yyyymm", sheet);
47      sales.setProperty("food", customelementcollection.getValue("食品"));
48      sales.setProperty("electric", customelementcollection.getValue("家電"));
49      sales.setProperty("bedding", customelementcollection.getValue("寝具"));
50      sales.setProperty("other", customelementcollection.getValue("その他"));
51      sales.setProperty("moddate", moddate);
52      ds.put(sales);                                                        //(9)
53    }
54    return "スプレッドデータのDatastore登録成功";
55  } catch (AuthenticationException e) {
56    e.printStackTrace();
57    return "スプレッドデータのDatastore登録不成功 :" + e;
58  } catch (IOException e) {
59    e.printStackTrace();
60    return "スプレッドデータのDatastore登録不成功 :" + e;
61  } catch (ServiceException e) {
62    e.printStackTrace();
63    return "スプレッドデータのDatastore登録不成功 :" + e;
64  }
65}

[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)

01public String getDsAllShopYm(String yyyymm) throws IOException, ServiceException {
02  try {
03    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();                //(1)
04    Filter ymFilter = new FilterPredicate("yyyymm", FilterOperator.EQUAL, yyyymm);  //(2)
05    Query q = new Query("salesym").setFilter(ymFilter).addSort("seq", SortDirection.ASCENDING);
06                                                                                      //(3)
07    PreparedQuery pq = ds.prepare(q);                                                   //(4)
08    String rv = "[";                                                                    //(7)
09    for (Entity res : pq.asIterable()) {                                                //(5)
10      String shop = (String) res.getProperty("shop");                                   //(6)
11      String food = (String) res.getProperty("food");
12      String electric = (String) res.getProperty("electric");
13      String bedding = (String) res.getProperty("bedding");
14      String other = (String) res.getProperty("other");
15      rv    += "[\"" + shop + "\","
16          + "\"" + food + "\","
17          + "\"" + electric + "\","
18          + "\"" + bedding + "\","
19          + "\"" + other + "\"],";                        //(7)
20    }
21    rv = rv.substring(0, rv.length() - 1) + "]";                                        //(7)
22    return rv;                                                                      //(8)
23  } catch (Exception e) {
24    e.printStackTrace();
25    return "参照不成功 Exception:" + e;
26  }
27}

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)

001<!DOCTYPE html>
002<html>
003<head>
004<meta charset="utf-8"/>
005<title> modDsSpreadAllShopYm </title>
006<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
007<link rel="stylesheet" href="../css/aw.css"/>
008<style type="text/css"/>
009#sgrid .aw-grid-row {height: 20px; border-bottom: 1px solid #ccc}
010#sgrid .aw-alternate-even {background: #fff;}
011#sgrid .aw-alternate-odd {background: #ddf;}
012#sgrid .aw-alternate-even .aw-column-0 {background: #ddf;}
013#sgrid .aw-alternate-odd .aw-column-0 {background: #ccf;}
014#sgrid .aw-rows-selected {background: #316ac5;}
015#sgrid .aw-rows-selected .aw-column-1 {background: #316ac5;}
016#sgrid .aw-column-0 {width: 75px; text-align: center; border-right: 1px dotted #ccc;}
017#sgrid .aw-column-1 {width: 65px; text-align: center; border-right: 1px dotted #ccc;}
018#sgrid .aw-column-2 {text-align: right}
019#sgrid .aw-column-3 {text-align: right}
020#sgrid .aw-column-4 {text-align: right}
021#sgrid .aw-column-5 {text-align: right}
022#sgrid .aw-column-6 {text-align: right}
023#sgrid .aw-mouseover-cell {color: red;}
024#sgrid .aw-grid-row .aw-cells-selected {background: #316ac5;}
025#sgrid .aw-grid-headers {color: blue}
026#sgrid .aw-grid-headers .aw-column-1 {font-weight: bold}
027#sgrid .aw-mouseover-header {color: red;}
028#sgrid .aw-mousedown-header {color: yellow;}
029#sgrid .aw-header-1 {background: #def}
030#sgrid .aw-row-selector {width: 20px; text-align: center}
031#sgrid .aw-row-2 .aw-row-selector {font-weight: bold}
032#sgrid .aw-mouseover-row .aw-row-selector {color: red;}
033#sgrid .aw-mouseover-selector {background: green;}
034#sgrid .aw-mousedown-selector {background: yellow;}
035#sgrid {font-size: 10pt}
036</style>
037<script type="text/javascript" src="../js/jquery-2.0.3.min.js"></script>
038<script type="text/javascript" src="../js/aw.js"></script>
039<script type="text/javascript">
040$(function() {
041  $("#addDs").click(function() {                              //(1)スプレッド登録
042    $("#dsp").html("<div id='stat'></div>")
043    var query = {};
044    query["mode"] = "addspreadallshoptodsym";
045    query["sheet"] = $("#year").val() + $("#month").val();
046    $.post("/spread", query, function(res) {
047      $("#stat").text(res);
048    });
049  });
050  $("#revSp").click(function() {                  //(2)ドライブのスプレッドデータ参照
051    getdata("getspreadallshopym");
052  });
053  $("#revDs").click(function() {                  //(3)Datastoreのスプレッドデータ参照
054    getdata("getdsallshopym");
055  });
056  function getdata(mode) {                        //(4) 参照処理関数
057    $("#dsp").html("<div id='sgrid'></div>")
058    var query = {};
059    query["mode"] = mode;
060    query["sheet"] = $("#year").val() + $("#month").val();
061    $.get("/spread", query, function(resp) {
062      var r = $.parseJSON(resp);      //(5)受信したJSONデータをjavaScriptオブジェクトに変換            var rows = 16;
063      var cols = 7;
064      var cdat = new Array(rows);
065      for (j = 0; j < rows; j++) {
066        cdat[j] = new Array(cols);            //(6)表示データ格納用に2次元配列生成
067      }
068      cdat[0][0] = "関東地区";
069      cdat[5][0] = "近畿地区";
070      cdat[10][0] = "中国地区";
071      if (mode == "getspreadallshopym") {     //(7)Driveスプレッドデータの配列セット
072        for (var i = 0; i < r.wsdat.length ; i++) {
073          var idat = r.wsdat[i].data.split(",");      //(8)
074          for (var j = 0; j < idat.length; j++) {
075          var vdat = idat[j].split(":")[1];       //(9)
076            cdat[i][j+1] = vdat;              //(10)
077          }
078        }
079      } else if (mode == "getdsallshopym") {  //(11)Datastore格納スプレッドの配列セット
080        for (var i = 0; i < r.length; i++) {
081          var rtotal = 0;
082          for (var j = 0; j < r[i].length; j++) {
083            if (j > 0) {
084              rtotal += parseInt(r[i][j]);            //(11)  行合計の加算
085            }
086            cdat[i][j+1] = r[i][j];                   //(12)セル値の配列セット
087          }
088        cdat[i][j+1] = rtotal;                            //(13)行合計の集計値セット
089        }
090        cdat[15][1] = "売上合計";
091        for (var i = 2; i < cols; i++) {       //(14)列合計値の計算
092            var ctotal = 0;
093            for (var j = 0; j < rows - 1 ;j++) {
094                ctotal += parseInt(cdat[j][i]);
095            }
096            cdat[rows -1][i] = ctotal;        //(15)列合計の集計値セット
097          }
098        }
099        var headers = ["地域名", "店舗名", "食品", "家電", "寝具", "その他", "合計"];
100                                            //(16)
101        var grid = new AW.Grid.Extended;
102        grid.setId("sgrid");
103        var gridw = 28 + 93 * cols;
104        var gridh = 28 + 26 * rows;
105        grid.setControlSize(gridw, gridh);        // グリッド幅と高さ指定
106        grid.setCellText(cdat);                   //表示データの配列を指定
107        grid.setCellEditable(true);               //グリッドのセルを編集可能に指定
108        grid.setHeaderText(headers);          //ヘッダ表示の配列を指定
109        grid.setRowCount(rows);               // 行数指定
110        grid.setColumnCount(cols);            // 列数指定
111        grid.setSelectorText(function(i){return this.getRowPosition(i);}); // 行ヘッダ表示
112        grid.setSelectorVisible(true);            //行ヘッダの表示指定
113        document.getElementById("sgrid").innerHTML = grid;    //(17)
114      });
115    }
116  });
117  </script>
118  </head>
119  <body>
120  <h3>Spreadsheet データ登録・参照</h3>
121  年<select id="year">
122    <option value="">=年選択=</option>
123    <option value="2013">2013年</option>
124    <option value="2012">2012年</option>
125    <option value="2011">2011年</option>
126  </select>
127  月<select id="month">
128      <option value="">=月選択=</option>
129    <option value="01">1月</option>
130    <option value="02">2月</option>
131    <option value="03">3月</option>
132    <option value="04">4月</option>
133    <option value="05">5月</option>
134    <option value="06">6月</option>
135    <option value="07">7月</option>
136    <option value="08">8月</option>
137    <option value="09">9月</option>
138    <option value="10">10月</option>
139    <option value="11">11月</option>
140    <option value="12">12月</option>
141  </select>
142  <input type="button" id="addDs" value=" スプレッド登録  " />
143   <input type="button" id="revSp" value=" スプレッド参照  " />
144  <input type="button" id="revDs" value="データストア参照 " />
145  <hr />
146  <div id="dsp"></div>
147  </body>
148</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)

001<!DOCTYPE html>
002<html>
003<head>
004<meta charset="utf-8"/>
005<title> modDsSpreadAllShopYmPq </title>
006<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
010<link rel="stylesheet" href="../css/pqgrid.min.css" />
011<script type="text/javascript" src="../js/pqgrid.min.js"></script>
012<script type="text/javascript">
013$(function() {
014  $("#addDs").click(function() {                                  //(1)スプレッド登録
015    $("#dsp").html("<div id='stat'></div>")
016    var query = {};
017    query["mode"] = "addspreadallshoptodsym";
018    query["sheet"] = $("#year").val() + $("#month").val();
019    $.post("/spread", query, function(res) {
020      $("#stat").text(res);
021    });
022  });
023  $("#revSp").click(function() {                          //(2)ドライブのスプレッドデータ参照
024    getdata("getspreadallshopym");
025  });
026  $("#revDs").click(function() {                          //(3)Datastoreのスプレッドデータ参照
027    getdata("getdsallshopym");
028  });
029  function getdata(mode) {                                //(4)参照処理関数
030    $("#dsp").html("<div id='sgrid'></div>")
031    var query = {};
032    query["mode"] = mode;
033    query["sheet"] = $("#year").val() + $("#month").val();
034    $.get("/spread", query, function(resp) {              //(5)
035      var r = $.parseJSON(resp);
036      var rows = 16;
037      var cols = 7;
038      var data = new Array(rows);
039      for (j = 0; j < rows; j++) {
040        data[j] = new Array(cols);                //(6)表示データ格納用に2次元配列生成
041      }
042      data[0][0] = "関東地区";
043      data[5][0] = "近畿地区";
044      data[10][0] = "中国地区";
045      if (mode == "getspreadallshopym") {         //(7)Driveスプレッドデータの配列セット
046        for (var i = 0; i < r.wsdat.length ; i++) {
047          var idat = r.wsdat[i].cdat.split(",");  //(8)
048          for (var j = 0; j < idat.length; j++) {
049            var vdat = idat[j].split(":")[1];         //(9)
050            data[i][j+1] = vdat;                  //(10)
051          }
052        }
053      } else if (mode == "getdsallshopym") {  //(11)Datastore格納スプレッドの配列セット
054        data[15][1] = "売上合計";
055        for (var i = 0; i < r.length; i++) {
056          var rtotal = 0;
057          for (var j = 0; j < r[i].length; j++) {
058            if (j > 0) {
059              rtotal += parseInt(r[i][j]);            //(11)  行合計の加算
060            }
061            data[i][j+1] = r[i][j];                       //(12)セル値の配列セット
062          }
063          data[i][j+1] = rtotal;                          //(13)行合計の集計値セット
064        }
065        for (var i = 2; i < cols; i++) {                       //(14)列合計値の計算
066          var ctotal = 0;
067          for (var j = 0; j < rows - 1 ;j++) {
068            ctotal += parseInt(data[j][i]);
069          }
070          data[rows - 1][i] = ctotal;                     //(15)列合計の集計値セット
071        }
072      }
073      var obj = {};
074      obj.width = 635;                                            //(16)
075      obj.height = 435;
076      obj.colModel = [{title:"地域名", width:80, dataType:"string"}, //(17)
077      {title:"店舗名", width:80, dataType:"string"},
078      {title:"食品 ", width:80, dataType:"integer", align:"right"},
079      {title:"家電 ", width:80, dataType:"integer", align:"right"},
080      {title:"寝具 ", width:80, dataType:"integer", align:"right"},
081      {title:"その他 ", width:80, dataType:"integer", align:"right"},
082      {title:"合計", width:80, dataType:"integer", align:"right"}];
083      obj.dataModel = {data:data};
084      $("#sgrid").pqGrid(obj);                            //(18)レンダリング
085    });
086  }
087});
088</script>
089</head>
090<body>
091<h3>Spreadsheet データ登録・参照</h3>
092年<select id="year">
093  <option value="">=年選択=</option>
094  <option value="2013">2013年</option>
095  <option value="2012">2012年</option>
096  <option value="2011">2011年</option>
097</select>
098月<select id="month">
099    <option value="">=月選択=</option>
100  <option value="01">1月</option>
101  <option value="02">2月</option>
102  <option value="03">3月</option>
103  <option value="04">4月</option>
104  <option value="05">5月</option>
105  <option value="06">6月</option>
106  <option value="07">7月</option>
107  <option value="08">8月</option>
108  <option value="09">9月</option>
109  <option value="10">10月</option>
110  <option value="11">11月</option>
111  <option value="12">12月</option>
112</select>
113<input type="button" id="addDs" value=" スプレッド登録  " />
114 <input type="button" id="revSp" value=" スプレッド参照  " />
115<input type="button" id="revDs" value="データストア参照 " />
116<hr />
117<div id="dsp"></div>
118</body>
119</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メルマガ会員のサービス内容を見る

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