Google DocsをDBとして使うアプリケーションを作成してみた

YoutubeBlogger、CalenarなどGoogleのサービスを利用するGoogle Data APIというのがあるらしいです。
その一つにGoogle Documentsのスプレッドシートを操作するAPIがあり、今回はそのAPIを使って簡単な食事記録アプリケーションを作ろうと思います。

インストール

easy_installが使えるなら
$ easy_install gdata
で終わり。ないならhttp://code.google.com/p/gdata-python-client/ から適当に持ってくる。

テーブル定義

次にアプリケーションで使うDBもといスプレッドシートを作成します。Google Docsにログインして、eatLoggingというブックを作成して、sheetという名前のシートを作成します。

次に、以下のフィールドを定義します。

  • date (日付)
  • item (品目)
  • type (種類)
  • price(値段)
  • note (備考)

こんな感じ。あとは適当にCGIを動かせるサーバを用意してAPIを叩くスクリプトを動作させればアプリケーションができるわけだ。

API操作

スプレッドシートを操作するAPI

import gdata.spreadsheet.text_db

で利用可能。このAPIを使うとスプレッドシートをあたかも簡易なDBのように使えるようになります。SQLみたいなクエリ言語は無くて辞書オブジェクトをレコードにしてそのまま突っ込む感じ。

record = {'name': 'alice', 'age': 20}
client = gdata.spreadsheet.text_db.DatabaseClient(uid, pwd)
db     = client.GetDatabases(name=databasename)[0]
tbl    = db.GetTables(name=tablename)[0]
tbl.AddRecord(record)

こんな感じでデータを追加できる。あとはバリデーションとか適当にやるだけ。ソースコードは下参照で。

動作確認

PCや携帯からページを開いて、データを入力して送信します。すると、Google Docsのデータが更新されるのを確認できます。


記入



送信


リアルタイムに変更を確認できる

これで、外出先とかでも携帯orスマホを開いて食事の記録をこまめに取れば、あとから日付で集約して日ごとの食費とか日当たりの食費の平均を簡単に出したり、簡単な食事のレビュー(肉食い過ぎetc)とかできるわけです!すばらしい!

Google Docsのフォーム機能との違い

似た機能がGoogle Docsのフォームで利用可能です。ただフォーム機能はデフォルト値を設定できないので、たとえばそれぞれの項目に「デフォルト値は0」とか、「本日の日付」とか指定したい場合はすべて手入力になってしまいます。携帯で日付を入力するなんて面倒すぎてやってられないので公式でデフォルト値を使えるようになればいいのにって思います。というかこのアプリケーションを作った理由がそもそも「携帯で日付を入力するのが死ぬほどめんどくさい」というのが理由だったりして。

モデル記述するとバリデーションとレポート作成までできるみたいな機能があればいいなあ。なにそのMS Accessって感じで。
外部キーも張れるようにしてORM作ったらおもしろいかもなあ。

ソースコード

コードではテーブル作成もアプリケーション側でやろうとしていますが、XML関連らしいエラーのレスポンスが返ってくるので諦めて手作業でテーブルを作成することにしています。使い方が悪いのかライブラリが悪いのか・・・

#! /usr/bin/python2.6
# coding: utf-8

uid = '(Google Accountのメールアドレス)'
pwd = '(Google Accountのパスワード)'
databasename = u'eatLogging'
tablename = u'sheet'
serialkey = '(ランダムな文字列)'

##############
import os
import cgi
import time
import gdata.spreadsheet.text_db

fs = cgi.FieldStorage()

tmpl = """
<html>
  <head>
    <meta http-equiv='content-type' content='text/html; charset=utf-8'/>
    <title>食事管理</title>
  </head>
  <body>
    <h1>食事管理</h1>
    <form action="%(scriptname)s%(serialkey)s" method="post">
      <table>
        <tr><td>日付</td><td><input  name="date" type="text" value="%(today)s"/></td></tr>
        <tr><td>種類</td><td><select name="type">
              <option value="間食">間食</option>
              <option value="朝食">朝食</option>
              <option value="昼食">昼食</option>
              <option value="夕食">夕食</option>
              <option value="その他">その他</option>
            </select>
            </td></tr>
        <tr><td>品目</td><td><input  name="item" type="text"/></td></tr>
        <tr><td>金額</td><td><input name="price" type="text" /></td></tr>
        <tr><td>備考</td><td><textarea name="note"></textarea></td></tr>
        <tr><td></td><td>
          <input type="hidden" name="posted" value="posted"/>
          <input type="submit" value="submit" />
        </td></tr>
      </table>
    </form>
    <div>
      <span>%(accept)s</span>
    </div>
  </body>
</html>
"""


def main():
    dic = {
        'scriptname': os.environ['SCRIPT_NAME'],
        'accept': u'',
        'today': time.strftime('%Y/%m/%d'),
        'serialkey': '?_serialkey=' + serialkey,
    }
    print 'content-type: text/html'
    print ''
    # validate serial key
    valid = True
    f.write('start\n')
    if not '_serialkey' in fs:
        valid = False
    if fs.getvalue('_serialkey') != serialkey:
        valid = False
    if not valid:
        print 'invalid'
        return
    
    if not 'posted' in fs:
        print tmpl % dic
        return
    # handle post
    keys = [('date', 1, 0), ('item', 1, 0), ('type', 1, 0), ('note', 0, '') , ('price', 0, 0)]
    record = dict((key, '') for key,_,_ in keys)
    for key,required,default in keys:
        if required == 1 and not key in fs:
            valid = False
            break
        if not key in fs:
            record[key] = unicode(default)
        else:
            record[key] = unicode(fs.getvalue(key))
    if not valid:
        print 'invalid'
        return
    client = gdata.spreadsheet.text_db.DatabaseClient(uid, pwd)
    if client.GetDatabases(name=databasename) == []:
        # create
        client.CreateDatabase(databasename)
    db = client.GetDatabases(name=databasename)[0]
    if db.GetTables(name=tablename) == []:
        db.CreateTable(tablename, [key for key in keys])
    tbl = db.GetTables(name=tablename)[0]
    tbl.AddRecord(record)
    
    dic['accept'] = u'送信しました'
    print tmpl % dic

main()