Puhser + Google App EngineでリアルタイムWebアプリ その2

WebSocketで目指せ“リアルタイムWeb”!」で取り上げられているRetrospectiveappのサーバサイド(Heroku + Sinatra)をGoogle App Engine + Bottleで実装してみる。
クライアントサイドは元ネタとほぼ同じ。
デモ : http://myretrospective.appspot.com/

Model

from google.appengine.ext import db

class Note(db.Model):
  x = db.IntegerProperty(required = True)
  y = db.IntegerProperty(required = True)
  w = db.IntegerProperty(required = True)
  h = db.IntegerProperty(required = True)
  angle = db.IntegerProperty(required = True)
  text = db.StringProperty(required = True, multiline = True)
  color = db.StringProperty(required = True)

App

import os
import re
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import bottle
from bottle import route, response, request, error, static_file
from django.utils import simplejson
from pusher import Pusher, Channel
import dbutils
from model import Note

bottle.debug(True)

APP_ID = 'app_id'
KEY = 'key'
SERCRET = 'secret'
CHANNEL_NAME = 'retrospectiveapp-test'

pusher= Pusher(APP_ID, KEY, SERCRET)
channel = Channel(CHANNEL_NAME, pusher)    

@route('/')
def index():
  return static_file('index.html', os.path.dirname(__file__))

@route('/notes', method = 'GET')
def notes_get():
  notes = [dbutils.to_dict(note) for note in Note.all()]
  if notes:  
    return simplejson.dumps(notes)
  return {}

@route('/notes', method = 'POST')
def notes_post():  
  x = request.POST.get('note[x]', '')
  y = request.POST.get('note[y]', '')
  w = request.POST.get('note[w]', '')
  h = request.POST.get('note[h]', '')
  text = request.POST.get('note[text]', '')
  angle = request.POST.get('note[angle]', '')
  color = request.POST.get('note[color]', '')

  note = Note(x = int(x),
              y = int(y),
              w = int(w),
              h = int(h),
              text = text,
              angle = int(float(angle)),
              color = color
              )
  note.put()

  channel.trigger('note-create', dbutils.to_dict(note))

  return dbutils.to_dict(note)

@route('/notes/:id/softupdate', method = 'PUT')
def softupdate(id):
  text = request.POST.get('n[text]', '')
  socket_id = request.POST.get('socket_id', '')
  
  channel.trigger('note-softupdate', {'text' : text, 'socket_id' : socket_id})
  return {'text' : text}

@route('/notes/:id', method = 'PUT')
def notes_id_post(id):
  n = Note.get_by_id(int(id))

  x = request.POST.get('note[x]', '')
  y = request.POST.get('note[y]', '')
  w = request.POST.get('note[w]', '')
  h = request.POST.get('note[h]', '')  
  text = request.POST.get('note[text]', '')
  
  if (text):
    n.text = unicode(text.decode('utf-8'))
  elif (x and y and w and h):
    n.x = int(x)
    n.y = int(y)
    n.w = int(w)
    n.h = int(h)
  n.put()
  
  channel.trigger('note-update', dbutils.to_dict(n))
  
  return dbutils.to_dict(n)

@route('/notes/:id', method = 'DELETE')
def notes_id_delete(id, method = 'DELETE'):
  n = Note.get_by_id(int(id))
  n.delete()
  
  channel.trigger('note-destroy', {'id' : id})
  
  return {'id' : id}

@error(404)
@error(403)
def mistake404(code):
  return 'error'
    
if __name__ == '__main__':
  app = bottle.app()
  run_wsgi_app(app)

Modelをdictに変換するためにKay Frameworkdbutilsをちょっと改造して使ってる。

from google.appengine.ext import db

import datetime
import time

#SIMPLE_TYPES = (int, long, float, bool, dict, basestring, list)
SIMPLE_TYPES = (int, long, float, bool, dict, list)

def to_dict(model):
  output = {}

  #set id
  output['id'] = model.key().id()

  for key in model.properties().iterkeys():
    value = getattr(model, key)
    if isinstance(value, basestring):
      output[key] = unicode(value)
    elif value is None or isinstance(value, SIMPLE_TYPES):
      output[key] = value
    elif isinstance(value, datetime.date):
      # Convert date/datetime to ms-since-epoch ("new Date()").
      ms = time.mktime(value.utctimetuple()) * 1000
      ms += getattr(value, 'microseconds', 0) / 1000
      output[key] = int(ms)
    elif isinstance(value, db.Model):
      output[key] = to_dict(value)
    else:
      output[key] = str(value)
  return output

感想

あとGitHubにホストされているコードを他の言語に移植したときってforkしていいんかな?って疑問におもた。したほうがいいんかな?