GAEでアクセスカウンター
Google App Engine でアクセスカウンターを作ってみました。
#counter.py #encoding:shuft-jis from __future__ import with_statement, division import os import cgi import datetime import wsgiref.handlers from google.appengine.ext import db from google.appengine.api import users from google.appengine.ext import webapp from google.appengine.ext.webapp import template class JST(datetime.tzinfo): def utcoffset(self, dt):return datetime.timedelta(hours=9) def dst(self, dt):return datetime.timedelta(0) def tzname(self, dt):return "JDT" class UTC(datetime.tzinfo): def utcoffset(self, dt):return datetime.timedelta(0) def tzname(self, dt):return "UTC" def dst(self, dt):return datetime.timedelta(0) UTC = UTC() TZ = JST() class Accessing(db.Model): user = db.UserProperty() datetime = db.DateTimeProperty(auto_now_add=False) date = db.DateProperty(auto_now_add=False) ip = db.StringProperty() class CountOfDate(db.Model): date = db.DateProperty(auto_now_add=False) count = db.IntegerProperty(required=True, default=0) class TotalDelta(db.Model): value = db.IntegerProperty(required=True, default=0) class Counter(object): def append(self, _ip=None): accessing = Accessing() now = datetime.datetime.now(TZ) accessing.datetime = now accessing.date = now.date() accessing.ip = os.environ['REMOTE_ADDR'] if _ip is None else _ip accessing.put() self._update_today_count() def set_total(self, v): m = self._total_delta_model() m.value += v - self.total() m.put() def total(self): query = CountOfDate.all() return sum(c.count for c in query) + self._total_delta() def _total_delta_model(self): totals = list(TotalDelta.all()) if not totals: t = TotalDelta() t.value = 0 t.put() return t else: assert len(totals) == 1 return totals[0] def _total_delta(self): return self._total_delta_model().value def _today_model(self): return self._theday_model(self._today_date()) def _update_today_count(self): self._update_theday_count(self._today_date()) def _update_theday_count(self, date): from operator import attrgetter query = Accessing.all().filter("date =", date) count = len(set(q.ip for q in query)) m = self._theday_model(date) m.count = count m.put() def _theday_model(self, date): query = CountOfDate.all().filter("date =", date) q = list(query) if not q: c = CountOfDate() c.date = date c.count = 0 c.put() return c else: assert len(list(q)) == 1 return q[0] def _today_date(self): return datetime.datetime.now(TZ).date() def today(self): return self._today_model().count def yesterday(self): d = self._today_date() + datetime.timedelta(days=1) return self._theday_model(d).count def ithis_week(self): for i in xrange(-6, 1): d = self._today_date() + datetime.timedelta(days=i) yield self._theday_model(d).count def this_week(self): return list(self.ithis_week()) class Hidden(webapp.RequestHandler): "カウントアップのみ、表示しない" def get(self): Counter().append(self.request.get("_ip")) path = os.path.join(os.path.dirname(__file__), 'counter-hidden.html') self.response.out.write(template.render(path, {})) class Page(webapp.RequestHandler): def get(self): Counter().append(self.request.get("_ip")) counts = Counter().this_week() m = max(counts) graph = [{"value":c, "height":(100 * c) // m} for c in counts] template_values = dict( total=Counter().total(), today=Counter().today(), yesterday=Counter().yesterday(), graph=graph, ) path = os.path.join(os.path.dirname(__file__), 'counter.html') self.response.out.write(template.render(path, template_values)) class SetTotal(webapp.RequestHandler): def get(self): Counter().set_total(int(self.request.get('value'))) self.redirect('/') application = webapp.WSGIApplication([ ("/", Page), ("/hidden", Hidden), ("/settotal", SetTotal) ], debug=True) def main(): wsgiref.handlers.CGIHandler().run(application) if __name__ == '__main__': main()
#app.yaml application: counter-is-none version: 1 runtime: python api_version: 1 handlers: - url: /css static_dir: css - url: .* script: counter.py
上記に加え、テンプレートにcounter-hidden.htmlとcounter.htmlが必要です。
キャッシュとかの使い方をまだ読んでいません。
ので、上のコードは遅いかも知れないです。