↑試作品なので消える可能性あり
こちらで動かしています。
http://sugi1982.appspot.com/
どちらも共通、リロードされるたびに数字がインクリメントされます。
SDKのversion 1.2.1 からimages APIがパワーアップして、画像を合成することができるようになったとか。
- The Images API now supports compositing images and calculating a color histogram for an image.
ということは内部で数字の画像をつなぎ合わせて、アクセスされるたびに画像の数字が変わるカウンターができるんじゃないか?と思って作ってみた。
まだPythonに慣れてなかったりGoogle App Engineにも慣れてなかったりで予想以上に時間と手間がかかってしまったけど…orz
作りは単純で、
- 内部で0~9に該当する画像データを持つ(管理者権限で画像をupload)
- アクセスカウンターのModelを作成してアクセス数を保持する
- getを受けたときにカウンターの数字をインクリメントさせ、その数字の各桁に対応する画像データを作成、つなぎ合わせ出力
というだけ。
とりあえずアクセス数だけはこうやって取れるようになった。せっかくなので
- ユーザー毎にURLを作成して独立して動くようにさせたり
- 画像のデザインやサイズを調整したりできるように
ということも考慮したい。
あとアクセス解析的なものはどこまでできるんだろう…?ちょっとそこまで作ろうと思ったけど各アクセス毎に時間やUserAgentをレコードに残すとなるとカウンターのインクリメントと同時にトランザクションで処理して…とか考えたらよくわからなくなったのでもう1回勉強し直してじっくり検討する。
ソースを以下にべたべたと貼ってみる。
コメントも書いてないし自分で見直しもしていないので色々イケてない部分があるかと思います。
これはマズいだろ、などといった点がありましたらご指摘いただけると幸いです。
- app.yaml
application: sugi1982 version: 1 runtime: python api_version: 1 handlers: - url: /uploader script: main.py login: admin - url: /.* script: main.py
- main.py
# -*- coding: utf-8 -*- import logging import os from google.appengine.ext import webapp from google.appengine.ext.webapp import template from google.appengine.ext.webapp.util import run_wsgi_app from counter import Counter from uploader import Uploader from viewer import Viewer class MainPage(webapp.RequestHandler): def get(self): template_values = { } path = os.path.join(os.path.dirname(__file__), 'templates', 'index.html') self.response.out.write(template.render(path, template_values)) application = webapp.WSGIApplication([ ('/counter/.*', Counter), ('/view/.*', Viewer), ('/uploader', Uploader), ('/', MainPage), ], debug = True) def main(): logging.getLogger().setLevel(logging.DEBUG) webapp.template.register_template_library('templatefilters') run_wsgi_app(application) if __name__ == "__main__": main()
- counter.py
# -*- coding: utf-8 -*- import logging from google.appengine.api import images from google.appengine.ext import db from google.appengine.ext import webapp from numbers import Numbers from record import Record class Counter(webapp.RequestHandler): def get(self): record = Record.get_or_insert("test") key = db.run_in_transaction(self.increment_count, record.key()) number = Record.get(key).count digits = [] while number / 10 != 0: digits.append(number % 10) number /= 10 digits.append(number) digits.reverse() image_list = [] numbers = Numbers.get_by_key_name("test") for i in range(len(digits)): image_list.append((numbers.digits[digits[i]], 64 * i, 0, 1.0, images.TOP_LEFT,)) image = images.composite(image_list, 64 * len(digits), 128) self.response.headers['Content-Type'] = 'image/png' self.response.out.write(image) def increment_count(self, key): record = Record.get(key) if record.count == None: record.count = 1 else: record.count += 1 return record.put()
- numbers.py
# -*- coding: utf-8 -*- from google.appengine.ext import db class Numbers(db.Model): digits = db.ListProperty(db.Blob, default = [db.Blob()] * 10)
- record.py
# -*- coding: utf-8 -*- from google.appengine.ext import db class Record(db.Model): count = db.IntegerProperty() datetime = db.DateTimeProperty(auto_now = True)
- uploader.py(管理者しかアクセスできない…はず)
# -*- coding: utf-8 -*- import os import logging from google.appengine.api import images from google.appengine.ext import db from google.appengine.ext import webapp from google.appengine.ext.webapp import template from numbers import Numbers class Uploader(webapp.RequestHandler): key = "test" def get(self): template_values = { } path = os.path.join(os.path.dirname(__file__), 'templates', 'upload.html') self.response.out.write(template.render(path, template_values)) def post(self): num = int(self.request.get('num')) logging.debug(num) try: image = images.Image(self.request.get('image')) logging.debug(image) image.resize(64, 128) numbers = Numbers.get_or_insert(self.key) numbers.digits[num] = db.Blob(image.execute_transforms()) numbers.put() except images.NotImageError: logging.error(images.NotImageError) self.redirect(self.request.url)