Google App EngineでFizzBuzz

 Google App Engine(以下GAE)って無料で使えるわりにいろんな便利な機能があります。今回はそのGAEの便利機能を使ってFizzBuzzを作ってみた。
 まずはGoogleアカウント。最近はどこのサイトでもログインが必要。でも認証機能って一から作ると面倒だよね。そこでGAEではGoogle純正の認証機能が用意されてる。超便利。
http://code.google.com/intl/ja/appengine/docs/python/users/
 次はMemcache。GAEでデータを保存するためにはいちいちModelを定義しなくちゃいけない。永続的に保存しとくにはそれしかないけど、ちょっとだけ覚えておきたいデータとかもあるよね。そんなときに便利なのがMemcache。なによりModel書かなくてもいいのが簡単(複雑なデータは保存出来ないけど)。
http://code.google.com/intl/ja/appengine/docs/python/memcache/
 3番目はTaskqueue。GAEでもっとも頭を悩ませるのが30秒ルール。時間がかかる処理が出来ないってこと。そこでTaskqueueの出番となる。大きな処理は分割してキューで非同期に処理するってわけ。
http://code.google.com/intl/ja/appengine/docs/python/taskqueue/
 最後はメール。GAEだと超簡単にメール出せます。
http://code.google.com/intl/ja/appengine/docs/python/mail/
 これら4つの機能を組み合わせて作ったのが以下のFizzBuzz。まずGoogleアカウントでログインします。するとMemcacheでFizzBuzzの答えを保存する場所が確保されて、最初の数字1のFizzBuzzの答えを求めるためのリクエストをTaskqueueに追加します。Taskqueueから呼ばれたリクエストは、パラメータの数字のFizzBuzzを求めてMemcacheに保存。そして、インクリメントした数字と共にまたリクエストをTaskqueueに追加します。ドミノみたいな感じです。最後に100までFizzBuzzが求められたら、ログインしたときのGoogleアカウントのメールアドレスに答えを送信します。まさにGAEによるピタゴラスイッチ。これ、意外と早くメールが届くのでびっくりしました。
http://gist.github.com/307608

# -*- coding: utf8 -*-

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import memcache
from google.appengine.api import mail
from google.appengine.api.labs import taskqueue
from google.appengine.api import users

class FizzBuzzHandler(webapp.RequestHandler):
    def post(self):
        mailaddress = self.request.get('mailaddress')
        num = self.request.get('number')
        if not mailaddress or not num:
            return
        key = 'fizzbuzz' + mailaddress
        num = int(num)
        result = memcache.get(key)
        if result is None:
            return
        result += self.fizzbuzz(num) + '\n'
        if num < 100:
            memcache.set(key, result, 3600)
            taskqueue.add(url='/fizzbuzz2', params={'mailaddress': mailaddress, 'number': str(num+1)})
        else:
            mail.send_mail('hoge@foo.com', mailaddress, 'FizzBuzz Result', result)
    
    def fizzbuzz(self, num):
        if num % 3 == 0:
            if num % 5 == 0:
                return 'FizzBuzz'
            else:
                return 'Fizz'
        elif num % 5 == 0:
            return 'Buzz'
        else:
            return str(num)

class MainHandler(webapp.RequestHandler):
    def get(self):
        w = self.response.out.write
        w('<html><body>')
        user = users.get_current_user()
        if user:
            memcache.set('fizzbuzz'+user.email(), '', 3600)
            taskqueue.add(url='/fizzbuzz2', params={'mailaddress': user.email(), 'number': '1'})
            w(('処理が完了したら %s にメールを送ります' % user.email()))
            w(('<br /><a href="%s">ログアウト</a>' % users.create_logout_url('/fizzbuzz')))
        else:
            w((u'<a href="%s">ログインするとFizzBuzz開始</a>' % users.create_login_url('/fizzbuzz')))
        w('</body></html>')

application = webapp.WSGIApplication(
    [('/fizzbuzz2', FizzBuzzHandler),
     ('/fizzbuzz', MainHandler)],
    debug=True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()