原口 豊(はらぐち・ゆたか)
大手証券会社システム部に在籍後、1998年ベイテックシステムズ(現サテライトオフィス)を設立し、社長就任。2008年に、いち早くクラウドコンピューティングの可能性に注目し、GoogleApps(TM)導入サポートを開始。導入実績は、ガリバー、アデランス、三井倉庫などの大手企業から、中堅・中小企業まで、1,200社以上。「組織&グループカレンダー for Google Apps」など、多数のテンプレートを無償提供するなど、Google Appsの普及に尽力。Google Enterprise Day 2011ではパートナーアワードを3年連続で受賞した。
フォームの内容を反映するスプレッドシート作成
今回はGoogle App Engineを使った応用編として、フォームへ入力した内容を任意のスプレッドシートへ書き込むと同時に、ユーザーへメール送信するというテクニックを紹介したい。これは前々回に取り上げた「メール送信」、前回に取り上げた「スプレッドシートへの書き込み」を応用したものだ。
これまでの記事をご覧いただけばわかると思うが、まずはWebアプリケーションの登録を済ませておく。次に、フォームの内容を書き込むためのスプレッドシートを用意する。こちらは特に細かな設定が必要なわけではなく、フォームで入力させたい項目を事前に書き込んでおくだけでOK。
今回はテストケースとして、入力フォームで使いがちな「投稿日/姓/名/セイ/メイ/メールアドレス/郵便番号/都道府県/市区町村/電話番号/性別/生年月日/その他」という13項目を設定してみた。
フォーム用のスクリプトやHTLMファイルを用意
用意するファイルは、前回使った「init.py」「app.yaml」「index.py」、Python用のライブラリ「gdata-python-client」から抽出した「atom」および「gdata」という2つのフォルダに加え、フォーム投稿のメインとなる「entry.py」、フォーム入力完了時の画面表示用に「thanks.py」。さらに「templates」フォルダ内に2種類のHTMLファイル「index.html」と「thanks.html」、「css」フォルダ内にスタイルシート設定用の「style.css」、「images」フォルダ内にフォームで使用する画像を入れておく。
今回のサンプルには、サテライトオフィスが行っている「Google Apps プレミアエディション お友達紹介キャンペーン」のフォームを使用した。それでは以下、各ファイルの詳細について見ていこう。なお、HTMLおよびCSSファイルに関する説明は省いてあるが、興味がある方はサンプルファイルをダウンロードしてみていただきたい。
「__init__.py」
メール送信やスプレッドシートへ書き込みと同様に、中身は空のままで問題ない。
「app.yaml」
こちらは1行目「application:」のアプリケーションIDに加えて、読み込むファイルを下記のように記述する。
application: アプリケーションIDを入力
version: 1
runtime: python
api_version: 1
default_expiration: "4d 5h"
handlers:
- url: /favicon.ico
static_files: images/favicon.ico
upload: images/favicon.ico
- url: /images
static_dir: images
secure: optional
- url: /css/(.*)
static_files: css/\1
upload: css/(.*)
secure: optional
- url: /entry
script: entry.py
- url: /thanks
script: thanks.py
- url: /.*
script: index.py
「index.py」
今回はページの表示だけを行うため、「index.py」自体の内容は少ない。
# coding: utf-8
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
############################################################
## フォーム投稿サンプル 入力画面
############################################################
class Page(webapp.RequestHandler):
def get(self):
# ページの表示のみを行う
self.response.out.write(template.render('templates/index.html', {}))
def main():
run_wsgi_app(webapp.WSGIApplication([(r'.*', Page)]))
if __name__ == "__main__":
main()
「entry.py」
こちらが今回のメインとなるスクリプトだ。これまでの連載で紹介したメール送信およびスプレッドシートへの書き込みという2種類の要素が盛り込まれている。個々の環境に応じて、スプレッドシートキー、スプレッドシートを編集可能なユーザーアカウントのメールアドレスとパスワード、送信元メールアドレス、件名と本文を変更しよう。送信先メールアドレスは、ユーザーがフォーム上から入力したものが引用されるようになっている。必要ならばCCとBCCを設定することも可能だ。
# coding: utf-8
import datetime
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import gdata.spreadsheet.service
import gdata.service
import gdata.alt.appengine
import atom.service
import gdata.spreadsheet
from google.appengine.api import mail
############################################################
## フォーム投稿サンプル 内部処理(メール送信、スプレッドシートへの書き込み)
############################################################
class Page(webapp.RequestHandler):
def post(self):
try:
# 入力値を取得
lastname = unicode(self.request.get('lastname'))
firstname = unicode(self.request.get('firstname'))
lastname_yomi = unicode(self.request.get('lastname_yomi'))
firstname_yomi = unicode(self.request.get('firstname_yomi'))
mailaddress = unicode(self.request.get('mailaddress'))
post_number = unicode(self.request.get('post_number'))
province = unicode(self.request.get('province'))
city = unicode(self.request.get('city'))
phone = unicode(self.request.get('phone'))
gender = unicode(self.request.get('gender'))
birthday = unicode(self.request.get('birthday'))
other = unicode(self.request.get('other'))
#####################################################################
# ここから編集が必要な項目
#####################################################################
# スプレッドシート書き込み 設定
# スプレッドシートキーを設定
spreadsheet_key = 'ここにスプレッドシートキーを入力'
# スプレッドシートを編集可能なユーザーアカウントを設定
user_mail = 'ここにメールアドレスを入力'
# ユーザーアカウントのパスワードを設定
password = 'ここにパスワードを入力'
# 書き込み先のシート名を設定
# ''の前に u を付け、ユニコードに変換
# 変換を行わない場合、エラーが発生する
spreadsheet_name = u'シート1'
# メール送信 設定
kw = {}
# 送信先メールアドレス
# Toを設定(ユーザーが入力したメールアドレスへ送信)
kw['to'] = mailaddress
# Ccを設定(省略可能)
kw['cc'] = 'admin@domain.com'
# Bccを設定(省略可能)
kw['bcc'] = 'admin@domain.com'
# 送信元メールアドレス(Google App Engineの「owner」もしくは「developer」のメールアドレスが必要)
kw['sender'] = 'admin@domain.com'
# 件名
kw['subject'] = '件名'
# 本文はTEXT形式、HTML形式のどちらかを入力
# 本文 TEXT形式
# 入力された名前を、メール内に埋め込んでいます。
kw['body'] = lastname + u' ' + firstname + u'様へ\nサンプルメール:\nこれは本文です'
# 本文 HTML形式 (コメントアウト中)
#kw['html'] = 'サンプルメール:\nこれは<strong>本文</strong>です'
# メールの送信はワークシートの書き込み成功後に実行される
#####################################################################
# ここまで編集が必要な項目
#####################################################################
# 投稿時間を取得
now_date = unicode(datetime.datetime.today().strftime("%Y/%m/%d %H:%M:%S"))
# 書き込み内容のリストを設定(投稿時間 + ユーザーが入力した情報を設定)
values = [now_date ,lastname ,firstname ,lastname_yomi ,firstname_yomi ,mailaddress ,post_number ,province ,city ,phone ,gender ,birthday ,other]
# SpreadsheetsServiceを取得
service = gdata.spreadsheet.service.SpreadsheetsService()
gdata.alt.appengine.run_on_appengine(service, store_tokens=True,single_user_mode=True)
# 編集に使用するユーザーアカウントの情報を設定
service.email = user_mail
service.password = password
# 設定したユーザーアカウントでログインを実行
service.ProgrammaticLogin()
# スプレッドシートIDを取得
spreadsheet_id = self.getSpreadsheetIdByName(service, spreadsheet_key, spreadsheet_name)
# ワークシートIDが取得出来なかった場合、メッセージを表示して処理を終了
if spreadsheet_id == '':
self.response.out.write('error<br/>')
self.response.out.write('Spreadsheet not found.')
return
# ワークシートIDが取得できた場合、書き込み処理の実行
self.addSpreadsheetRecord(service, spreadsheet_key, spreadsheet_id, values)
# メール送信の実行
message = mail.EmailMessage(**kw)
message.send()
# 全ての処理が成功したら、完了ページへ移動
self.redirect('/thanks')
return
except BaseException, e:
self.response.out.write('error<br/>')
self.response.out.write(e)
return
def getSpreadsheetIdByName(self, service, spreadsheet_key, Spreadsheet_name):
u'''スプレッドシートIDを取得'''
# keyを使ってスプレッドシートを取得
feed = service.GetWorksheetsFeed(spreadsheet_key)
spreadsheet_entry = None
spreadsheet_id = ''
if not feed:
# 取得出来なかった場合、空欄を返却
return ''
# スプレッドシートが取得できた場合、書き込み先のシートを取得
for entry in feed.entry:
# 取得したいシート名と一致した場合、そのシートを取得
if(entry.title.text == Spreadsheet_name):
spreadsheet_entry = entry
break
# 取得したシートからスプレッドシートIDを取得
if spreadsheet_entry:
id_parts = spreadsheet_entry.id.text.split('/')
spreadsheet_id = id_parts[-1]
else:
# 取得出来なかった場合、空欄を返却
return ''
# 取得したスプレッドシートIDを返却
return spreadsheet_id
def addSpreadsheetRecord(self, service, spreadsheet_key, spreadsheet_id, values):
u'''スプレッドシートへ書き込み処理を実行'''
try:
data={}
# スプレッドシートのタイトル一覧(一列目の項目)を取得
titles = self.getSpreadsheetTitleList(service, spreadsheet_key, spreadsheet_id)
# 書き込み内容と、タイトル一覧を対応付ける
for i,value in enumerate(values):
if i < len(titles):
key = titles[i]
data[key] = value
else:
break
# スプレッドシートキー、スプレッドシートIDを使ってスプレッドシートへの書き込みを実行
return service.InsertRow(data, spreadsheet_key, spreadsheet_id)
except Exception, e:
raise e
def getSpreadsheetTitleList(self, service, spreadsheet_key, spreadsheet_id):
u'''スプレッドシートのタイトル一覧を取得'''
first_row_contents = []
#スプレッドシートの一行目(タイトル)を取得
query = gdata.spreadsheet.service.CellQuery()
query.max_row = '1'
query.min_row = '1'
feed = service.GetCellsFeed(spreadsheet_key, spreadsheet_id, query=query)
# 取得できた場合、タイトルのリストを作成
for entry in feed.entry:
first_row_contents.append(entry.content.text)
# タイトル一覧を格納したリストを返却
return first_row_contents
def main():
run_wsgi_app(webapp.WSGIApplication([(r'.*', Page)]))
if __name__ == "__main__":
main()
「thanks.py」
フォーム入力が完了した際に表示する画面を設定する。
# coding: utf-8
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
############################################################
## フォーム投稿サンプル 完了画面
############################################################
class Page(webapp.RequestHandler):
def get(self):
# ページの表示のみを行う
self.response.out.write(template.render('templates/thanks.html', {}))
def main():
run_wsgi_app(webapp.WSGIApplication([(r'.*', Page)]))
if __name__ == "__main__":
main()
「atom」フォルダ内の「__init__.py」
スプレッドシートへ書き込みと同様に、72行目・73行目を変更する。
変更前
MEMBER_STRING_ENCODING = 'utf-8'
MEMBER_STRING_ENCODING = unicode
↓
変更後
MEMBER_STRING_ENCODING = 'utf-8'
MEMBER_STRING_ENCODING = unicode
なお、「templates」フォルダ内の「index.html」、「templates」フォルダ内の「thanks.html」、「css」フォルダ内の「style.css」のサンプルファイルはダウンロード可能だ。これらのファイルを必要に応じて、書き換えていただきたい。
「images」フォルダ内の画像
前回・前々回とも「images」フォルダにはアイコン画像「favicon.ico」しか含まれていなかったが、今回はヘッダーやボタンなどの画像も入れておく。
ここまで用意ができれば、あとは今までと同じ方法でWebアプリケーションの追加およびフォルダ指定を実行。デプロイ後にブラウザでアクセスして、フォーム入力画面が表示されるかを確認しよう。
実際にフォームへの入力を済ませると、「thanks.html」の画面へと移行。同時にスプレッドシートへの反映と、ユーザーに対するメール送信が行われる。
ここまでの連載でGoogle App Engineを使ったメール送信、スプレッドシートへの書き込み、そして2つを組み合わせたフォームまでを順番に紹介してきた。個別の機能ではわかりづらかった部分も、具体的な形になると身近に感じられ、なおかつ幅広い活用性を持つことを理解できるだろう。
このようにGoogle App Engineを使うと、GoogleドキュメントやGmailなどサービス単位では困難だった、もしくはできなかったことも実現可能なのである。