From: Jan D. Behrens Date: Mon, 27 Aug 2012 22:35:34 +0000 (+0200) Subject: Merge branch 'master' into badges X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=e8555dd70abb91613b9089fbd9cf2ce3ef9ee56a;p=xonotic%2Fxonstat.git Merge branch 'master' into badges Conflicts: xonstat/batch/badges/gen_badges.py --- e8555dd70abb91613b9089fbd9cf2ce3ef9ee56a diff --cc xonstat/batch/badges/gen_badges.py index 905200d,d59c336..d554560 --- a/xonstat/batch/badges/gen_badges.py +++ b/xonstat/batch/badges/gen_badges.py @@@ -1,18 -1,14 +1,16 @@@ #-*- coding: utf-8 -*- +import sys +import re import cairo as C + from datetime import datetime import sqlalchemy as sa import sqlalchemy.sql.functions as func - from datetime import datetime - from mako.template import Template + from colorsys import rgb_to_hls, hls_to_rgb from os import system from pyramid.paster import bootstrap from xonstat.models import * - from xonstat.views.player import player_info_data -from xonstat.util import qfont_decode +from xonstat.util import qfont_decode, _all_colors - from colorsys import rgb_to_hls, hls_to_rgb # similar to html_colors() from util.py @@@ -31,37 -27,11 +29,37 @@@ _dec_colors = [ (0.5,0.5,0.5) ] +# maximal number of query results (for testing, set to 0 to get all) - NUM_PLAYERS = 50 ++NUM_PLAYERS = 100 + +# image dimensions (likely breaks layout if changed) +WIDTH = 560 +HEIGHT = 70 + +# output filename, parameter is player id +OUTPUT = "output/%d.png" + + +NICK_POS = (5,18) +NICK_MAXWIDTH = 330 + +GAMES_POS = (60,35) +GAMES_WIDTH = 110 + +WINLOSS_POS = (508,6) +WINLOSS_WIDTH = 104 + +KILLDEATH_POS = (390,6) +KILLDEATH_WIDTH = 104 + +PLAYTIME_POS = (452,64) +PLAYTIME_WIDTH = 218 + + # parameters to affect the output, could be changed via URL params = { - 'width': 560, - 'height': 70, - 'bg': 1, # 0 - black, 1 - dark_wall + 'bg': 1, # 0 - black, 1 - dark_wall, ... + 'overlay': 0, # 0 - none, 1 - default overlay, ... 'font': 0, # 0 - xolonium, 1 - dejavu sans } @@@ -109,31 -73,23 +107,31 @@@ def get_data(player) total_stats['gametypes'].append(game_type_cd) total_stats['games_breakdown'][game_type_cd] = games total_stats['games_alivetime'][game_type_cd] = alivetime - + - (total_stats['kills'], total_stats['deaths'], total_stats['suicides'], - total_stats['alivetime'],) = DBSession.query( + (total_stats['kills'], total_stats['deaths'], total_stats['alivetime'],) = DBSession.query( func.sum(PlayerGameStat.kills), func.sum(PlayerGameStat.deaths), - func.sum(PlayerGameStat.suicides), func.sum(PlayerGameStat.alivetime)).\ filter(PlayerGameStat.player_id == player_id).\ one() - + - (total_stats['wins'],) = DBSession.query( - func.count("*")).\ - filter(Game.game_id == PlayerGameStat.game_id).\ - filter(PlayerGameStat.player_id == player_id).\ - filter(Game.winner == PlayerGameStat.team or PlayerGameStat.rank == 1).\ - one() +# (total_stats['wins'],) = DBSession.query( +# func.count("*")).\ +# filter(Game.game_id == PlayerGameStat.game_id).\ +# filter(PlayerGameStat.player_id == player_id).\ +# filter(Game.winner == PlayerGameStat.team or PlayerGameStat.rank == 1).\ +# one() + + (total_stats['wins'],) = DBSession.\ + query("total_wins").\ + from_statement( + "select count(*) total_wins " + "from games g, player_game_stats pgs " + "where g.game_id = pgs.game_id " + "and player_id=:player_id " + "and (g.winner = pgs.team or pgs.rank = 1)" + ).params(player_id=player_id).one() - + ranks = DBSession.query("game_type_cd", "rank", "max_rank").\ from_statement( "select pr.game_type_cd, pr.rank, overall.max_rank " @@@ -173,7 -129,10 +171,6 @@@ def render_image(data): """Render an image from the given data fields.""" - - - width, height = params['width'], params['height'] - output = "output/%s.png" % data['player'].player_id - font = "Xolonium" if params['font'] == 1: font = "DejaVu Sans" @@@ -192,10 -151,10 +189,10 @@@ # draw background (just plain fillcolor) if params['bg'] == 0: - ctx.rectangle(0, 0, width, height) - ctx.set_source_rgba(0.2, 0.2, 0.2, 1.0) + ctx.rectangle(0, 0, WIDTH, HEIGHT) + ctx.set_source_rgb(0.04, 0.04, 0.04) # bgcolor of Xonotic forum ctx.fill() - + # draw background image (try to get correct tiling, too) if params['bg'] > 0: bg = None @@@ -227,86 -171,69 +224,85 @@@ ctx.paint() bg_yoff += bg_h bg_xoff += bg_w - + + # draw overlay graphic + if params['overlay'] > 0: + overlay = None + if params['overlay'] == 1: + overlay = C.ImageSurface.create_from_png("img/overlay.png") + if overlay: + ctx.set_source_surface(overlay, 0, 0) + ctx.mask_surface(overlay) + ctx.paint() - ## draw player's nickname with fancy colors + ## draw player's nickname with fancy colors + + # deocde nick, strip all weird-looking characters - qstr = qfont_decode(player.nick).replace('^^', '^').replace(u'\x00', '').replace(u' ', ' ') ++ qstr = qfont_decode(player.nick).replace('^^', '^').replace(u'\x00', '') + chars = [] + for c in qstr: + if ord(c) < 128: + chars.append(c) + qstr = ''.join(chars) + stripped_nick = strip_colors(qstr) + # fontsize is reduced if width gets too large - nick_xmax = 335 ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) ctx.set_font_size(20) - xoff, yoff, tw, th = ctx.text_extents(player.stripped_nick)[:4] - if tw > nick_xmax: + xoff, yoff, tw, th = ctx.text_extents(stripped_nick)[:4] + if tw > NICK_MAXWIDTH: ctx.set_font_size(18) - xoff, yoff, tw, th = ctx.text_extents(player.stripped_nick)[:4] - if tw > nick_xmax: + xoff, yoff, tw, th = ctx.text_extents(stripped_nick)[:4] + if tw > NICK_MAXWIDTH: ctx.set_font_size(16) - xoff, yoff, tw, th = ctx.text_extents(player.stripped_nick)[:4] - if tw > nick_xmax: + xoff, yoff, tw, th = ctx.text_extents(stripped_nick)[:4] + if tw > NICK_MAXWIDTH: ctx.set_font_size(14) - xoff, yoff, tw, th = ctx.text_extents(player.stripped_nick)[:4] - if tw > nick_xmax: + xoff, yoff, tw, th = ctx.text_extents(stripped_nick)[:4] + if tw > NICK_MAXWIDTH: ctx.set_font_size(12) - + - # split up nick into colored segments and draw each of them - qstr = qfont_decode(player.nick).replace('^^', '^').replace('\x00', ' ') - txt_xoff = 0 - txt_xpos, txt_ypos = 5,18 - + # split nick into colored segments - parts = [] - pos = 1 - while True: - npos = qstr.find('^', pos) - if npos < 0: - parts.append(qstr[pos-1:]) - break; - parts.append(qstr[pos-1:npos]) - pos = npos+1 - - for txt in parts: + xoffset = 0 + _all_colors = re.compile(r'(\^\d|\^x[\dA-Fa-f]{3})') + #print qstr, _all_colors.findall(qstr), _all_colors.split(qstr) + + parts = _all_colors.split(qstr) + while len(parts) > 0: + tag = None + txt = parts[0] + if _all_colors.match(txt): + tag = txt[1:] # strip leading '^' + if len(parts) < 2: + break + txt = parts[1] + del parts[1] + del parts[0] + + if not txt or len(txt) == 0: + # only colorcode and no real text, skip this + continue + r,g,b = _dec_colors[7] try: - if txt.startswith('^'): - txt = txt[1:] - if txt.startswith('x'): - r = int(txt[1] * 2, 16) / 255.0 - g = int(txt[2] * 2, 16) / 255.0 - b = int(txt[3] * 2, 16) / 255.0 - hue, light, satur = rgb_to_hls(r, g, b) - if light < _contrast_threshold: - light = _contrast_threshold - r, g, b = hls_to_rgb(hue, light, satur) - txt = txt[4:] - else: - r,g,b = _dec_colors[int(txt[0])] - txt = txt[1:] + if tag.startswith('x'): + r = int(tag[1] * 2, 16) / 255.0 + g = int(tag[2] * 2, 16) / 255.0 + b = int(tag[3] * 2, 16) / 255.0 + hue, light, satur = rgb_to_hls(r, g, b) + if light < _contrast_threshold: + light = _contrast_threshold + r, g, b = hls_to_rgb(hue, light, satur) + else: + r,g,b = _dec_colors[int(tag[0])] except: r,g,b = _dec_colors[7] - - if len(txt) < 1: - # only colorcode and no real text, skip this - continue - + ctx.set_source_rgb(r, g, b) - ctx.move_to(txt_xpos + txt_xoff, txt_ypos) + ctx.move_to(NICK_POS[0] + xoffset, NICK_POS[1]) ctx.show_text(txt) xoff, yoff, tw, th = ctx.text_extents(txt)[:4] @@@ -324,21 -256,21 +320,22 @@@ ctx.set_source_rgb(1.0, 1.0, 1.0) txt = "[ %s ]" % gt.upper() xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(games_x-xoff-tw/2,games_y-yoff-4) + ctx.move_to(GAMES_POS[0]+xoffset-xoff-tw/2, GAMES_POS[1]-yoff-4) ctx.show_text(txt) + + old_aa = ctx.get_antialias() ctx.set_antialias(C.ANTIALIAS_NONE) ctx.set_source_rgb(0.8, 0.8, 0.8) ctx.set_line_width(1) - ctx.move_to(games_x-games_w/2+5, games_y+8) - ctx.line_to(games_x+games_w/2-5, games_y+8) + ctx.move_to(GAMES_POS[0]+xoffset-GAMES_WIDTH/2+5, GAMES_POS[1]+8) + ctx.line_to(GAMES_POS[0]+xoffset+GAMES_WIDTH/2-5, GAMES_POS[1]+8) ctx.stroke() - ctx.move_to(games_x-games_w/2+5, games_y+32) - ctx.line_to(games_x+games_w/2-5, games_y+32) + ctx.move_to(GAMES_POS[0]+xoffset-GAMES_WIDTH/2+5, GAMES_POS[1]+32) + ctx.line_to(GAMES_POS[0]+xoffset+GAMES_WIDTH/2-5, GAMES_POS[1]+32) ctx.stroke() ctx.set_antialias(old_aa) - + if not elos.has_key(gt) or not ranks.has_key(gt): ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_BOLD) ctx.set_font_size(12) @@@ -371,12 -303,13 +368,12 @@@ # print win percentage - - win_x, win_y = 505,11 - win_w, win_h = 100,14 + - ctx.rectangle(win_x-win_w/2,win_y-win_h/2,win_w,win_h) - ctx.set_source_rgba(0.8, 0.8, 0.8, 0.1) - ctx.fill(); + if params['overlay'] == 0: + ctx.rectangle(WINLOSS_POS[0]-WINLOSS_WIDTH/2, WINLOSS_POS[1]-7, WINLOSS_WIDTH, 15) + ctx.set_source_rgba(0.8, 0.8, 0.8, 0.1) + ctx.fill(); - + ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) ctx.set_font_size(10) ctx.set_source_rgb(0.8, 0.8, 0.8) @@@ -427,27 -353,25 +424,28 @@@ # print kill/death ratio - - kill_x, kill_y = 395,11 - kill_w, kill_h = 100,14 + - ctx.rectangle(kill_x-kill_w/2,kill_y-kill_h/2,kill_w,kill_h) - ctx.set_source_rgba(0.8, 0.8, 0.8, 0.1) - ctx.fill() + if params['overlay'] == 0: + ctx.rectangle(KILLDEATH_POS[0]-KILLDEATH_WIDTH/2, KILLDEATH_POS[1]-7, KILLDEATH_WIDTH, 15) + ctx.set_source_rgba(0.8, 0.8, 0.8, 0.1) + ctx.fill() - + ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) ctx.set_font_size(10) ctx.set_source_rgb(0.8, 0.8, 0.8) txt = "Kill Ratio" xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(kill_x-xoff-tw/2,kill_y-yoff-3) + ctx.move_to(KILLDEATH_POS[0]-xoff-tw/2, KILLDEATH_POS[1]-yoff-3) ctx.show_text(txt) - + + kills, deaths = total_stats['kills'] , total_stats['deaths'] txt = "???" - if total_stats['deaths'] > 0 and total_stats['kills'] is not None: - ratio = float(total_stats['kills'])/total_stats['deaths'] + try: + ratio = float(kills)/deaths txt = "%.3f" % round(ratio, 3) + except: + ratio = 0 ++ ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_BOLD) ctx.set_font_size(12) if ratio >= 3: @@@ -483,12 -404,13 +481,12 @@@ # print playing time - - time_x, time_y = 450,64 - time_w, time_h = 210,10 + - ctx.rectangle(time_x-time_w/2,time_y-time_h/2-1,time_w,time_y+time_h/2-1) - ctx.set_source_rgba(0.8, 0.8, 0.8, 0.6) - ctx.fill(); + if params['overlay'] == 0: + ctx.rectangle( PLAYTIME_POS[0]-PLAYTIME_WIDTH/2, PLAYTIME_POS[1]-7, PLAYTIME_WIDTH, 14) + ctx.set_source_rgba(0.8, 0.8, 0.8, 0.6) + ctx.fill(); - + ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) ctx.set_font_size(10) ctx.set_source_rgb(0.1, 0.1, 0.1) @@@ -536,11 -459,13 +535,14 @@@ for player in players sstart = datetime.now() render_image(data) sstop = datetime.now() - render_time += (sstop-sstart).total_seconds() - print + td = sstop-sstart + total_seconds = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 + render_time += total_seconds stop = datetime.now() - print "Creating the badges took %.2f seconds (%.2f s per player)" % ((stop-start).total_seconds(), (stop-start).total_seconds()/float(len(players))) - print " Total time for getting data: %.2f s" % data_time - print " Total time for renering images: %.2f s" % render_time + td = stop-start + total_seconds = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 + print "Creating the badges took %.2f seconds (%.2f s per player)" % (total_seconds, total_seconds/float(len(players))) + print "Total time for redering images: %.2f s" % render_time + print "Total time for getting data: %.2f s" % data_time +