def get(self): global global_dict #global_dict = {} starttime = time.time() query = self.request.query_string query = query.replace("%20", " ") parts = query.split("&") requests = {} if parts[0] != "": for pair in parts: ab = pair.split('=') requests[ab[0]] = ab[1] timing = bool(requests.get("timing", False)) regen = bool(requests.get("regen", False)) extraArgs = "" #if timing: # extraArgs += "&timing=1" outstr = None if not regen: outstr = global_dict.get("stats", None) if outstr is None: outstr = memcache.get("stats") if outstr is None: tq = taskqueue.Queue() tqs_r = tq.fetch_statistics_async() #gameHref = "<a href=Rankings?game=" + game + extraArgs + ">" + game + "</a>" out = [] out.append(structures.html_header % ("Statistics", "LiteRumble Statistics")) out.append("\nStats generated: " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " UTC") out.append("\nAllowed Robocode versions: " + str(structures.allowed_clients) + "\n<br>\n") q = structures.Rumble.all() rumbles = [[], [], []] categories = ["1v1", "Melee", "Teams"] for r in q.run(): memr = global_dict.get(r.Name, None) if memr is None: memr = memcache.get(r.Name) if memr is not None: r = memr if r.Melee: rumbles[1].append(r) elif r.Teams: rumbles[2].append(r) else: rumbles[0].append(r) for cat, rumbs in zip(categories, rumbles): for r in rumbs: try: scores = pickle.loads( zlib.decompress(r.ParticipantsScores)) except: logging.error("cannot load ParticipantsScores from " + r.Name) try: scores = marshal.loads( zlib.decompress(r.ParticipantsScores)) except: scores = {} entries = len(scores) if r.LastUpload is None: latest = None for s in scores.values(): t = s.LastUpload if latest is None or t > latest: latest = t r.LastUpload = latest r.__dict__["entries"] = entries #r.__dict__["scores"] = scores rumbs.sort(key=lambda r: -r.__dict__["entries"]) out.append("<table class=\"rumble\">\n<tr>") out.append( "<th>" + cat + "</th>\n<th>Participants</th>\n<th>Total Uploads</th>\n<th>Last Upload</th></tr>" ) for i, r in enumerate(rumbs): game = r.Name gameHref = "<a href=\"Rankings?game=" + game + extraArgs + "\" ><b>" + game + "</b></a>" lastTimeSince = timeSince(r.LastUpload) + " ago" out.append("\n<tr>\n<td>" + gameHref + "</td>\n<th>" + str(r.__dict__["entries"]) + "</th>\n<th>") out.append( str(r.TotalUploads) + "</th><th>" + lastTimeSince + "</th>\n</tr>") try: uploaders = pickle.loads(zlib.decompress(r.Uploaders)) if len(uploaders) == 0: uploaders = {} except TypeError: uploaders = {} #out.append("\n<tr><td></td><td><i><u>Uploader Name</u></i></td><td></td><td></td></tr>") uv = uploaders.values() cutoff = datetime.datetime.now() - datetime.timedelta(31) uv = filter( lambda u: datetime.datetime.strptime( u.latest, "%Y-%m-%d %H:%M:%S") > cutoff, uv) uv.sort(key=lambda u: u.latest, reverse=True) for j, u in enumerate(uv): # if j == 0: # out.append("\n<tr><td><i>Uploader Name</i></td><td>") # else: out.append("\n<tr><td></td><td>") name = u.name if name == "Put_Your_Name_Here": name = "Anonymous" out.append(name) out.append("</td><td>") out.append(str(u.total)) out.append("</td><td>") out.append(timeSince(u.latest) + " ago") out.append("</td></tr>") for r in rumbs: r.__dict__.pop("entries", 1) #r.__dict__.pop("scores",1) out.append("\n</table>") tqs = tqs_r.get_result() tasks = tqs.tasks last_min = tqs.executed_last_minute if last_min is None or last_min == 0: last_min = 1 if tasks is None: tasks is 0 backlog = float(tasks) * 60.0 / last_min #out.append() tq_string = "<table>\n<tr><th colspan=\"2\">Upload Queue</th></tr>\n<tr><td>Projected Processing Time</td><td>" + formatSecs( backlog) + "</td></tr>" tq_string += "\n<tr><td>Current Size</td><td>" + str( tasks) + " pairing" if tasks != 1: tq_string += "s" tq_string += "</td></tr>\n</table>\n<br>" out.insert(2, tq_string) outstr = string.join(out, "") memcache.set("stats", outstr) global_dict["stats"] = outstr elapsed = time.time() - starttime if timing: outstr += "<br>\n Page served in " + str(int(round( elapsed * 1000))) + "ms." outstr += "</body></html>" self.response.out.write(outstr)
def post(self): global global_dict global global_sync global locks global last_write #global sync_lock starttime = time.time() post_body = self.request.body sections = post_body.split('&') results = {} for pair in sections: ab = pair.split('=') if ab is not None and len(ab) == 2: results[ab[0]] = ab[1] client = results.get("client", "ERROR") version = results.get("version", "ERROR") rumble = results.get("game", None) bota = results.get("fname", None) botb = results.get("sname", None) uploads_allowed = global_dict.get("uploads allowed", None) uploads_allowed_expired = global_dict.get("uploads allowed check time", None) if uploads_allowed is None or uploads_allowed_expired is None or datetime.datetime.now( ) > uploads_allowed_expired: tq = taskqueue.Queue() tqs_r = tq.fetch_statistics_async() tqs = tqs_r.get_result() last_min = tqs.executed_last_minute if last_min is None or last_min <= 120: last_min = 120 tasks = tqs.tasks if tasks is None: tasks is 0 backlog = float(tasks) / last_min uploads_allowed = backlog < 5 global_dict["uploads allowed"] = uploads_allowed global_dict["uploads allowed check time"] = datetime.datetime.now( ) + datetime.timedelta(1. / (24 * 60)) if not uploads_allowed: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Queue full," + bota_name + " vs " + botb_name + " discarded.") logging.info("Queue full, discarding " + bota_name + " vs " + botb_name) return if (version in structures.allowed_versions and client in structures.allowed_clients and rumble is not None and bota is not None and botb is not None): #prio_q = taskqueue.Queue("priority-battles") #prio_task_list_rpc = prio_q.lease_tasks_by_tag_async(30,1,rumble) try: taskqueue.add(url='/HandleQueuedResults', payload=json.dumps(results)) bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] logging.info("adding " + bota_name + " vs " + botb_name) except apiproxy_errors.OverQuotaError: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Queue full," + bota_name + " vs " + botb_name + " discarded.") logging.info("discarding " + bota_name + " vs " + botb_name + " via error") #time.sleep(0.5) return except taskqueue.Error: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Task queue error," + bota_name + " vs " + botb_name + " discarded.") # prio_task_list = prio_task_list_rpc.get_result() # if prio_task_list: # prio_string = prio_task_list[0].payload # self.response.out.write(prio_string) # logging.info("sending back priority battle: " + prio_string + ", " + rumble) # prio_q.delete_tasks(prio_task_list) rq_name = rumble + "|queue" try: rumble_queue = global_dict[rq_name] try: prio_string = rumble_queue.get_nowait() logging.info("sending back priority battle: " + prio_string + ", " + rumble) self.response.out.write(prio_string) #logging.info("Sent back priority battles: " + prio_string) except Queue.Empty: #logging.info("No available priority battles") prio_string = None except KeyError: logging.info("No queue for rumble " + rumble + ", adding one!") global_dict[rq_name] = Queue.Queue(maxsize=300) bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. " + bota_name + " vs " + botb_name + " added to queue") elapsed = time.time() - starttime self.response.out.write(" in " + str(int(round(elapsed * 1000))) + "ms") if results["user"] == "Put_Your_Name_Here": self.response.out.write( "\nPlease set your username in /robocode/roborumble/{rumblename}.txt!" ) else: logging.info("version: " + client) self.response.out.write("OK. CLIENT NOT SUPPORTED. Use one of: " + str(structures.allowed_clients) + ", not " + client)
def post(self): global global_dict global global_sync global locks global last_write #global sync_lock starttime = time.time() post_body = self.request.body sections = post_body.split('&') results = {} for pair in sections: ab = pair.split('=') if ab is not None and len(ab) == 2: results[ab[0]] = ab[1] client = results.get("client","ERROR") version = results.get("version","ERROR") rumble = results.get("game",None) bota = results.get("fname",None) botb = results.get("sname",None) uploads_allowed = global_dict.get("uploads allowed",None) uploads_allowed_expired = global_dict.get("uploads allowed check time",None) if uploads_allowed is None or uploads_allowed_expired is None or datetime.datetime.now() > uploads_allowed_expired : tq = taskqueue.Queue() tqs_r = tq.fetch_statistics_async() tqs = tqs_r.get_result() last_min = tqs.executed_last_minute if last_min is None or last_min <= 120: last_min = 120 tasks = tqs.tasks if tasks is None: tasks is 0 backlog = float(tasks)/last_min uploads_allowed = backlog < 5 global_dict["uploads allowed"]=uploads_allowed global_dict["uploads allowed check time"] = datetime.datetime.now() + datetime.timedelta(1./(24*60)) if not uploads_allowed: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Queue full," + bota_name + " vs " + botb_name + " discarded.") logging.info("Queue full, discarding " + bota_name + " vs " + botb_name) return if (version in structures.allowed_versions and client in structures.allowed_clients and rumble is not None and bota is not None and botb is not None): #prio_q = taskqueue.Queue("priority-battles") #prio_task_list_rpc = prio_q.lease_tasks_by_tag_async(30,1,rumble) try: taskqueue.add(url='/HandleQueuedResults', payload=json.dumps(results)) bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] logging.info("adding " + bota_name + " vs " + botb_name ) except apiproxy_errors.OverQuotaError: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Queue full," + bota_name + " vs " + botb_name + " discarded.") logging.info("discarding " + bota_name + " vs " + botb_name + " via error") #time.sleep(0.5) return except taskqueue.Error: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Task queue error," + bota_name + " vs " + botb_name + " discarded.") # prio_task_list = prio_task_list_rpc.get_result() # if prio_task_list: # prio_string = prio_task_list[0].payload # self.response.out.write(prio_string) # logging.info("sending back priority battle: " + prio_string + ", " + rumble) # prio_q.delete_tasks(prio_task_list) rq_name = rumble + "|queue" try: rumble_queue = global_dict[rq_name] try: prio_string = rumble_queue.get_nowait() logging.info("sending back priority battle: " + prio_string + ", " + rumble) self.response.out.write(prio_string) #logging.info("Sent back priority battles: " + prio_string) except Queue.Empty: #logging.info("No available priority battles") prio_string = None except KeyError: logging.info("No queue for rumble " + rumble + ", adding one!") global_dict[rq_name] = Queue.Queue(maxsize=300) bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. " + bota_name + " vs " + botb_name + " added to queue") elapsed = time.time() - starttime self.response.out.write(" in " + str(int(round(elapsed*1000))) + "ms") if results["user"] == "Put_Your_Name_Here": self.response.out.write("\nPlease set your username in /robocode/roborumble/{rumblename}.txt!") else: logging.info("version: " + client) self.response.out.write("OK. CLIENT NOT SUPPORTED. Use one of: " + str(structures.allowed_clients) + ", not " + client)
def get(self): global global_dict #global_dict = {} #starttime = time.time() parts = self.request.query_string.split("&") requests = {} if parts[0] != "": for pair in parts: ab = pair.split('=') requests[ab[0]] = ab[1] game = requests.get("game", None) if game is None: self.response.out.write("NO RUMBLE SPECIFIED IN FORM game=____") return version = requests.get("version", None) if version is None or version != "1": self.response.out.write("VERSION NOT SPECIFIED AS version=1") return rumble = global_dict.get(game, None) if rumble is None: rumble = memcache.get(game) if rumble is None: rumble = structures.Rumble.get_by_key_name(game) if rumble is None: self.response.out.write("RUMBLE NOT FOUND") return else: global_dict[game] = rumble memcache.set(game, rumble) else: global_dict[game] = rumble # try: # scoresdicts = marshal.loads(zlib.decompress(rumble.ParticipantsScores)) # scoreslist = [structures.LiteBot() for _ in scoresdicts] # for s,d in zip(scoreslist,scoresdicts): # s.__dict__.update(d) # r = scoreslist # except: botsdict = pickle.loads(zlib.decompress(rumble.ParticipantsScores)) r = botsdict.values() out = [] for bot in r: name = bot.Name name = name.replace(" ", "_") out.append(name) out.append("=") out.append(str(bot.APS)) out.append(",") out.append(str(bot.Battles)) out.append(",") out.append(bot.LastUpload) out.append("\n") #line = name + "=" + str(bot.APS) + "," + str(bot.Battles) + "," + bot.LastUpload + "\n" #out.append(line) self.response.out.write(''.join(out))
def post(self): global global_dict global global_sync global locks global last_write starttime = time.time() post_body = self.request.body sections = post_body.split('&') results = {} for pair in sections: ab = pair.split('=') if ab is not None and len(ab) == 2: results[ab[0]] = ab[1] client = results.get("client","ERROR") version = results.get("version","ERROR") rumble = results.get("game",None) bota = results.get("fname",None) botb = results.get("sname",None) if bota and botb: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] now = datetime.datetime.now() battleTimeStr = results.get("time",None) if battleTimeStr is not None: battleTime = datetime.datetime.utcfromtimestamp(int(battleTimeStr)/1e3) logging.info("Uploaded battle run at " + str(battleTime)) if battleTime < now - datetime.timedelta(1): self.response.out.write("OK. ERROR: your uploaded data is more than 24 hours old!") logging.info("Old data uploaded, discarding " + bota_name + " vs " + botb_name + " fought at " + str(battleTime)) return uploads_allowed = global_dict.get("uploads allowed",None) uploads_allowed_expired = global_dict.get("uploads allowed check time",None) if uploads_allowed is None or uploads_allowed_expired is None or now > uploads_allowed_expired : tq = taskqueue.Queue() tqs_r = tq.fetch_statistics_async() tqs = tqs_r.get_result() last_min = tqs.executed_last_minute if last_min is None or last_min <= 250: last_min = 250 tasks = tqs.tasks if tasks is None: tasks is 0 backlog = float(tasks)/last_min uploads_allowed = backlog < 5 global_dict["uploads allowed"]=uploads_allowed global_dict["uploads allowed check time"] = now + datetime.timedelta(1./(24*60)) if not uploads_allowed: self.response.out.write("OK. Queue full," + bota_name + " vs " + botb_name + " discarded.") logging.info("Queue full, discarding " + bota_name + " vs " + botb_name) return if (version in structures.allowed_versions and client in structures.allowed_clients and rumble is not None and bota is not None and botb is not None): try: taskqueue.add(url='/HandleQueuedResults', payload=json.dumps(results), headers={'X-AppEngine-FailFast': 'True'}) bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] logging.info("adding " + bota_name + " vs " + botb_name ) except apiproxy_errors.OverQuotaError: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Queue full," + bota_name + " vs " + botb_name + " discarded.") logging.info("discarding " + bota_name + " vs " + botb_name + " via error") #time.sleep(0.5) return except taskqueue.Error: bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. Task queue error," + bota_name + " vs " + botb_name + " discarded.") rq_name = rumble + "|queue" try: rumble_queue = global_dict[rq_name] try: prio_string = rumble_queue.pop() logging.info("sending back priority battle: " + prio_string + ", " + rumble) self.response.out.write(prio_string) #logging.info("Sent back priority battles: " + prio_string) except IndexError: #logging.info("No available priority battles") prio_string = None except KeyError: logging.info("No queue for rumble " + rumble + ", adding one!") global_dict[rq_name] = deque() bota_name = bota.split(" ")[0].split(".")[-1] botb_name = botb.split(" ")[0].split(".")[-1] self.response.out.write("OK. " + bota_name + " vs " + botb_name + " added to queue") elapsed = time.time() - starttime self.response.out.write(" in " + str(int(round(elapsed*1000))) + "ms") if results["user"] == "Put_Your_Name_Here": self.response.out.write("\nPlease set your username in /robocode/roborumble/{rumblename}.txt!") else: logging.info("version: " + client) self.response.out.write("OK. CLIENT NOT SUPPORTED. Use one of: " + str(structures.allowed_clients) + ", not " + client)
def get(self): # global_dict = {} global global_dict starttime = time.time() query = self.request.query_string query = query.replace("%20", " ") parts = query.split("&") requests = {} if parts[0] != "": for pair in parts: ab = pair.split('=') requests[ab[0]] = ab[1] game = requests.get("game", ) if game is None: self.response.out.write( "ERROR: RUMBLE NOT SPECIFIED IN FORMAT game=____") return name = requests.get("name", None) if name is None: self.response.out.write( "ERROR: BOT NOT SPECIFIED IN FORMAT name=____") return lim = int(requests.get("limit", "10000000")) order = requests.get("order", None) timing = bool(requests.get("timing", False)) api = bool(requests.get("api", False)) extraArgs = "" if timing: extraArgs += "&timing=1" reverseSort = True if order is None or order.replace(" ", "") == "": order = "Name" reverseSort = False elif order[0] == "-": order = order[1:] reverseSort = False if order == "Latest Battle": order = "LastUpload" parsetime = time.time() - starttime cached = True keyhash = name + "|" + game #bot = global_dict.get(keyhash,None) #if bot is None: bot = memcache.get(keyhash) # global_dict[keyhash] = bot if bot is None or bot.PairingsList is None: bot = structures.BotEntry.get_by_key_name(keyhash) if bot is not None: memcache.set(keyhash, bot) # global_dict[keyhash] = bot cached = False rumble = None if not api: rumble = global_dict.get(game, None) if rumble is None: rumble = memcache.get(game) if rumble is None: rumble = structures.Rumble.get_by_key_name(game) if rumble is None: self.response.out.write("RUMBLE NOT FOUND") return else: global_dict[game] = rumble memcache.set(game, rumble) else: global_dict[game] = rumble if bot is None: self.response.out.write( "ERROR. name/game combination does not exist: " + name + "/" + game) else: flagmap = global_dict.get(structures.default_flag_map) if flagmap is None: flagmap = memcache.get(structures.default_flag_map) if flagmap is None: flagmapholder = structures.FlagMap.get_by_key_name( structures.default_flag_map) if flagmapholder is None: flagmap = zlib.compress(pickle.dumps({})) else: flagmap = flagmapholder.InternalMap memcache.set(structures.default_flag_map, flagmap) global_dict[structures.default_flag_map] = flagmap else: global_dict[structures.default_flag_map] = flagmap try: flagmap = pickle.loads(zlib.decompress(flagmap)) except: flagmap = marshal.loads(zlib.decompress(flagmap)) bots = None if lim > 0 or not api: try: bots = pickle.loads(zlib.decompress(bot.PairingsList)) except: botsDicts = marshal.loads(zlib.decompress( bot.PairingsList)) bots = [structures.ScoreSet() for _ in botsDicts] for s, d in zip(bots, botsDicts): s.__dict__.update(d) removes = [] for b in bots: lastUpload = None try: lastUpload = b.LastUpload except: b.LastUpload = datetime.datetime.now().strftime( "%Y-%m-%d %H:%M:%S") package = string.split(b.Name, ".")[0] if package in flagmap: b.Flag = flagmap[package] else: b.Flag = "NONE" try: b.APS = float(b.APS) b.KNNPBI = float(b.KNNPBI) b.NPP = float(b.NPP) b.Battles = int(b.Battles) except: removes.append(b) for b in removes: bots.pop(bots.index(b)) package = string.split(bot.Name, ".")[0] if package in flagmap: bot.Flag = flagmap[package] else: bot.Flag = "NONE" retrievetime = time.time() - parsetime - starttime if lim > 0: bots = filter(lambda b: getattr(b, 'Alive', True), bots) if order not in bots[0].__dict__: order = "Name" bots = sorted(bots, key=attrgetter(order), reverse=reverseSort) if api: outs = ["{"] outs.append("\n\"name\":\"") outs.append(name) outs.append("\",\n\"flag\":\"") outs.append(bot.Flag) outs.append("\",\n\"APS\":") outs.append(str(bot.APS)) outs.append(",\n\"PWIN\":") outs.append(str(50.0 * float(bot.PL) / bot.Pairings + 50.0)) outs.append(",\n\"ANPP\":") outs.append(str(bot.ANPP)) outs.append(",\n\"vote\":") outs.append(str(bot.VoteScore)) outs.append(",\n\"survival\":") outs.append(str(bot.Survival)) outs.append(",\n\"pairings\":") outs.append(str(bot.Pairings)) outs.append(",\n\"battles\":") outs.append(str(bot.Battles)) outs.append(",\n\"latest\":\"") outs.append(str(bot.LastUpload)) outs.append("\"") if lim > 0: outs.append(",\n\"pairingsList\":[\n") headings = [ "\"name\"", "\"flag\"", "\"rank\"", "\"APS\"", "\"NPP\"", "\"survival\"", "\"KNNPBI\"", "\"battles\"", "\"latest\"" ] escapes = ["\"", "\"", "", "", "", "", "", "", "\""] count = 0 for b in bots: count += 1 if count > lim: break cells = [ b.Name, b.Flag, count, round(100.0 * b.APS) * 0.01, round(100.0 * b.NPP) * 0.01, round(100.0 * b.Survival) * 0.01, round(100.0 * b.KNNPBI) * 0.01, b.Battles, b.LastUpload ] outs.append(" {") for i in range(len(cells)): outs.append(headings[i]) outs.append(":") outs.append(escapes[i]) outs.append(str(cells[i])) outs.append(escapes[i]) outs.append(",") outs[-1] = "},\n" outs[-1] = "}\n]" outs.append("\n}") self.response.out.write(''.join(outs)) else: sorttime = time.time() - retrievetime - parsetime - starttime if order == "LastUpload": order = "Latest Battle" out = [] gameHref = "<a href=\"Rankings?game=" + game + extraArgs + "\">" + game + "</a>" gameTitle = "Bot details of <b>" + name + "</b> in " + gameHref + " vs. " + str( len(bots)) + " bots." flagtag = "<img id='flag' src=\"/flags/" + bot.Flag + ".gif\"> " + structures.country_lookup[ bot.Flag] endName = name.split(" ")[0].split(".")[-1] out.append(structures.html_header % (endName + " in " + game, gameTitle)) out.append("<table>\n") wikiurl = "<a href=\"http://www.robowiki.net/wiki/{identifier}\">{name}</a>"\ .format(identifier=name.split(" ")[0].split(".")[-1], name=name) out.append("<tr>\n<th>Name</th>\n<td>\n" + wikiurl + "</td>\n<th>Score Distribution</th></tr>") out.append("<tr>\n<th>Flag</th>\n<td>\n" + flagtag + "</td><td rowspan=\"9\">") enemyScores = pickle.loads( zlib.decompress(rumble.ParticipantsScores)) # RGB color model # Default colors # colorSurvival = (0,255,0) # colorAPS = (255,0,0) # colorAPSvsKNPBI = (0,0,255) # Colors suggested at # http://ksrowell.com/blog-visualizing-data/2012/02/02/optimal-colors-for-graphs/ colorSurvival = (62, 150, 81) colorAPS = (204, 37, 41) colorKNPBI = (57, 106, 177) size = 230 a = numpy.zeros((size + 1, size + 1, 3), dtype=numpy.uint32) counts = numpy.zeros((size + 1, size + 1), dtype=numpy.uint32) for b in bots: eScore = enemyScores.get(b.Name, None) if eScore: x = int(round(eScore.APS * 0.01 * size)) yAPS = max( 0, min(size, size - int(round(b.APS * 0.01 * size)))) a[yAPS, x, :] += colorAPS counts[yAPS, x] += 1 ySurvival = max( 0, min(size, size - int(round(b.Survival * 0.01 * size)))) a[ySurvival, x, :] += colorSurvival counts[ySurvival, x] += 1 yKNNPBI = max( 0, min( size, size - int(round( (b.KNNPBI + 50) * 0.01 * size)))) a[yKNNPBI, x, :] += colorKNPBI counts[yKNNPBI, x] += 1 a[counts == 0, :] = 255 setVals = counts > 1 for i in range(3): a[setVals, i] = a[setVals, i] / counts[setVals] midHeight = size - int(round(.5 * size)) a[midHeight, counts[midHeight, :] == 0, :] = 127 b = Image.fromarray(a.astype("uint8"), "RGB") c = cStringIO.StringIO() b.save(c, format="png") d = c.getvalue() c.close() e = base64.b64encode(d).decode("ascii") out.append( '<img title=\"Red = APS, Green = Survival, Blue = APS vs (KNNPBI+50)\" style=\"border: black 1px solid;\" alt="score distibution" src="data:image/png;base64,' ) out.append(e) out.append("\">") out.append("</td></tr>") out.append("<tr>\n<th>APS</th>\n<td>\n" + str(bot.APS) + "</td></tr>") out.append("<tr>\n<th>PWIN</th>\n<td>\n" + str(50.0 * float(bot.PL) / bot.Pairings + 50.0) + "</td></tr>") out.append("<tr>\n<th>ANPP</th>\n<td>\n" + str(bot.ANPP) + "</td></tr>") out.append("<tr>\n<th>Vote</th>\n<td>\n" + str(bot.VoteScore) + "</td></tr>") out.append("<tr>\n<th>Survival</th>\n<td>\n" + str(bot.Survival) + "</td></tr>") out.append("<tr>\n<th>Pairings</th>\n<td>\n" + str(bot.Pairings) + "</td></tr>") out.append("<tr>\n<th>Battles</th>\n<td>\n" + str(bot.Battles) + "</td></tr>") out.append("<tr>\n<th>Latest Battle</th>\n<td>\n" + str(bot.LastUpload) + " UTC</td></tr>") out.append("<tr>\n<td colspan=\"2\">") out.append( "<form name=\"input\" action=\"BotCompare\" method=\"get\">" ) out.append("<input type=\"hidden\" name=\"game\" value=\"" + game + "\" />") out.append("<input type=\"hidden\" name=\"bota\" value=\"" + name + "\" />") out.append("<input type=\"text\" name=\"botb\" value=\"" + name + "\" />") out.append( "<input type=\"submit\" value=\"Compare\" /></form>") out.append( "</td><td>Opponent APS (X) vs. Pairing (Y) </td></tr></table>" ) if lim > 0: out.append("\n<table>\n<tr>\n") headings = [ " ", "Flag", "Name", "", "APS", "NPP", "Survival", "KNNPBI", "Battles", "Latest Battle" ] for heading in headings: sortedBy = (order == heading) headinglink = heading if sortedBy and reverseSort: heading = "-" + heading headinglink = heading elif not sortedBy: headinglink = "-" + headinglink orderHref = "<a href=\"BotDetails?game=" + game + "&name=" + name.replace( " ", "%20") + "&order=" + headinglink.replace( " ", "%20") + extraArgs + "\">" + heading + "</a>" if sortedBy: out.append("\n<th class=\"sortedby\">" + orderHref + "</th>") else: out.append("\n<th>" + orderHref + "</th>") out.append("\n</tr>") rank = 0 highlightKey = [ False, False, False, False, True, True, True, True, False, False ] mins = [0, 0, 0, 0, 40, 40, 40, -0.1, 0, 0] maxs = [0, 0, 0, 0, 60, 70, 60, 0.1, 0, 0] for bot in bots: rank += 1 if rank > lim: break botName = bot.Name botNameHref = "<a href=\"BotDetails?game=" + game + "&name=" + botName.replace( " ", "%20") + extraArgs + "\">" + botName + " </a>" compareHref = "<a href=\"BotCompare?game=" + game + "&bota=" + name.replace( " ", "%20") + "&botb=" + botName.replace( " ", "%20") + extraArgs + "\">compare</a>" ft = [] ft.append("<img id='flag' src=\"/flags/") ft.append(bot.Flag) ft.append(".gif\">") flagtag = ''.join(ft) cells = [ str(rank), flagtag, botNameHref, compareHref, round(100.0 * bot.APS) * 0.01, round(100.0 * bot.NPP) * 0.01, round(100.0 * bot.Survival) * 0.01, round(100.0 * bot.KNNPBI) * 0.01, bot.Battles, bot.LastUpload ] out.append("\n<tr>") for i, cell in enumerate(cells): if highlightKey[i]: if cell < mins[i]: out.append("\n<td class=\"red\">" + str(cell) + "</td>") elif cell > maxs[i]: out.append("\n<td class=\"green\">" + str(cell) + "</td>") else: out.append("\n<td>" + str(cell) + "</td>") else: out.append("\n<td>" + str(cell) + "</td>") out.append("\n</tr>") out.append("</table>") htmltime = time.time( ) - sorttime - retrievetime - parsetime - starttime elapsed = time.time() - starttime if timing: out.append("<br />\n Page served in " + str(int(round(elapsed * 1000))) + "ms. Bot cached: " + str(cached)) out.append("\n<br /> parsing: " + str(int(round(parsetime * 1000)))) out.append("\n<br /> retrieve: " + str(int(round(retrievetime * 1000)))) out.append("\n<br /> sort: " + str(int(round(sorttime * 1000)))) out.append("\n<br /> html generation: " + str(int(round(htmltime * 1000)))) out.append("</body></html>") outstr = string.join(out, "") self.response.out.write(outstr)
def get(self): # global_dict = {} global global_dict starttime = time.time() query = self.request.query_string query = query.replace("%20"," ") parts = query.split("&") requests = {} if parts[0] != "": for pair in parts: ab = pair.split('=') requests[ab[0]] = ab[1] game = requests.get("game",) if game is None: self.response.out.write("ERROR: RUMBLE NOT SPECIFIED IN FORMAT game=____") return name = requests.get("name",None) if name is None: self.response.out.write("ERROR: BOT NOT SPECIFIED IN FORMAT name=____") return lim = int(requests.get("limit","10000000")) order = requests.get("order",None) timing = bool(requests.get("timing",False)) api = bool(requests.get("api",False)) extraArgs = "" if timing: extraArgs += "&timing=1" reverseSort = True if order is None or order.replace(" ","") == "": order = "Name" reverseSort = False elif order[0] == "-": order = order[1:] reverseSort = False if order == "Latest Battle": order = "LastUpload" parsetime = time.time() - starttime cached = True keyhash = name + "|" + game #bot = global_dict.get(keyhash,None) #if bot is None: bot = memcache.get(keyhash) # global_dict[keyhash] = bot if bot is None or bot.PairingsList is None: bot = structures.BotEntry.get_by_key_name(keyhash) if bot is not None: memcache.set(keyhash,bot) # global_dict[keyhash] = bot cached = False rumble = None if not api: rumble = global_dict.get(game,None) if rumble is None: rumble = memcache.get(game) if rumble is None: rumble = structures.Rumble.get_by_key_name(game) if rumble is None: self.response.out.write("RUMBLE NOT FOUND") return else: global_dict[game]=rumble memcache.set(game,rumble) else: global_dict[game] = rumble if bot is None: self.response.out.write( "ERROR. name/game combination does not exist: " + name + "/" + game) else: flagmap = global_dict.get(structures.default_flag_map) if flagmap is None: flagmap = memcache.get(structures.default_flag_map) if flagmap is None: flagmapholder = structures.FlagMap.get_by_key_name(structures.default_flag_map) if flagmapholder is None: flagmap = zlib.compress(pickle.dumps({})) else: flagmap = flagmapholder.InternalMap memcache.set(structures.default_flag_map,flagmap) global_dict[structures.default_flag_map] = flagmap else: global_dict[structures.default_flag_map] = flagmap try: flagmap = pickle.loads(zlib.decompress(flagmap)) except: flagmap = marshal.loads(zlib.decompress(flagmap)) bots = None if lim > 0 or not api: try: bots = pickle.loads(zlib.decompress(bot.PairingsList)) except: botsDicts = marshal.loads(zlib.decompress(bot.PairingsList)) bots = [structures.ScoreSet() for _ in botsDicts] for s,d in zip(bots,botsDicts): s.__dict__.update(d) removes = [] for b in bots: lastUpload = None try: lastUpload = b.LastUpload except: b.LastUpload = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") package = string.split(b.Name,".")[0] if package in flagmap: b.Flag = flagmap[package] else: b.Flag = "NONE" try: b.APS = float(b.APS) b.KNNPBI = float(b.KNNPBI) b.NPP = float(b.NPP) b.Battles = int(b.Battles) except: removes.append(b) for b in removes: bots.pop(bots.index(b)) package = string.split(bot.Name,".")[0] if package in flagmap: bot.Flag = flagmap[package] else: bot.Flag = "NONE" retrievetime = time.time() - parsetime - starttime if lim > 0: bots = filter(lambda b: getattr(b,'Alive',True), bots) if order not in bots[0].__dict__: order = "Name" bots = sorted(bots, key=attrgetter(order), reverse=reverseSort) if api: outs = ["{"] outs.append("\n\"name\":\"") outs.append(name) outs.append("\",\n\"flag\":\"") outs.append(bot.Flag) outs.append("\",\n\"APS\":") outs.append(str(bot.APS)) outs.append(",\n\"PWIN\":") outs.append(str(50.0*float(bot.PL)/bot.Pairings + 50.0)) outs.append(",\n\"ANPP\":") outs.append(str(bot.ANPP)) outs.append(",\n\"vote\":") outs.append(str(bot.VoteScore)) outs.append(",\n\"survival\":") outs.append(str(bot.Survival)) outs.append(",\n\"pairings\":") outs.append(str(bot.Pairings)) outs.append(",\n\"battles\":") outs.append(str(bot.Battles)) outs.append(",\n\"latest\":\"") outs.append(str(bot.LastUpload)) outs.append("\"") if lim > 0: outs.append(",\n\"pairingsList\":[\n") headings = [ "\"name\"", "\"flag\"", "\"rank\"", "\"APS\"", "\"NPP\"", "\"survival\"", "\"KNNPBI\"", "\"battles\"", "\"latest\""] escapes = ["\"","\"","","","","","","","\""] count = 0 for b in bots: count += 1 if count > lim: break cells = [b.Name, b.Flag, count, round(100.0*b.APS)*0.01, round(100.0*b.NPP)*0.01, round(100.0*b.Survival)*0.01, round(100.0*b.KNNPBI)*0.01, b.Battles,b.LastUpload] outs.append(" {") for i in range(len(cells)): outs.append(headings[i]) outs.append(":") outs.append(escapes[i]) outs.append(str(cells[i])) outs.append(escapes[i]) outs.append(",") outs[-1] = "},\n" outs[-1] = "}\n]" outs.append("\n}") self.response.out.write(''.join(outs)) else: sorttime = time.time() - retrievetime - parsetime - starttime if order == "LastUpload": order = "Latest Battle" out = [] gameHref = "<a href=\"Rankings?game=" + game + extraArgs + "\">" + game + "</a>" gameTitle = "Bot details of <b>" + name + "</b> in "+ gameHref + " vs. " + str(len(bots)) + " bots." flagtag = "<img id='flag' src=\"/flags/" + bot.Flag + ".gif\"> " + structures.country_lookup[bot.Flag] endName = name.split(" ")[0].split(".")[-1] out.append(structures.html_header % (endName + " in " + game,gameTitle)) out.append("<table>\n") out.append("<tr>\n<th>Name</th>\n<td>\n" + name + "</td>\n<th>Score Distribution</th></tr>") out.append("<tr>\n<th>Flag</th>\n<td>\n" + flagtag + "</td><td rowspan=\"9\">") enemyScores = pickle.loads(zlib.decompress(rumble.ParticipantsScores)) # RGB color model # Default colors # colorSurvival = (0,255,0) # colorAPS = (255,0,0) # colorAPSvsKNPBI = (0,0,255) # Colors suggested at # http://ksrowell.com/blog-visualizing-data/2012/02/02/optimal-colors-for-graphs/ colorSurvival = (62,150,81) colorAPS = (204,37,41) colorKNPBI = (57,106,177) size = 230 a = numpy.zeros((size+1,size+1,3), dtype=numpy.uint32) counts = numpy.zeros((size+1, size+1), dtype=numpy.uint32) for b in bots: eScore = enemyScores.get(b.Name,None) if eScore: x = int(round(eScore.APS*0.01*size)) yAPS = max(0,min(size,size-int(round(b.APS*0.01*size)))) a[yAPS, x, :] += colorAPS counts[yAPS,x] += 1 ySurvival = max(0,min(size,size-int(round(b.Survival*0.01*size)))) a[ySurvival, x, :] += colorSurvival counts[ySurvival,x] += 1 yKNNPBI = max(0,min(size,size-int(round((b.KNNPBI+50)*0.01*size)))) a[yKNNPBI, x, :] += colorKNPBI counts[yKNNPBI,x] += 1 a[counts==0,:] = 255 setVals = counts>1 for i in range(3): a[setVals,i] = a[setVals,i]/counts[setVals] midHeight = size - int(round(.5*size)) a[midHeight,counts[midHeight,:] == 0,:] = 127 b = Image.fromarray(a.astype("uint8"),"RGB") c = cStringIO.StringIO() b.save(c,format="png") d = c.getvalue() c.close() e = base64.b64encode(d).decode("ascii") out.append('<img title=\"Red = APS, Green = Survival, Blue = APS vs (KNNPBI+50)\" style=\"border: black 1px solid;\" alt="score distibution" src="data:image/png;base64,') out.append(e) out.append("\">") out.append("</td></tr>") out.append("<tr>\n<th>APS</th>\n<td>\n" + str(bot.APS) + "</td></tr>") out.append("<tr>\n<th>PWIN</th>\n<td>\n" + str(50.0*float(bot.PL)/bot.Pairings + 50.0) + "</td></tr>") out.append("<tr>\n<th>ANPP</th>\n<td>\n" + str(bot.ANPP) + "</td></tr>") out.append("<tr>\n<th>Vote</th>\n<td>\n" + str(bot.VoteScore) + "</td></tr>") out.append("<tr>\n<th>Survival</th>\n<td>\n" + str(bot.Survival) + "</td></tr>") out.append("<tr>\n<th>Pairings</th>\n<td>\n" + str(bot.Pairings) + "</td></tr>") out.append("<tr>\n<th>Battles</th>\n<td>\n" + str(bot.Battles) + "</td></tr>") out.append("<tr>\n<th>Latest Battle</th>\n<td>\n" + str(bot.LastUpload) + " UTC</td></tr>") out.append("<tr>\n<td colspan=\"2\">") out.append("<form name=\"input\" action=\"BotCompare\" method=\"get\">") out.append("<input type=\"hidden\" name=\"game\" value=\"" + game+ "\" />") out.append("<input type=\"hidden\" name=\"bota\" value=\"" + name + "\" />") out.append("<input type=\"text\" name=\"botb\" value=\"" + name + "\" />") out.append("<input type=\"submit\" value=\"Compare\" /></form>") out.append("</td><td>Opponent APS (X) vs. Pairing (Y) </td></tr></table>") if lim > 0: out.append("\n<table>\n<tr>\n") headings = [" ", "Flag", "Name", "", "APS", "NPP", "Survival", "KNNPBI", "Battles", "Latest Battle"] for heading in headings: sortedBy = (order == heading) headinglink = heading if sortedBy and reverseSort: heading = "-" + heading headinglink = heading elif not sortedBy: headinglink = "-" + headinglink orderHref = "<a href=\"BotDetails?game="+game+"&name="+name.replace(" ","%20")+"&order="+ headinglink.replace(" ","%20") + extraArgs + "\">"+heading+"</a>" if sortedBy: out.append( "\n<th class=\"sortedby\">" + orderHref + "</th>") else: out.append( "\n<th>" + orderHref + "</th>") out.append( "\n</tr>") rank = 0 highlightKey = [False,False,False,False,True,True,True,True,False,False] mins = [0,0,0,0,40,40,40,-0.1,0,0] maxs = [0,0,0,0,60,70,60, 0.1,0,0] for bot in bots: rank += 1 if rank > lim: break botName=bot.Name botNameHref = "<a href=\"BotDetails?game="+game+"&name=" + botName.replace(" ","%20")+extraArgs+"\">"+botName+" </a>" compareHref = "<a href=\"BotCompare?game="+game+"&bota=" + name.replace(" ","%20") + "&botb=" + botName.replace(" ","%20") + extraArgs + "\">compare</a>" ft = [] ft.append("<img id='flag' src=\"/flags/") ft.append(bot.Flag) ft.append(".gif\">") flagtag = ''.join(ft) cells = [str(rank), flagtag, botNameHref, compareHref, round(100.0*bot.APS)*0.01, round(100.0*bot.NPP)*0.01, round(100.0*bot.Survival)*0.01, round(100.0*bot.KNNPBI)*0.01, bot.Battles, bot.LastUpload] out.append("\n<tr>") for i,cell in enumerate(cells): if highlightKey[i]: if cell < mins[i]: out.append( "\n<td class=\"red\">" + str(cell) + "</td>") elif cell > maxs[i]: out.append( "\n<td class=\"green\">" + str(cell) + "</td>") else: out.append( "\n<td>" + str(cell) + "</td>") else: out.append( "\n<td>" + str(cell) + "</td>") out.append( "\n</tr>") out.append( "</table>") htmltime = time.time() - sorttime - retrievetime - parsetime - starttime elapsed = time.time() - starttime if timing: out.append( "<br />\n Page served in " + str(int(round(elapsed*1000))) + "ms. Bot cached: " + str(cached)) out.append("\n<br /> parsing: " + str(int(round(parsetime*1000))) ) out.append("\n<br /> retrieve: " + str(int(round(retrievetime*1000))) ) out.append("\n<br /> sort: " + str(int(round(sorttime*1000))) ) out.append("\n<br /> html generation: " + str(int(round(htmltime*1000))) ) out.append( "</body></html>") outstr = string.join(out,"") self.response.out.write(outstr)
def get(self): global global_dict #global_dict = {} #starttime = time.time() parts = self.request.query_string.split("&") requests = {} if parts[0] != "": for pair in parts: ab = pair.split('=') requests[ab[0]] = ab[1] game = requests.get("game",None) if game is None: self.response.out.write("NO RUMBLE SPECIFIED IN FORM game=____") return version = requests.get("version",None) if version is None or version != "1": self.response.out.write("VERSION NOT SPECIFIED AS version=1") return rumble = global_dict.get(game,None) if rumble is None: rumble = memcache.get(game) if rumble is None: rumble = structures.Rumble.get_by_key_name(game) if rumble is None: self.response.out.write("RUMBLE NOT FOUND") return else: global_dict[game]=rumble memcache.set(game,rumble) else: global_dict[game] = rumble # try: # scoresdicts = marshal.loads(zlib.decompress(rumble.ParticipantsScores)) # scoreslist = [structures.LiteBot() for _ in scoresdicts] # for s,d in zip(scoreslist,scoresdicts): # s.__dict__.update(d) # r = scoreslist # except: botsdict = pickle.loads(zlib.decompress(rumble.ParticipantsScores)) r = botsdict.values() out = [] for bot in r: name = bot.Name name = name.replace(" ","_") out.append(name) out.append("=") out.append(str(bot.APS)) out.append(",") out.append(str(bot.Battles)) out.append(",") out.append(bot.LastUpload) out.append("\n") #line = name + "=" + str(bot.APS) + "," + str(bot.Battles) + "," + bot.LastUpload + "\n" #out.append(line) self.response.out.write(''.join(out))
def get(self): global global_dict #global_dict = {} starttime = time.time() query = self.request.query_string query = query.replace("%20"," ") parts = query.split("&") requests = {} if parts[0] != "": for pair in parts: ab = pair.split('=') requests[ab[0]] = ab[1] timing = bool(requests.get("timing",False)) regen = bool(requests.get("regen",False)) extraArgs = "" #if timing: # extraArgs += "&timing=1" outstr = None if not regen: outstr = global_dict.get("stats",None) if outstr is None: outstr = memcache.get("stats") if outstr is None: tq = taskqueue.Queue() tqs_r = tq.fetch_statistics_async() #gameHref = "<a href=Rankings?game=" + game + extraArgs + ">" + game + "</a>" out = [] out.append(structures.html_header % ("Statistics","LiteRumble Statistics")) out.append("\nStats generated: " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " UTC") out.append("\nAllowed Robocode versions: " + str(structures.allowed_clients) + "\n<br>\n") q = structures.Rumble.all() rumbles = [[],[],[]] categories = ["1v1","Melee","Teams"] for r in q.run(): memr = global_dict.get(r.Name,None) if memr is None: memr = memcache.get(r.Name) if memr is not None: r = memr if r.Melee: rumbles[1].append(r) elif r.Teams: rumbles[2].append(r) else: rumbles[0].append(r) for cat,rumbs in zip(categories,rumbles): for r in rumbs: try: scores = pickle.loads(zlib.decompress(r.ParticipantsScores)) except: logging.error("cannot load ParticipantsScores from " + r.Name) try: scores = marshal.loads(zlib.decompress(r.ParticipantsScores)) except: scores = {} entries = len(scores) if r.LastUpload is None: latest = None for s in scores.values(): t = s.LastUpload if latest is None or t > latest: latest = t r.LastUpload = latest r.__dict__["entries"] = entries #r.__dict__["scores"] = scores rumbs.sort(key = lambda r: -r.__dict__["entries"]) out.append( "<table class=\"rumble\">\n<tr>") out.append( "<th>" + cat + "</th>\n<th>Participants</th>\n<th>Total Uploads</th>\n<th>Last Upload</th></tr>") for i,r in enumerate(rumbs): game = r.Name gameHref = "<a href=\"Rankings?game=" + game + extraArgs + "\" ><b>" + game + "</b></a>" lastTimeSince = timeSince(r.LastUpload) + " ago" out.append( "\n<tr>\n<td>" + gameHref + "</td>\n<th>" + str(r.__dict__["entries"]) + "</th>\n<th>") out.append(str(r.TotalUploads) + "</th><th>"+lastTimeSince+"</th>\n</tr>") try: uploaders = pickle.loads(zlib.decompress(r.Uploaders)) if len(uploaders) == 0: uploaders = {} except TypeError: uploaders = {} #out.append("\n<tr><td></td><td><i><u>Uploader Name</u></i></td><td></td><td></td></tr>") uv = uploaders.values() cutoff = datetime.datetime.now() - datetime.timedelta(31) uv = filter(lambda u: datetime.datetime.strptime(u.latest,"%Y-%m-%d %H:%M:%S") > cutoff, uv) uv.sort(key = lambda u: u.latest, reverse=True) for j,u in enumerate(uv): # if j == 0: # out.append("\n<tr><td><i>Uploader Name</i></td><td>") # else: out.append("\n<tr><td></td><td>") name = u.name if name == "Put_Your_Name_Here": name = "Anonymous" out.append(name) out.append("</td><td>") out.append(str(u.total)) out.append("</td><td>") out.append(timeSince(u.latest) + " ago") out.append("</td></tr>") for r in rumbs: r.__dict__.pop("entries",1) #r.__dict__.pop("scores",1) out.append( "\n</table>") tqs = tqs_r.get_result() tasks = tqs.tasks last_min = tqs.executed_last_minute if last_min is None or last_min == 0: last_min = 1 if tasks is None: tasks is 0 backlog = float(tasks)*60.0/last_min #out.append() tq_string = "<table>\n<tr><th colspan=\"2\">Upload Queue</th></tr>\n<tr><td>Projected Processing Time</td><td>" + formatSecs(backlog) + "</td></tr>" tq_string += "\n<tr><td>Current Size</td><td>" + str(tasks) + " pairing" if tasks != 1: tq_string += "s" tq_string += "</td></tr>\n</table>\n<br>" out.insert(2,tq_string) outstr = string.join(out,"") memcache.set("stats",outstr) global_dict["stats"] = outstr elapsed = time.time() - starttime if timing: outstr += "<br>\n Page served in " + str(int(round(elapsed*1000))) + "ms." outstr += "</body></html>" self.response.out.write(outstr)