Created
November 12, 2009 20:18
-
-
Save ericflo/233239 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from uuid import uuid1 | |
from simplejson import dumps, loads | |
from urlparse import urlparse | |
from httplib import HTTPConnection | |
class Client(object): | |
def __init__(self, server): | |
self.server = server | |
url_parts = urlparse(server) | |
self.port = url_parts.port | |
self.host, _, _ = url_parts.netloc.partition(':') | |
self.headers = {'Content-Type': 'application/json'} | |
def __getattr__(self, obj): | |
return self._request(obj) | |
def _request(self, method): | |
def _inner(*args, **kwargs): | |
data = dumps({ | |
'id': str(uuid1()), | |
'method': method, | |
'args': args, | |
'kwargs': kwargs, | |
}) | |
conn = HTTPConnection(self.host, self.port) | |
conn.request('POST', '/', data, self.headers) | |
response = conn.getresponse().read() | |
decoded_response = loads(response) | |
conn.close() | |
error = decoded_response.get('error') | |
if error is not None: | |
raise ValueError(error) | |
return decoded_response.get('result') | |
return _inner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import traceback | |
from simplejson import dumps, loads | |
from werkzeug import Request, Response | |
from werkzeug.exceptions import HTTPException, NotFound, BadRequest | |
def create_app(methods=None): | |
methods = methods or {} | |
@Request.application | |
def application(request): | |
try: | |
# Parse the JSON in the request | |
try: | |
data = loads(request.stream.read()) | |
except ValueError: | |
raise BadRequest() | |
# Grab the function to execute | |
try: | |
method = methods.get(data['method']) | |
except (KeyError, IndexError): | |
raise BadRequest() | |
if method is None: | |
raise NotFound() | |
# Get the args and kwargs | |
args = data.get('args', []) | |
kwargs = data.get('kwargs', {}) | |
kwargs = dict(((k.encode('utf-8'), v) for k, v in kwargs.iteritems())) | |
# Attempt to call the method with the params, or catch the | |
# exception and pass that back to the client | |
try: | |
response = Response(dumps({ | |
'id': data.get('id'), | |
'result': method(*args, **kwargs), | |
})) | |
except (KeyboardInterrupt, SystemExit): | |
raise | |
except Exception, e: | |
print e | |
response = Response(dumps({ | |
'id': data.get('id'), | |
'error': ''.join(traceback.format_exception(*sys.exc_info())), | |
})) | |
# Finish up and return the response | |
response.headers['Content-Type'] = 'application/json' | |
response.headers['Content-Length'] = len(response.data) | |
response.status_code = 200 | |
return response | |
except HTTPException, e: | |
# If an http exception is caught we can return it as response | |
# because those exceptions render standard error messages when | |
# called as wsgi application. | |
return e | |
return application | |
def run_server(app, port): | |
from cherrypy import wsgiserver | |
server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', port), app, | |
numthreads=10) | |
try: | |
server.start() | |
except KeyboardInterrupt: | |
server.stop() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime | |
import struct | |
import tc | |
import time | |
from struct import pack | |
from simplejson import dumps, loads | |
DBNAME = '/var/tokyocabinet/plays.bdb' | |
TS_LENGTH = 16 | |
MAX_TS = 9999999999999999 | |
NINES = '9' * TS_LENGTH | |
ZEROES = '0' * TS_LENGTH | |
from rpcutils import create_app, run_server | |
DB = None | |
def _get_db(): | |
global DB | |
if DB is None: | |
DB = tc.BDB(DBNAME, tc.TDBOWRITER | tc.TDBOCREAT) | |
return DB | |
def add_play(game_id, user_id, date, fb, session_key=None): | |
db = _get_db() | |
ts = str(MAX_TS - int(time.time() * 1e6)).zfill(TS_LENGTH) | |
keys = ( | |
'all-%s' % (ts,), | |
'user-%s-%s' % (str(user_id).zfill(TS_LENGTH), ts), | |
'game-%s-%s' % (str(game_id).zfill(TS_LENGTH), ts), | |
) | |
doc = dumps({ | |
'game_id': game_id, | |
'session_key': session_key, | |
'user_id': user_id, | |
'fb': fb, | |
'date': date, | |
}) | |
for key in keys: | |
db.put(key, doc) | |
count_key = 'playcount-%s' % (game_id,) | |
try: | |
db.putkeep(count_key, struct.pack('i', 1)) | |
except tc.Error: | |
db.addint(count_key, 1) | |
day_count_key = 'playcount-%s-%s' % (game_id, date.split()[0]) | |
try: | |
db.putkeep(day_count_key, struct.pack('i', 1)) | |
except tc.Error: | |
db.addint(day_count_key, 1) | |
return True | |
def get_plays_for_game(game_id, fb_only=False, limit=20): | |
db = _get_db() | |
game_id = str(game_id).zfill(TS_LENGTH) | |
b = 'game-%s-%s' % (game_id, ZEROES) | |
e = 'game-%s-%s' % (game_id, NINES) | |
resp = map(loads, [db[k] for k in db.range(b, False, e, True, limit)]) | |
if fb_only: | |
resp = filter(lambda x: x['fb'], resp) | |
return resp | |
def get_plays_for_user(user_id, fb_only=False, limit=20): | |
db = _get_db() | |
user_id = str(user_id).zfill(TS_LENGTH) | |
b = 'user-%s-%s' % (user_id, ZEROES) | |
e = 'user-%s-%s' % (user_id, NINES) | |
resp = map(loads, [db[k] for k in db.range(b, False, e, True, limit)]) | |
if fb_only: | |
resp = filter(lambda x: x['fb'], resp) | |
return resp | |
def get_plays(limit=20, fb_only=False): | |
db = _get_db() | |
b = 'all-%s' % (ZEROES,) | |
e = 'all-%s' % (NINES,) | |
resp = map(loads, [db[k] for k in db.range(b, False, e, True, limit)]) | |
if fb_only: | |
resp = filter(lambda x: x['fb'], resp) | |
return resp | |
def get_counts_for_game(game_id): | |
db = _get_db() | |
count_key = 'playcount-%s' % (game_id,) | |
try: | |
count = struct.unpack('i', db[count_key])[0] | |
except KeyError: | |
count = 0 | |
today = datetime.date.today() | |
last_week = today - datetime.timedelta(days=7) | |
last_month = today - datetime.timedelta(days=30) | |
week_count = 0 | |
month_count = 0 | |
day = last_month | |
while day <= today: | |
day_count_key = 'playcount-%s-%s' % (game_id, day.strftime('%Y-%m-%d')) | |
try: | |
day_count = struct.unpack('i', db[day_count_key])[0] | |
except KeyError: | |
day_count = 0 | |
month_count += day_count | |
if day >= last_week: | |
week_count += day_count | |
day += datetime.timedelta(days=1) | |
return { | |
'week': week_count, | |
'month': month_count, | |
'alltime': count, | |
} | |
application = create_app({ | |
'add_play': add_play, | |
'get_plays_for_game': get_plays_for_game, | |
'get_plays_for_user': get_plays_for_user, | |
'get_plays': get_plays, | |
'get_counts_for_game': get_counts_for_game, | |
}) | |
if __name__ == '__main__': | |
run_server(application, 4000) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment