-Metadata-Version: 1.0
+Metadata-Version: 1.1
Name: XonStat
Version: 0.0
Summary: XonStat
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
-Description: This is XonStat, the application in front of xonstatdb. XonStat handles the submission of statistical information from the open source first person shooter Xonotic.
+Description: This is **XonStat**, the application in front of [xonstatdb][xonstatdb].
+ [XonStat][xonstat] handles the submission of statistical information from the open source first person shooter [Xonotic][xonotic].
+
+ ----
To start, first run the following from the root directory to set up dependencies:
python setup.py develop
- Next you'll want to set up xonstatdb. This is maintained as a separate project here:
+ Next you'll want to set up [xonstatdb][xonstatdb]. This is maintained as a separate project here:
https://github.com/antzucaro/xonstatdb
- Next you'll want to open up development.ini and change a few things for added security. Chief among these is the "sqlalchemy.url" setting, which contains your username and password for the database. Change that match the new password you gave xonstat during the installation of xonstatdb. The other setting to change is "security.secret," which is used to keep your web session (cookies and such) secure.
+ Next you'll want to open up development.ini and change a few things for added security.
+ Chief among these is the "sqlalchemy.url" setting, which contains your username and password for the database.
+ Change that match the new password you gave xonstat during the installation of xonstatdb.
+ The other setting to change is "session.secret," which is used to keep your web session (cookies and such) secure.
To start the server run the following from the root directory. I recommend running this within a GNU screen session:
paster serve development.ini #(or production.ini if you've configured that settings file instead)
- To get a Xonotic server configured to use this server, change the CVAR "g_playerstats_uri" to point to the correct host, port, and URL path. By default this is:
+ To get a Xonotic server configured to use this server, change the CVAR `g_playerstats_uri` to point to the correct host, port, and URL path. By default this is:
http://localhost:6543/stats/submit
- ...so in the command line of the server (or in your config) you can put:
+ ...so in the server console (or in your config) you can put:
set g_playerstats_uri http://localhost:6543/stats/submit
- If you have any questions or issues please open up a bug report here, or - better yet ! - fork it and send me a pull request.
+ If you have any questions or issues please open up a bug report here, or - better yet ! - fork it and send me a pull request.
- TODO:
+ [xonstatdb]: https://github.com/antzucaro/xonstatdb
+ [xonstat]: http://stats.xonotic.org/
+ [xonotic]: http://www.xonotic.org/
- - "e matches" and "e joins" seem to be mutually exclusive. Add a check for either (instead of just joins" before adding a player_game_stats record).
+ ----
- - map names are being recorded multiple times in the maps table. They should be found when being played subsequent times.
+ Project is licensed GPLv2+.
0.0
CHANGES.txt
COPYING.txt
MANIFEST.in
-README.txt
development.ini
production.ini
setup.cfg
setup.py
+test.txt
XonStat.egg-info/PKG-INFO
XonStat.egg-info/SOURCES.txt
XonStat.egg-info/dependency_links.txt
XonStat.egg-info/requires.txt
XonStat.egg-info/top_level.txt
xonstat/__init__.py
+xonstat/d0_blind_id.py
+xonstat/elo.py
xonstat/models.py
xonstat/tests.py
xonstat/util.py
+xonstat/batch/badges/img/asfalt.png
+xonstat/batch/badges/img/background_archer-v1.png
+xonstat/batch/badges/img/black_linen_v2.png
+xonstat/batch/badges/img/broken_noise.png
+xonstat/batch/badges/img/burried.png
+xonstat/batch/badges/img/dark_leather.png
+xonstat/batch/badges/img/dark_wall.png
+xonstat/batch/badges/img/overlay_classic.png
+xonstat/batch/badges/img/overlay_minimal.png
+xonstat/batch/badges/img/txture.png
+xonstat/batch/badges/output/10180.png
+xonstat/batch/badges/output/10427.png
+xonstat/batch/badges/output/10543.png
+xonstat/batch/badges/output/10551.png
+xonstat/batch/badges/output/1060.png
+xonstat/batch/badges/output/10627.png
+xonstat/batch/badges/output/10710.png
+xonstat/batch/badges/output/10738.png
+xonstat/batch/badges/output/10780.png
+xonstat/batch/badges/output/10814.png
+xonstat/batch/badges/output/10876.png
+xonstat/batch/badges/output/10939.png
+xonstat/batch/badges/output/10978.png
+xonstat/batch/badges/output/1101.png
+xonstat/batch/badges/output/1229.png
+xonstat/batch/badges/output/1543.png
+xonstat/batch/badges/output/1726.png
+xonstat/batch/badges/output/1789.png
+xonstat/batch/badges/output/1820.png
+xonstat/batch/badges/output/2024.png
+xonstat/batch/badges/output/2026.png
+xonstat/batch/badges/output/2148.png
+xonstat/batch/badges/output/2206.png
+xonstat/batch/badges/output/2247.png
+xonstat/batch/badges/output/2265.png
+xonstat/batch/badges/output/2311.png
+xonstat/batch/badges/output/2409.png
+xonstat/batch/badges/output/2485.png
+xonstat/batch/badges/output/251.png
+xonstat/batch/badges/output/26.png
+xonstat/batch/badges/output/2627.png
+xonstat/batch/badges/output/264.png
+xonstat/batch/badges/output/2640.png
+xonstat/batch/badges/output/2755.png
+xonstat/batch/badges/output/2769.png
+xonstat/batch/badges/output/2848.png
+xonstat/batch/badges/output/2961.png
+xonstat/batch/badges/output/3071.png
+xonstat/batch/badges/output/3322.png
+xonstat/batch/badges/output/3367.png
+xonstat/batch/badges/output/3370.png
+xonstat/batch/badges/output/359.png
+xonstat/batch/badges/output/3652.png
+xonstat/batch/badges/output/3666.png
+xonstat/batch/badges/output/3715.png
+xonstat/batch/badges/output/3781.png
+xonstat/batch/badges/output/3823.png
+xonstat/batch/badges/output/3903.png
+xonstat/batch/badges/output/3911.png
+xonstat/batch/badges/output/4057.png
+xonstat/batch/badges/output/4059.png
+xonstat/batch/badges/output/4083.png
+xonstat/batch/badges/output/4295.png
+xonstat/batch/badges/output/4321.png
+xonstat/batch/badges/output/4323.png
+xonstat/batch/badges/output/4349.png
+xonstat/batch/badges/output/4387.png
+xonstat/batch/badges/output/4388.png
+xonstat/batch/badges/output/4466.png
+xonstat/batch/badges/output/4718.png
+xonstat/batch/badges/output/4722.png
+xonstat/batch/badges/output/4875.png
+xonstat/batch/badges/output/4888.png
+xonstat/batch/badges/output/496.png
+xonstat/batch/badges/output/4996.png
+xonstat/batch/badges/output/5028.png
+xonstat/batch/badges/output/5116.png
+xonstat/batch/badges/output/5161.png
+xonstat/batch/badges/output/5411.png
+xonstat/batch/badges/output/5729.png
+xonstat/batch/badges/output/6312.png
+xonstat/batch/badges/output/6755.png
+xonstat/batch/badges/output/6810.png
+xonstat/batch/badges/output/6873.png
+xonstat/batch/badges/output/6967.png
+xonstat/batch/badges/output/7003.png
+xonstat/batch/badges/output/7080.png
+xonstat/batch/badges/output/7247.png
+xonstat/batch/badges/output/7416.png
+xonstat/batch/badges/output/7494.png
+xonstat/batch/badges/output/7519.png
+xonstat/batch/badges/output/7678.png
+xonstat/batch/badges/output/7942.png
+xonstat/batch/badges/output/7981.png
+xonstat/batch/badges/output/8087.png
+xonstat/batch/badges/output/8198.png
+xonstat/batch/badges/output/8519.png
+xonstat/batch/badges/output/8533.png
+xonstat/batch/badges/output/8761.png
+xonstat/batch/badges/output/8985.png
+xonstat/batch/badges/output/9203.png
+xonstat/batch/badges/output/9223.png
+xonstat/batch/badges/output/9230.png
+xonstat/batch/badges/output/9243.png
+xonstat/batch/badges/output/9344.png
+xonstat/batch/badges/output/9394.png
+xonstat/batch/badges/output/9545.png
+xonstat/batch/badges/output/9684.png
+xonstat/batch/badges/output/9803.png
+xonstat/batch/badges/output/9970.png
+xonstat/batch/badges/output/minimal/10180.png
+xonstat/batch/badges/output/minimal/10427.png
+xonstat/batch/badges/output/minimal/10543.png
+xonstat/batch/badges/output/minimal/10551.png
+xonstat/batch/badges/output/minimal/1060.png
+xonstat/batch/badges/output/minimal/10627.png
+xonstat/batch/badges/output/minimal/10710.png
+xonstat/batch/badges/output/minimal/10738.png
+xonstat/batch/badges/output/minimal/10780.png
+xonstat/batch/badges/output/minimal/10814.png
+xonstat/batch/badges/output/minimal/10876.png
+xonstat/batch/badges/output/minimal/10939.png
+xonstat/batch/badges/output/minimal/10978.png
+xonstat/batch/badges/output/minimal/1101.png
+xonstat/batch/badges/output/minimal/1229.png
+xonstat/batch/badges/output/minimal/1543.png
+xonstat/batch/badges/output/minimal/1726.png
+xonstat/batch/badges/output/minimal/1789.png
+xonstat/batch/badges/output/minimal/1820.png
+xonstat/batch/badges/output/minimal/2024.png
+xonstat/batch/badges/output/minimal/2026.png
+xonstat/batch/badges/output/minimal/2148.png
+xonstat/batch/badges/output/minimal/2206.png
+xonstat/batch/badges/output/minimal/2247.png
+xonstat/batch/badges/output/minimal/2265.png
+xonstat/batch/badges/output/minimal/2311.png
+xonstat/batch/badges/output/minimal/2409.png
+xonstat/batch/badges/output/minimal/2485.png
+xonstat/batch/badges/output/minimal/251.png
+xonstat/batch/badges/output/minimal/26.png
+xonstat/batch/badges/output/minimal/2627.png
+xonstat/batch/badges/output/minimal/264.png
+xonstat/batch/badges/output/minimal/2640.png
+xonstat/batch/badges/output/minimal/2755.png
+xonstat/batch/badges/output/minimal/2769.png
+xonstat/batch/badges/output/minimal/2848.png
+xonstat/batch/badges/output/minimal/2961.png
+xonstat/batch/badges/output/minimal/3071.png
+xonstat/batch/badges/output/minimal/3322.png
+xonstat/batch/badges/output/minimal/3367.png
+xonstat/batch/badges/output/minimal/3370.png
+xonstat/batch/badges/output/minimal/359.png
+xonstat/batch/badges/output/minimal/3652.png
+xonstat/batch/badges/output/minimal/3666.png
+xonstat/batch/badges/output/minimal/3715.png
+xonstat/batch/badges/output/minimal/3781.png
+xonstat/batch/badges/output/minimal/3823.png
+xonstat/batch/badges/output/minimal/3903.png
+xonstat/batch/badges/output/minimal/3911.png
+xonstat/batch/badges/output/minimal/4057.png
+xonstat/batch/badges/output/minimal/4059.png
+xonstat/batch/badges/output/minimal/4083.png
+xonstat/batch/badges/output/minimal/4295.png
+xonstat/batch/badges/output/minimal/4321.png
+xonstat/batch/badges/output/minimal/4323.png
+xonstat/batch/badges/output/minimal/4349.png
+xonstat/batch/badges/output/minimal/4387.png
+xonstat/batch/badges/output/minimal/4388.png
+xonstat/batch/badges/output/minimal/4466.png
+xonstat/batch/badges/output/minimal/4718.png
+xonstat/batch/badges/output/minimal/4722.png
+xonstat/batch/badges/output/minimal/4875.png
+xonstat/batch/badges/output/minimal/4888.png
+xonstat/batch/badges/output/minimal/496.png
+xonstat/batch/badges/output/minimal/4996.png
+xonstat/batch/badges/output/minimal/5028.png
+xonstat/batch/badges/output/minimal/5116.png
+xonstat/batch/badges/output/minimal/5161.png
+xonstat/batch/badges/output/minimal/5411.png
+xonstat/batch/badges/output/minimal/5729.png
+xonstat/batch/badges/output/minimal/6312.png
+xonstat/batch/badges/output/minimal/6755.png
+xonstat/batch/badges/output/minimal/6810.png
+xonstat/batch/badges/output/minimal/6873.png
+xonstat/batch/badges/output/minimal/6967.png
+xonstat/batch/badges/output/minimal/7003.png
+xonstat/batch/badges/output/minimal/7080.png
+xonstat/batch/badges/output/minimal/7247.png
+xonstat/batch/badges/output/minimal/7416.png
+xonstat/batch/badges/output/minimal/7494.png
+xonstat/batch/badges/output/minimal/7519.png
+xonstat/batch/badges/output/minimal/7678.png
+xonstat/batch/badges/output/minimal/7942.png
+xonstat/batch/badges/output/minimal/7981.png
+xonstat/batch/badges/output/minimal/8087.png
+xonstat/batch/badges/output/minimal/8198.png
+xonstat/batch/badges/output/minimal/8519.png
+xonstat/batch/badges/output/minimal/8533.png
+xonstat/batch/badges/output/minimal/8761.png
+xonstat/batch/badges/output/minimal/8985.png
+xonstat/batch/badges/output/minimal/9203.png
+xonstat/batch/badges/output/minimal/9223.png
+xonstat/batch/badges/output/minimal/9230.png
+xonstat/batch/badges/output/minimal/9243.png
+xonstat/batch/badges/output/minimal/9344.png
+xonstat/batch/badges/output/minimal/9394.png
+xonstat/batch/badges/output/minimal/9545.png
+xonstat/batch/badges/output/minimal/9684.png
+xonstat/batch/badges/output/minimal/9803.png
+xonstat/batch/badges/output/minimal/9970.png
xonstat/static/favicon.ico
xonstat/static/css/colorbox.css
-xonstat/static/css/normalize.css
+xonstat/static/css/reset.css
xonstat/static/css/style.css
+xonstat/static/css/style.min.css
+xonstat/static/css/tables.css
+xonstat/static/css/img/Xonotic_icon.png
+xonstat/static/css/img/button_sprite.png
+xonstat/static/css/img/glyphicons-halflings-white.png
+xonstat/static/css/img/glyphicons-halflings.png
+xonstat/static/css/img/inputbox_sprite.png
+xonstat/static/css/img/web_background.png
+xonstat/static/css/img/web_background_2.jpg
+xonstat/static/css/img/web_background_3.jpg
+xonstat/static/css/img/web_background_l2.png
+xonstat/static/css/img/web_border.png
+xonstat/static/css/img/web_checkbox_c0.png
+xonstat/static/css/img/web_checkbox_c1.png
+xonstat/static/css/img/web_checkbox_d0.png
+xonstat/static/css/img/web_checkbox_d1.png
+xonstat/static/css/img/web_checkbox_f0.png
+xonstat/static/css/img/web_checkbox_f1.png
+xonstat/static/css/img/web_checkbox_n0.png
+xonstat/static/css/img/web_checkbox_n1.png
+xonstat/static/css/img/web_checkmark.png
+xonstat/static/css/img/web_closebutton_c.png
+xonstat/static/css/img/web_closebutton_f.png
+xonstat/static/css/img/web_closebutton_n.png
+xonstat/static/css/img/web_colorpicker.png
+xonstat/static/css/img/web_colorpicker_selected.png
+xonstat/static/css/img/web_cursor.png
+xonstat/static/css/img/web_cursor_move.png
+xonstat/static/css/img/web_cursor_resize.png
+xonstat/static/css/img/web_cursor_resize2.png
+xonstat/static/css/img/web_icon_aeslevel1.png
+xonstat/static/css/img/web_icon_aeslevel2.png
+xonstat/static/css/img/web_icon_aeslevel3.png
+xonstat/static/css/img/web_icon_aeslevel4.png
+xonstat/static/css/img/web_icon_aeslevel5.png
+xonstat/static/css/img/web_icon_ipv4.png
+xonstat/static/css/img/web_icon_ipv6.png
+xonstat/static/css/img/web_icon_pure1.png
+xonstat/static/css/img/web_radiobutton_c0.png
+xonstat/static/css/img/web_radiobutton_c1.png
+xonstat/static/css/img/web_radiobutton_d0.png
+xonstat/static/css/img/web_radiobutton_d1.png
+xonstat/static/css/img/web_radiobutton_f0.png
+xonstat/static/css/img/web_radiobutton_f1.png
+xonstat/static/css/img/web_radiobutton_n0.png
+xonstat/static/css/img/web_radiobutton_n1.png
+xonstat/static/css/img/web_scrollbar_c.png
+xonstat/static/css/img/web_scrollbar_f.png
+xonstat/static/css/img/web_scrollbar_n.png
+xonstat/static/css/img/web_scrollbar_s.png
+xonstat/static/css/img/web_slider_c.png
+xonstat/static/css/img/web_slider_d.png
+xonstat/static/css/img/web_slider_f.png
+xonstat/static/css/img/web_slider_n.png
+xonstat/static/css/img/web_slider_s.png
+xonstat/static/css/img/web_tooltip.png
xonstat/static/images/border.png
xonstat/static/images/controls.png
xonstat/static/images/crylink.png
xonstat/static/images/minstanex.png
xonstat/static/images/nex.png
xonstat/static/images/porto.png
+xonstat/static/images/rifle.png
xonstat/static/images/rocketlauncher.png
xonstat/static/images/seeker.png
xonstat/static/images/shotgun.png
-xonstat/static/images/sniperrifle.png
xonstat/static/images/tuba.png
xonstat/static/images/uzi.png
+xonstat/static/images/icons/24x24/arena.png
+xonstat/static/images/icons/24x24/as.png
+xonstat/static/images/icons/24x24/assault.png
+xonstat/static/images/icons/24x24/ca.png
+xonstat/static/images/icons/24x24/ctf.png
+xonstat/static/images/icons/24x24/cts.png
+xonstat/static/images/icons/24x24/dm.png
+xonstat/static/images/icons/24x24/dom.png
+xonstat/static/images/icons/24x24/duel.png
+xonstat/static/images/icons/24x24/freezetag.png
+xonstat/static/images/icons/24x24/ft.png
+xonstat/static/images/icons/24x24/ka.png
+xonstat/static/images/icons/24x24/kh.png
+xonstat/static/images/icons/24x24/lms.png
+xonstat/static/images/icons/24x24/nb.png
+xonstat/static/images/icons/24x24/nexball.png
+xonstat/static/images/icons/24x24/ons.png
+xonstat/static/images/icons/24x24/overall.png
+xonstat/static/images/icons/24x24/rc.png
+xonstat/static/images/icons/24x24/rune.png
+xonstat/static/images/icons/24x24/tdm.png
+xonstat/static/images/icons/48x48/arena.png
+xonstat/static/images/icons/48x48/as.png
+xonstat/static/images/icons/48x48/assault.png
+xonstat/static/images/icons/48x48/ca.png
+xonstat/static/images/icons/48x48/ctf.png
+xonstat/static/images/icons/48x48/cts.png
+xonstat/static/images/icons/48x48/dm.png
+xonstat/static/images/icons/48x48/dom.png
+xonstat/static/images/icons/48x48/duel.png
+xonstat/static/images/icons/48x48/freezetag.png
+xonstat/static/images/icons/48x48/ft.png
+xonstat/static/images/icons/48x48/ka.png
+xonstat/static/images/icons/48x48/kh.png
+xonstat/static/images/icons/48x48/lms.png
+xonstat/static/images/icons/48x48/nb.png
+xonstat/static/images/icons/48x48/nexball.png
+xonstat/static/images/icons/48x48/ons.png
+xonstat/static/images/icons/48x48/overall.png
+xonstat/static/images/icons/48x48/rc.png
+xonstat/static/images/icons/48x48/rune.png
+xonstat/static/images/icons/48x48/tdm.png
+xonstat/static/js/bootstrap-alert.js
+xonstat/static/js/bootstrap-button.js
+xonstat/static/js/bootstrap-carousel.js
+xonstat/static/js/bootstrap-collapse.js
+xonstat/static/js/bootstrap-collapse.min.js
+xonstat/static/js/bootstrap-dropdown.js
+xonstat/static/js/bootstrap-modal.js
+xonstat/static/js/bootstrap-popover.js
+xonstat/static/js/bootstrap-scrollspy.js
+xonstat/static/js/bootstrap-tab.js
+xonstat/static/js/bootstrap-tabs.js
+xonstat/static/js/bootstrap-tooltip.js
+xonstat/static/js/bootstrap-transition.js
+xonstat/static/js/bootstrap-typeahead.js
+xonstat/static/js/default.js
+xonstat/static/js/jquery-1.6.1.min.js
+xonstat/static/js/jquery-1.7.1.min.js
xonstat/static/js/jquery.colorbox-min.js
xonstat/static/js/jquery.dataTables.min.js
-xonstat/static/js/jquery.js
+xonstat/static/js/jquery.dataTables.numHtml.js
+xonstat/static/js/jquery.flot.min.js
xonstat/templates/accuracy.mako
xonstat/templates/base.mako
xonstat/templates/game_index.mako
xonstat/templates/map_index.mako
xonstat/templates/map_info.mako
xonstat/templates/mytemplate.pt
+xonstat/templates/nav.mako
+xonstat/templates/navlinks.mako
xonstat/templates/player_game_index.mako
xonstat/templates/player_index.mako
xonstat/templates/player_info.mako
+xonstat/templates/rank_index.mako
xonstat/templates/scoreboard.mako
+xonstat/templates/search.mako
xonstat/templates/server_game_index.mako
xonstat/templates/server_index.mako
xonstat/templates/server_info.mako
xonstat/views/main.py
xonstat/views/map.py
xonstat/views/player.py
+xonstat/views/search.py
xonstat/views/server.py
xonstat/views/submission.py
\ No newline at end of file
-pyramid>=1.1
+pyramid
SQLAlchemy
transaction
repoze.tm2>=1.0b1
sqlahelper
webhelpers
psycopg2
-PasteScript
'repoze.tm2>=1.0b1', # default_commit_veto
'zope.sqlalchemy',
'WebError',
- 'pyramid-jinja2',
'sqlahelper',
'webhelpers',
'psycopg2',
import sqlahelper
from pyramid_beaker import set_cache_regions_from_settings
from pyramid.config import Configurator
+from pyramid.httpexceptions import HTTPNotFound
from pyramid.renderers import JSONP
from sqlalchemy import engine_from_config
from xonstat.models import initialize_db
config = Configurator(settings=settings)
+ config.add_renderer('jsonp', JSONP(param_name='callback'))
+
+ # for static assets
config.add_static_view('static', 'xonstat:static')
- config.add_renderer('jsonp', JSONP(param_name='callback'))
+ # for 404s
+ config.add_view(notfound, context=HTTPNotFound, renderer="404.mako")
# ROOT ROUTE
config.add_route("main_index", "/")
config.add_view(main_index, route_name="main_index", renderer="main_index.mako")
# MAIN SUBMISSION ROUTE
- #config.add_route("stats_submit", "stats/submit")
- #config.add_view(stats_submit, route_name="stats_submit")
config.add_route("submit_stats", "stats/submit")
config.add_view(submit_stats, route_name="submit_stats")
# PLAYER ROUTES
config.add_route("player_game_index", "/player/{player_id:\d+}/games")
- config.add_route("player_game_index_json", "/player/{player_id:\d+}/games.json")
config.add_view(player_game_index, route_name="player_game_index", renderer="player_game_index.mako")
+
+ config.add_route("player_game_index_json", "/player/{player_id:\d+}/games.json")
config.add_view(player_game_index_json, route_name="player_game_index_json", renderer="jsonp")
+ config.add_route("player_hashkey_info_text", "/player/me")
+ config.add_view(player_hashkey_info_text, route_name="player_hashkey_info_text", renderer="player_hashkey_info_text.mako")
+
config.add_route("player_info", "/player/{id:\d+}")
- config.add_route("player_info_json", "/player/{id:\d+}.json")
config.add_view(player_info, route_name="player_info", renderer="player_info.mako")
- config.add_view(player_info_json, route_name="player_info_json", renderer="jsonp")
-
- config.add_route("player_hashkey_info_text", "/hashkey/{hashkey}")
- config.add_view(player_hashkey_info_text, route_name="player_hashkey_info_text", renderer="player_hashkey_info_text.mako")
- #config.add_route("player_hashkey_info_json", "/hashkey/{hashkey}.json")
- #config.add_view(player_hashkey_info_json, route_name="player_hashkey_info_json", renderer="jsonp")
+ config.add_route("player_info_json", "/player/{id:\d+}.json")
+ config.add_view(player_info_json, route_name="player_info_json", renderer="jsonp")
config.add_route("player_elo_info_json", "/elo/{hashkey}")
config.add_view(player_elo_info_json, route_name="player_elo_info_json", renderer="jsonp")
config.add_route("player_accuracy", "/player/{id:\d+}/accuracy")
- #config.add_route("player_accuracy_json", "/player/{id:\d+}/accuracy.json")
config.add_view(player_accuracy_json, route_name="player_accuracy", renderer="jsonp")
- #config.add_view(player_accuracy_json, route_name="player_accuracy_json", renderer="jsonp")
config.add_route("player_index", "/players")
- config.add_route("player_index_json", "/players.json")
config.add_view(player_index, route_name="player_index", renderer="player_index.mako")
+
+ config.add_route("player_index_json", "/players.json")
config.add_view(player_index_json, route_name="player_index_json", renderer="jsonp")
config.add_route("player_damage", "/player/{id:\d+}/damage")
config.add_view(player_damage_json, route_name="player_damage", renderer="jsonp")
config.add_route("player_captimes", "/player/{id:\d+}/captimes")
- config.add_route("player_captimes_json", "/player/{id:\d+}/captimes.json")
config.add_view(player_captimes, route_name="player_captimes", renderer="player_captimes.mako")
+
+ config.add_route("player_captimes_json", "/player/{id:\d+}/captimes.json")
config.add_view(player_captimes_json, route_name="player_captimes_json", renderer="jsonp")
# GAME ROUTES
- # config.add_route("game_index", "/games")
- # config.add_route("game_index_json", "/games.json")
- # config.add_view(game_index, route_name="game_index", renderer="game_index.mako")
- # config.add_view(game_index_json, route_name="game_index_json", renderer="jsonp")
-
config.add_route("game_info", "/game/{id:\d+}")
- config.add_route("game_info_json", "/game/{id:\d+}.json")
config.add_view(game_info, route_name="game_info", renderer="game_info.mako")
+
+ config.add_route("game_info_json", "/game/{id:\d+}.json")
config.add_view(game_info_json, route_name="game_info_json", renderer="jsonp")
- config.add_route("rank_index", "/ranks/{game_type_cd:ctf|dm|tdm|duel}")
- config.add_route("rank_index_json", "/ranks/{game_type_cd:ctf|dm|tdm|duel}.json")
+ config.add_route("rank_index", "/ranks/{game_type_cd:ctf|dm|tdm|duel|ca|ft}")
config.add_view(rank_index, route_name="rank_index", renderer="rank_index.mako")
+
+ config.add_route("rank_index_json", "/ranks/{game_type_cd:ctf|dm|tdm|duel|ca|ft}.json")
config.add_view(rank_index_json, route_name="rank_index_json", renderer="jsonp")
config.add_route("game_index", "/games")
# SERVER ROUTES
config.add_route("server_index", "/servers")
- config.add_route("server_index_json", "/servers.json")
config.add_view(server_index, route_name="server_index", renderer="server_index.mako")
+
+ config.add_route("server_index_json", "/servers.json")
config.add_view(server_index_json, route_name="server_index_json", renderer="jsonp")
config.add_route("server_game_index", "/server/{server_id:\d+}/games/page/{page:\d+}")
- config.add_route("server_game_index_json", "/server/{server_id:\d+}/games.json")
config.add_view(server_game_index, route_name="server_game_index", renderer="server_game_index.mako")
+
+ config.add_route("server_game_index_json", "/server/{server_id:\d+}/games.json")
config.add_view(server_game_index_json, route_name="server_game_index_json", renderer="jsonp")
config.add_route("server_info", "/server/{id:\d+}")
- config.add_route("server_info_json", "/server/{id:\d+}.json")
config.add_view(server_info, route_name="server_info", renderer="server_info.mako")
+
+ config.add_route("server_info_json", "/server/{id:\d+}.json")
config.add_view(server_info_json, route_name="server_info_json", renderer="jsonp")
# MAP ROUTES
config.add_route("map_index", "/maps")
- config.add_route("map_index_json", "/maps.json")
config.add_view(map_index, route_name="map_index", renderer="map_index.mako")
+
+ config.add_route("map_index_json", "/maps.json")
config.add_view(map_index_json, route_name="map_index_json", renderer="jsonp")
config.add_route("map_info", "/map/{id:\d+}")
- config.add_route("map_info_json", "/map/{id:\d+}.json")
config.add_view(map_info, route_name="map_info", renderer="map_info.mako")
+
+ config.add_route("map_info_json", "/map/{id:\d+}.json")
config.add_view(map_info_json, route_name="map_info_json", renderer="jsonp")
config.add_route("map_captimes", "/map/{id:\d+}/captimes")
- config.add_route("map_captimes_json", "/map/{id:\d+}/captimes.json")
config.add_view(map_captimes, route_name="map_captimes", renderer="map_captimes.mako")
+
+ config.add_route("map_captimes_json", "/map/{id:\d+}/captimes.json")
config.add_view(map_captimes_json, route_name="map_captimes_json", renderer="jsonp")
# SEARCH ROUTES
config.add_route("search", "search")
- config.add_route("search_json", "search.json")
config.add_view(search, route_name="search", renderer="search.mako")
+
+ config.add_route("search_json", "search.json")
config.add_view(search_json, route_name="search_json", renderer="jsonp")
return config.make_wsgi_app()
try:
txt = "%.2f%%" % round(win_pct, 2)
except:
- win_pct = 0
+ win_pct = 0.
if self.winp_pos:
- if win_pct >= 0.5:
- nr = 2*(win_pct-0.5)
+ if win_pct >= 50.0:
+ nr = 2*(win_pct/100-0.5)
r = nr*self.winp_colortop[0] + (1-nr)*self.winp_colormid[0]
g = nr*self.winp_colortop[1] + (1-nr)*self.winp_colormid[1]
b = nr*self.winp_colortop[2] + (1-nr)*self.winp_colormid[2]
else:
- nr = 2*win_pct
+ nr = 2*(win_pct/100)
r = nr*self.winp_colormid[0] + (1-nr)*self.winp_colorbot[0]
g = nr*self.winp_colormid[1] + (1-nr)*self.winp_colorbot[1]
b = nr*self.winp_colormid[2] + (1-nr)*self.winp_colorbot[2]
return "<SummaryStat(total_players=%s, total_games=%s, total_servers=%s)>" % (self.total_players, self.total_games, self.total_servers)
+class TeamGameStat(object):
+ def __init__(self, team_game_stat_id=None, create_dt=None):
+ self.team_game_stat_id = team_game_stat_id
+ self.create_dt = create_dt
+
+ def __repr__(self):
+ return "<TeamGameStat(%s, %s, %s)>" % (self.team_game_stat_id, self.game_id, self.team)
+
+ def to_dict(self):
+ return {
+ 'team_game_stat_id':self.team_game_stat_id,
+ 'game_id':self.game_id,
+ 'team':self.team,
+ 'score':self.score,
+ 'rounds':self.rounds,
+ 'caps':self.caps,
+ 'create_dt':self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ }
+
+ def team_html_color(self):
+ if self.team == 5:
+ return "red"
+ if self.team == 14:
+ return "blue"
+ if self.team == 13:
+ return "yellow"
+ if self.team == 10:
+ return "pink"
+
+
def initialize_db(engine=None):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
- MetaData = sqlalchemy.MetaData(bind=engine, reflect=True)
+ MetaData = sqlalchemy.MetaData(bind=engine)
+ MetaData.reflect()
# assign all those tables to an object
achievements_table = MetaData.tables['achievements']
player_ranks_table = MetaData.tables['player_ranks']
player_captimes_table = MetaData.tables['player_map_captimes']
summary_stats_table = MetaData.tables['summary_stats']
+ team_game_stats_table = MetaData.tables['team_game_stats']
# now map the tables and the objects together
mapper(PlayerAchievement, achievements_table)
mapper(PlayerRank, player_ranks_table)
mapper(PlayerCaptime, player_captimes_table)
mapper(SummaryStat, summary_stats_table)
+ mapper(TeamGameStat, team_game_stats_table)
/* Game scoreboard */
.game {
float: left;
- margin-bottom: 30px;
min-width: 700px;
padding: 10px 7px;
}
background-color: #222;
}
-.teamscores td {
- background-color: #000;
- text-align: center;
- padding: 4px;
- font-size: 18px;
+.teamscore {
+ text-align: right;
+ text-shadow: -1px -1px 0 #222;
+ font-size: 20px;
+ font-weight: bold;
+ padding: 10px 7px;
+ margin-right: 5px;
+ margin-top: 12px;
}
-.teamscores td.red {
- background-color: #4d0000;
+.teamscore .teamname {
+ font-size: 12px;
}
-.teamscores td.blue {
- background-color: #00004d;
+.teamscore .red {
+ color: #ad0000;
}
-.teamscores td.yellow {
- background-color: #4d4d00;
+.teamscore .blue {
+ color: #0000ad;
}
-.teamscores td.pink {
- background-color: #4d004d;
+.teamscore .yellow {
+ color: #adad00;
+}
+.teamscore .pink {
+ color: #ad00ad;
}
+.player-score { color: #FEFF3A; }
+.ping { width: 50px; }
/* accuracy and weapon graphs */
.weapon-nav {
.tabbable p { font-size: 14px; }
.tabs-below .nav-tabs > li > a { border-radius: 4px 4px 4px 4px; }
.nav-tabs > .active > a, .nav-tabs > .active > a:hover { background-color: #111; color: #aaa; border-color: #222; }
+.nav-tabs > .active > a, .nav-tabs > .active > a:focus { background-color: #111; color: #aaa; border-color: #222; }
.nav-tabs > li > a { border-radius: 4px 4px 4px 4px; text-align: center; }
.nav-tabs > li > a:hover { background-color: #111; border-color: #333; }
.nav-tabs { border-bottom: 0px solid #000; }
float: left;
}
-/* elo colors */
-.eloup { color: green; }
-.elodown { color: rgb(190,0,0); }
-.eloneutral { color: gray; }
-/* limit player nick lengths */
+/* Gametype filters */
+.btn-toolbar .nav > li a {
+ width: 80px;
+}
+
+.btn.dropdown-toggle, .btn.dropdown-toggle:active {
+ background: none;
+ border: 1px solid transparent;
+ border-radius:4px 4px 4px 4px;
+ line-height:20px;
+ color: #428bca;
+ padding: 10px 0px 10px 0px;
+ font-size: 14px;
+ outline: 0;
+}
+.btn.dropdown-toggle:hover, .btn.dropdown-toggle:focus {
+ background-color:#111;
+ color:#2a6496;
+ border-color:#333;
+}
+
+.btn.dropdown-toggle > .caret {
+ height: 21px;
+ border-top-color: #428bca;
+ border-top-width: 8px;
+ border-left-width: 8px;
+ border-right-width: 8px;
+}
+
+.dropdown-menu {
+ width: 100px;
+}
+
+.dropdown-menu.nav-tabs {
+ padding:4px;
+ background-color: #111;
+ border: 1px solid #333;
+ width: 256px; }
+.dropdown-menu.nav-tabs > li > a {
+ width:80px;
+ color:inherit;
+}
+.dropdown-menu.nav-tabs > li > a:hover {
+ color:#222;
+}
+
+/* Player nicknames */
.nostretch {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+.player-nick {
+ width: 25%;
+}
+
+/* elo colors */
+.eloup { color: green; }
+.elodown { color: rgb(190,0,0); }
+.eloneutral { color: gray; }
+
/* Navigation links */
.pagination > li > a, .pagination > li > span {
background-color: #111111;
-@font-face{font-family:'XoloniumNormal';src:url('fonts/xolonium-webfont.eot');src:url('fonts/xolonium-webfont.eot?#iefix') format('embedded-opentype'),url('fonts/xolonium-webfont.woff') format('woff'),url('fonts/xolonium-webfont.ttf') format('truetype'),url('fonts/xolonium-webfont.svg#XoloniumNormal') format('svg');font-weight:normal;font-style:normal}body{background:url("img/web_background_4.jpg") no-repeat fixed center center / cover black;background-color:black;color:#d0d0d0;font-family:"XoloniumNormal","Helvetica Neue",Helvetica,Arial,sans-serif}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999}h1{font-size:30px;line-height:36px}h1 small{font-size:18px}h2{font-size:24px;line-height:36px}h2 small{font-size:18px}h3{line-height:27px;font-size:18px}h3 small{font-size:14px}h4,h5,h6{line-height:18px}h4{font-size:14px}h4 small{font-size:12px}h5{font-size:12px}h6{font-size:11px;color:#999;text-transform:uppercase}table{background:#000;background:none repeat scroll 0 0 rgba(0,0,0,0.7);border:1px solid #436688}table th{border:1px solid #436688;background-color:#001021}table td{border:1px solid #436688;font-size:10px}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#222}.table th,.table td{border:1px solid #436688}.table td{vertical-align:middle}.table .tdcenter{text-align:center}.accordion-group{border:1px solid #272525}.accordion-inner{border:0}#statline{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;position:relative;top:-25px}#xonborder{background:#000;background:none repeat scroll 0 0 rgba(0,0,0,0.5);border-radius:15px 15px 15px 15px;margin-bottom:30px;margin-left:0;padding:20px}#title{color:#08c;font-size:30px;margin-bottom:15px;position:relative;text-align:center;text-shadow:2px 2px 3px #333}.indexform{margin:20px 0 20px 0}.indexbox{width:250px}.navbar-brand{margin-left:0;padding-bottom:0;padding-top:10px;text-align:left}.navbar-inverse{background:none repeat scroll 0 0 rgba(0,0,0,0.6)}.navbar-inverse .nav>.active>a,.navbar-inverse .nav>.active>a:hover,.navbar-inverse .nav>.active>a:focus{background:none repeat scroll 0 0 rgba(49,49,49,0.6)}.navbar-inverse .nav>li>a,.navbar-brand{font-family:XoloniumNormal}.search,input[type="search"]{background-color:#606060;border:1px solid #202020;color:#aaa;width:100px}.game{float:left;margin-bottom:30px;min-width:700px;padding:10px 7px}.game a{color:#CCC}.game a:hover{color:#d95f00;text-decoration:none}.game tr{background-color:#000}.game tr.red{background-color:#4d0000}.game tr.blue{background-color:#00004d}.game tr.yellow{background-color:#4d4d00}.game tr.pink{background-color:#4d004d}.game tr:hover{background-color:#222}.teamscores td{background-color:#000;text-align:center;padding:4px;font-size:18px}.teamscores td.red{background-color:#4d0000}.teamscores td.blue{background-color:#00004d}.teamscores td.yellow{background-color:#4d4d00}.teamscores td.pink{background-color:#4d004d}.weapon-nav{height:70px;margin-bottom:20px}.weapon-nav ul{display:block;list-style:none outside none}.weapon-nav li{cursor:pointer;float:left;margin-right:10px}.weapon-nav li:hover{border-bottom:2px solid #001021}.weapon-nav .weapon-active{border-bottom:2px solid #436688}.weapon-nav p{text-align:center}.flot table,.flot td{background-color:black;border:0}#gbtabcontainer{margin-top:10px}#gbtab{font-size:12px}.tabbable p{font-size:14px}.tabs-below .nav-tabs>li>a{border-radius:4px 4px 4px 4px}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{background-color:#111;color:#aaa;border-color:#222}.nav-tabs>li>a{border-radius:4px 4px 4px 4px;text-align:center}.nav-tabs>li>a:hover{background-color:#111;border-color:#333}.nav-tabs{border-bottom:0 solid #000}.table .tdcenter{text-align:center}.game-detail img{float:left;margin-right:10px;margin-bottom:5px}.game img{float:left;margin-right:5px;margin-bottom:5px}.game-detail p,.game h4{float:left}.eloup{color:green}.elodown{color:#be0000}.eloneutral{color:gray}.nostretch{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pagination>li>a,.pagination>li>span{background-color:#111;border-color:#313131;color:#797979}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>.active>a,.pagination>.active>span{background-color:#2b2222}@media(min-width:768px){.navbar-form{float:right}}
-
-.scoreboard-entry { width: 120px; max-width: 120px; overflow: hidden; }
-
-td.teamscore { font-size: 16px; text-align: center; }
+@font-face{font-family:'XoloniumNormal';src:url('fonts/xolonium-webfont.eot');src:url('fonts/xolonium-webfont.eot?#iefix') format('embedded-opentype'),url('fonts/xolonium-webfont.woff') format('woff'),url('fonts/xolonium-webfont.ttf') format('truetype'),url('fonts/xolonium-webfont.svg#XoloniumNormal') format('svg');font-weight:normal;font-style:normal}body{background:url("img/web_background_4.jpg") no-repeat fixed center center / cover black;background-color:black;color:#d0d0d0;font-family:"XoloniumNormal","Helvetica Neue",Helvetica,Arial,sans-serif}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999}h1{font-size:30px;line-height:36px}h1 small{font-size:18px}h2{font-size:24px;line-height:36px}h2 small{font-size:18px}h3{line-height:27px;font-size:18px}h3 small{font-size:14px}h4,h5,h6{line-height:18px}h4{font-size:14px}h4 small{font-size:12px}h5{font-size:12px}h6{font-size:11px;color:#999;text-transform:uppercase}table{background:#000;background:none repeat scroll 0 0 rgba(0,0,0,0.7);border:1px solid #436688}table th{border:1px solid #436688;background-color:#001021}table td{border:1px solid #436688;font-size:10px}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#222}.table th,.table td{border:1px solid #436688}.table td{vertical-align:middle}.table .tdcenter{text-align:center}.accordion-group{border:1px solid #272525}.accordion-inner{border:0}#statline{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;position:relative;top:-25px}#xonborder{background:#000;background:none repeat scroll 0 0 rgba(0,0,0,0.5);border-radius:15px 15px 15px 15px;margin-bottom:30px;margin-left:0;padding:20px}#title{color:#08c;font-size:30px;margin-bottom:15px;position:relative;text-align:center;text-shadow:2px 2px 3px #333}.indexform{margin:20px 0 20px 0}.indexbox{width:250px}.navbar-brand{margin-left:0;padding-bottom:0;padding-top:10px;text-align:left}.navbar-inverse{background:none repeat scroll 0 0 rgba(0,0,0,0.6)}.navbar-inverse .nav>.active>a,.navbar-inverse .nav>.active>a:hover,.navbar-inverse .nav>.active>a:focus{background:none repeat scroll 0 0 rgba(49,49,49,0.6)}.navbar-inverse .nav>li>a,.navbar-brand{font-family:XoloniumNormal}.search,input[type="search"]{background-color:#606060;border:1px solid #202020;color:#aaa;width:100px}.game{float:left;min-width:700px;padding:10px 7px}.game a{color:#CCC}.game a:hover{color:#d95f00;text-decoration:none}.game tr{background-color:#000}.game tr.red{background-color:#4d0000}.game tr.blue{background-color:#00004d}.game tr.yellow{background-color:#4d4d00}.game tr.pink{background-color:#4d004d}.game tr:hover{background-color:#222}.teamscore{text-align:right;text-shadow:-1px -1px 0 #222;font-size:20px;font-weight:bold;padding:10px 7px;margin-right:5px;margin-top:12px}.teamscore .teamname{font-size:12px}.teamscore .red{color:#ad0000}.teamscore .blue{color:#0000ad}.teamscore .yellow{color:#adad00}.teamscore .pink{color:#ad00ad}.player-score{color:#feff3a}.ping{width:50px}.weapon-nav{height:70px;margin-bottom:20px}.weapon-nav ul{display:block;list-style:none outside none}.weapon-nav li{cursor:pointer;float:left;margin-right:10px}.weapon-nav li:hover{border-bottom:2px solid #001021}.weapon-nav .weapon-active{border-bottom:2px solid #436688}.weapon-nav p{text-align:center}.flot table,.flot td{background-color:black;border:0}#gbtabcontainer{margin-top:10px}#gbtab{font-size:12px}.tabbable p{font-size:14px}.tabs-below .nav-tabs>li>a{border-radius:4px 4px 4px 4px}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{background-color:#111;color:#aaa;border-color:#222}.nav-tabs>.active>a,.nav-tabs>.active>a:focus{background-color:#111;color:#aaa;border-color:#222}.nav-tabs>li>a{border-radius:4px 4px 4px 4px;text-align:center}.nav-tabs>li>a:hover{background-color:#111;border-color:#333}.nav-tabs{border-bottom:0 solid #000}.table .tdcenter{text-align:center}.game-detail img{float:left;margin-right:10px;margin-bottom:5px}.game img{float:left;margin-right:5px;margin-bottom:5px}.game-detail p,.game h4{float:left}.btn-toolbar .nav>li a{width:80px}.btn.dropdown-toggle,.btn.dropdown-toggle:active{background:0;border:1px solid transparent;border-radius:4px 4px 4px 4px;line-height:20px;color:#428bca;padding:10px 0 10px 0;font-size:14px;outline:0}.btn.dropdown-toggle:hover,.btn.dropdown-toggle:focus{background-color:#111;color:#2a6496;border-color:#333}.btn.dropdown-toggle>.caret{height:21px;border-top-color:#428bca;border-top-width:8px;border-left-width:8px;border-right-width:8px}.dropdown-menu{width:100px}.dropdown-menu.nav-tabs{padding:4px;background-color:#111;border:1px solid #333;width:256px}.dropdown-menu.nav-tabs>li>a{width:80px;color:inherit}.dropdown-menu.nav-tabs>li>a:hover{color:#222}.nostretch{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.player-nick{width:25%}.eloup{color:green}.elodown{color:#be0000}.eloneutral{color:gray}.pagination>li>a,.pagination>li>span{background-color:#111;border-color:#313131;color:#797979}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>.active>a,.pagination>.active>span{background-color:#2b2222}@media(min-width:768px){.navbar-form{float:right}}
--- /dev/null
+<%inherit file="base.mako"/>
+<%namespace name="nav" file="nav.mako" />
+
+<%block name="navigation">
+${nav.nav('games')}
+</%block>
+
+<h1>HTTP 404</h1>
+
+% if 0 <= rand < 30:
+<h2>Well this is <i>awkward</i>...I couldn't find what you were looking for!</h2>
+<p>(better get back to playing then, <b>hm</b>?)</p>
+% endif
+
+% if 30 <= rand < 40:
+<h2>Whoa there! Looks like you've taken a wrong turn.</h2>
+<p>(no one <i>tries</i> to get on highway 404, <b>no one</b>.)</p>
+% endif
+
+% if 40 <= rand < 50:
+<h2>There's nothing to see here. *waves hand*</h2>
+<p>(did Mirio put you up to this?)</p>
+% endif
+
+% if 50 <= rand < 60:
+<h2>Hey, you! Watch out - kojn's behind you!</h2>
+<p>(he killed this page, apparently)</p>
+% endif
+
+% if 60 <= rand < 70:
+<h2>Samual must have destroyed this page.</h2>
+<p>(it wasn't pulling its own weight anyway)</p>
+% endif
+
+% if 70 <= rand < 80:
+<h2>divVerent has encrypted this page so hard you'll never decipher it.</h2>
+<p>(either that or you've hit the wrong page or something)</p>
+% endif
+
+% if 80 <= rand < 90:
+<h2>merlijn was unhappy with this page, so he removed it from the server.</h2>
+<p>(after yelling at me about it, of course)</p>
+% endif
+
+% if 90 <= rand <= 100:
+<h2>Morphed is modeling this page. It's gonna be awesome.</h2>
+<p>(until then, you'll probably want to get in a game or two)</p>
+% endif
</div>
</div>
<div class="row">
- <div class="span12 tabbable">
+ <div class="span12 btn-toolbar">
<ul class="nav nav-tabs">
- % for gt in ('overall','duel','ctf','dm','tdm','ca','kh','ft','lms','as','dom','nb','cts','rc'):
##% for gt in ('overall','duel','ctf','dm','tdm','ca','kh','ft','lms','as','dom','nb','cts','rc'):
+ ##% for gt in ('overall','duel','ctf','dm','tdm','ca','kh','ft','lms','as','dom','nb','cts','rc'):
+ % for gt in ('overall','duel','ctf','dm','tdm'):
<li>
% if gt == 'overall':
<a href="${request.route_url("game_index")}" alt="${gt}" title="" data-toggle="none">
</a>
</li>
% endfor
+ <li>
+ <div class="btn-group nav">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
+ <span class="caret"> </span><br/>
+ more...
+ </a>
+ <ul class="dropdown-menu nav-tabs">
+ % for gt in ('ca','kh','ft','lms','as','dom','nb','cts','rc'):
+ <li>
+ <a href="${request.route_url("game_index", game_type_cd=gt)}" alt="${gt}" title="" data-toggle="none">
+ <span class="sprite sprite-${gt}"> </span><br/>
+ ${gt}
+ </a>
+ </li>
+ % endfor
+ </ul>
+ </div>
+ </li>
</ul>
</div>
<div class="span12 offset1 tab-content">
% else:
<div class="row">
- <div class="span10 game-detail">
- <h2>Game Detail</h2>
- <img width="48" height="48" src="/static/images/icons/48x48/${game.game_type_cd}.png" alt="${game.game_type_cd}"/>
+ <h2>Game Detail</h2>
+ <div class="span8 game-detail">
+ <img width="64" height="64" src="/static/images/icons/48x48/${game.game_type_cd}.png" alt="${game.game_type_cd}"/>
<p>
Played: <span class="abstime" data-epoch="${game.epoch()}" title="${game.start_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${game.fuzzy_date()}</span><br />
Game Type: ${gametype.descr} (${game.game_type_cd})<br />
</p>
<span class="clear"></span>
</div>
+ % if teamscores:
+ <div class="span3 teamscores">
+ <table class="table table-condensed">
+ <thead>
+ <th>Team</th>
+ <th>Score</th>
+ </thead>
+ <tbody>
+ % for ts in teamscores:
+ <tr class="${ts.team}"><td>${ts.team.capitalize()}</td><td>${ts.score}</td></tr>
+ % endfor
+ </tbody>
+ </table>
+ </div>
+ % endif
</div>
+% if len(tgstats) == len(stats_by_team):
+## if we have teamscores in the db
+% for tgstat in tgstats:
+<div class="row">
+ <div class="span1 teamscore">
+ <div class="teamname ${tgstat.team_html_color()}">
+ ${tgstat.team_html_color().capitalize()}
+ </div>
+ <div class="${tgstat.team_html_color()}">
+ % if game.game_type_cd == 'ctf':
+ ${tgstat.caps}
+ % elif game.game_type_cd == 'ca':
+ ${tgstat.rounds}
+## dom -> ticks, rc -> laps, nb -> goals, as -> objectives
+ % else:
+ ${tgstat.score}
+ % endif
+ </div>
+ </div>
+ <div class="span10 game">
+ ${scoreboard(game.game_type_cd, stats_by_team[tgstat.team], show_elo, show_latency)}
+ </div>
+</div>
+% endfor
+% else:
+% for team in stats_by_team.keys():
<div class="row">
<div class="span12 game">
- <h3>Scoreboard</h3>
- ${scoreboard(game.game_type_cd, pgstats, teams, show_elo, show_latency)}
+ ${scoreboard(game.game_type_cd, stats_by_team[team], show_elo, show_latency)}
</div>
</div>
+% endfor
+% endif
% if len(captimes) > 0:
<div class="row">
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Nick</th>
- <th>Elo</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Nick</th>
+ <th style="width:60px;">Elo</th>
</tr>
</thead>
<tbody>
% for r in rs:
<tr>
<td>${i}</td>
- <td class="player-nick" style="max-width: 16em;"><a href="${request.route_url('player_info', id=r.player_id)}" title="Go to the player info page for this player">${r.nick_html_colors()|n}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('player_info', id=r.player_id)}" title="Go to the player info page for this player">${r.nick_html_colors()|n}</a></td>
<td>${int(round(r.elo))}</td>
</tr>
<% i = i+1 %>
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Nick</th>
- <th class="play-time">Play Time</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Nick</th>
+ <th class="play-time" style="width:90px;">Play Time</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if player_id != '-':
- <td class="player-nick"><a href="${request.route_url('player_info', id=player_id)}" title="Go to the player info page for this player">${nick|n}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('player_info', id=player_id)}" title="Go to the player info page for this player">${nick|n}</a></td>
% else:
- <td>${nick|n}</td>
+ <td class="nostretch" style="max-width:150px;">${nick|n}</td>
% endif
<td class="play-time">${alivetime}</td>
</tr>
% endfor
</tbody>
</table>
- <p class="note">*Most active stats are from the past 7 days</p>
</div> <!-- /span4 -->
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Server</th>
- <th>Games</th>
+ <th style="width:40px;">#</th>
+ <th style="width:180px;">Server</th>
+ <th style="width:60px;">Games</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if server_id != '-':
- <td><a href="${request.route_url('server_info', id=server_id)}" title="Go to the server info page for ${name}">${name}</a></td>
+ <td class="nostretch" style="max-width:180px;"><a href="${request.route_url('server_info', id=server_id)}" title="Go to the server info page for ${name}">${name}</a></td>
% else:
- <td>${name}</td>
+ <td class="nostretch" style="max-width:180px;">${name}</td>
% endif
<td>${count}</td>
</tr>
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Map</th>
- <th>Games</th>
+ <th style="width:40px;">#</th>
+ <th style="width:180px;">Map</th>
+ <th style="width:60px;">Games</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if map_id != '-':
- <td><a href="${request.route_url('map_info', id=map_id)}" title="Go to the map info page for ${name}">${name}</a></td>
+ <td class="nostretch" style="max-width:180px;"><a href="${request.route_url('map_info', id=map_id)}" title="Go to the map info page for ${name}">${name}</a></td>
% else:
- <td>${name}</td>
+ <td class="nostretch" style="max-width:180px;">${name}</td>
% endif
<td>${count}</td>
</tr>
</table>
</div> <!-- /span4 -->
</div> <!-- /row -->
+<row class="span12">
+ <p class="note">*Most active stats are from the past 7 days</p>
+</div>
##### RECENT GAMES #####
<td><a href="${request.route_url('server_info', id=rg.server_id)}" title="Go to the detail page for this server">${rg.server_name}</a></td>
<td><a href="${request.route_url('map_info', id=rg.map_id)}" title="Go to the map detail page for this map">${rg.map_name}</a></td>
<td><span class="abstime" data-epoch="${rg.epoch}" title="${rg.start_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${rg.fuzzy_date}</span></td>
- <td class="player-nick">
+ <td class="nostretch">
% if rg.player_id > 2:
<a href="${request.route_url('player_info', id=rg.player_id)}" title="Go to the player info page for this player">${rg.nick_html_colors|n}</a></td>
% else:
<h2>${map.name}</h2>
<p><a href="${map_url}">Back to map info page</a></p>
- <h3>Fastest flag capture times:</h3>
+ <h3>Fastest Flag Captures:</h3>
<table class="table table-bordered table-condensed">
<thead>
<td class="tdcenter"><a class="btn btn-primary btn-small" href="${request.route_url('game_info', id=ct.game_id)}" title="View detailed information about this game">view</a></td>
<td>${ct.fastest_cap.total_seconds()} seconds</td>
<td class="player-nick">
- % if rg.player_id > 2:
- <a href="${request.route_url('player_info', id=rg.player_id)}" title="Go to the player info page for this player">${rg.nick_html_colors|n}</a>
+ % if ct.player_id > 2:
+ <a href="${request.route_url('player_info', id=ct.player_id)}" title="Go to the player info page for this player">${ct.player_nick_html|n}</a>
% else:
${rg.nick_html_colors|n}
% endif
</div>
</div>
+
<td><a href="${request.route_url("map_info", id=map.map_id)}" title="Go to this map's info page">${map.name}</a></th>
<td><span class="abstime" data-epoch="${map.epoch()}" title="${map.create_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${map.fuzzy_date()}</span></td>
<td class="tdcenter">
- <a href="${request.route_url("game_finder", _query={'map_id':map.map_id})}" title="View recent games on this map">
+ <a href="${request.route_url("game_index", _query={'map_id':map.map_id})}" title="View recent games on this map">
<i class="glyphicon glyphicon-list"></i>
</a>
</td>
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Nick</th>
- <th>Score</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Nick</th>
+ <th style="width:90px;">Score</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if score_player_id != '-':
- <td><a href="${request.route_url('player_info', id=score_player_id)}" title="Go to the player info page for this player">${score_nick|n}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('player_info', id=score_player_id)}" title="Go to the player info page for this player">${score_nick|n}</a></td>
% else:
- <td>${score_nick}</td>
+ <td class="nostretch" style="max-width:150px;">${score_nick}</td>
% endif
<td>${score_value}</td>
</tr>
% endfor
</tbody>
</table>
- <p class="note">*Most active stats are from the past 7 days</p>
</div>
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Nick</th>
- <th>Playing Time</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Nick</th>
+ <th style="width:90px;">Playing Time</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if player_id != '-':
- <td><a href="${request.route_url('player_info', id=player_id)}" title="Go to the player info page for this player">${nick|n}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('player_info', id=player_id)}" title="Go to the player info page for this player">${nick|n}</a></td>
% else:
- <td>${nick}</td>
+ <td class="nostretch" style="max-width:150px;">${nick}</td>
% endif
<td>${alivetime}</td>
</tr>
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Name</th>
- <th>Times Played</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Name</th>
+ <th style="width:90px;"># Games</th>
</tr>
</thead>
<tbody>
% for (server_id, name, times_played) in top_servers:
<tr>
<td>${i}</td>
- <td><a href="${request.route_url('server_info', id=server_id)}" title="Go to the server info page for this server">${name}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('server_info', id=server_id)}" title="Go to the server info page for this server">${name}</a></td>
<td>${times_played}</td>
</tr>
<% i = i+1 %>
</div>
</div> <!-- /row -->
+<div class="row">
+ <div class="span12">
+ <p class="note">*Most active stats are from the past 7 days</p>
+ </div>
+</div>
+
% if len(captimes) > 0:
<div class="row">
<div class="span6">
<td class="tdcenter"><span class="sprite sprite-${rg.game_type_cd}" alt="${rg.game_type_cd}" title="${rg.game_type_descr}"></span></td>
<td><a href="${request.route_url('server_info', id=rg.server_id)}" title="Go to the detail page for this server">${rg.server_name}</a></td>
<td><span class="abstime" data-epoch="${rg.epoch}" title="${rg.start_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${rg.fuzzy_date}</span></td>
- <td>
+ <td class="nostretch">
% if rg.player_id > 2:
<a href="${request.route_url('player_info', id=rg.player_id)}" title="Go to the player info page for this player">${rg.nick_html_colors|n}</a>
% else:
% endfor
</tbody>
</table>
- <p><a href="${request.route_url('game_finder', _query={'map_id':gmap.map_id})}">More...</a></p>
+ <p><a href="${request.route_url('game_index', _query={'map_id':gmap.map_id})}">More...</a></p>
</div>
</div>
% endif
</%block>
% if len(captimes) == 0:
- <h2>Sorry, no caps yet. Get playing!</h2>
+<h2>Sorry, no caps yet. Get playing!</h2>
+<p><a href="${player_url}">Back to player info page</a></p>
% else:
<div class="row">
<div class="span12">
- <h3>Fastest Flag Captures by ${player.nick_html_colors()|n}</h3>
- <p><a href="${player_url}">Back to player info page</a></p>
+ <h3>Fastest Flag Captures by
+ <a href="${request.route_url('player_info', id=player.player_id)}">
+ ${player.nick_html_colors()|n}
+ </a>
+ </h3>
<table class="table table-hover table-condensed">
<thead>
<div class="span12 tabbable">
<ul class="nav nav-tabs">
% for game in games_played:
+ % if not game.game_type_cd in ['cq']:
<li
% if game.game_type_cd == game_type_cd or (game.game_type_cd == 'overall' and game_type_cd is None):
class="active"
% endif
>
-
% if game.game_type_cd == 'overall':
<a href="${request.route_url("player_game_index", player_id=player.player_id)}" alt="${game.game_type_cd}" title="" data-toggle="none">
% else:
${game.game_type_cd} <br />
</a>
</li>
+ % endif
% endfor
</ul>
</div>
e favorite-map ${fav_maps['overall'].map_name} ${fav_maps['overall'].times_played} ${fav_maps['overall'].game_type_cd}
% for game_type_cd in overall_stats.keys():
% if game_type_cd != 'overall':
-{ G ${game_type_cd}
+G ${game_type_cd}
% if game_type_cd in elos.keys():
e elo ${elos[game_type_cd].elo}
% endif
% endif
% endfor
e favorite-map ${fav_maps[game_type_cd].map_name} ${fav_maps[game_type_cd].times_played}
-}
% endif
% endfor
% else:
<div class="row">
<div class="span12">
- <h2>${player.nick_html_colors()|n}</h2>
- <h4><i><span class="abstime" data-epoch="${player.epoch()}" title="${player.create_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">Joined ${player.joined_pretty_date()}</span> (player #${player.player_id})</i></h4>
+ <h2>
+ ${player.nick_html_colors()|n}
+ </h2>
+ <h4>
+ <i><span class="abstime" data-epoch="${player.epoch()}" title="${player.create_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">Joined ${player.joined_pretty_date()}</span> (player #${player.player_id})</i>
+ % if cake_day:
+ <img src="/static/images/icons/24x24/cake.png" title="Happy cake day!" />
+ % endif
+ </h4>
</div>
</div>
<div id="gbtabcontainer" class="tabbable tabs-below">
<div class="tab-content">
% for g in games_played:
+ % if not g.game_type_cd in ['cq']:
<div class="tab-pane fade in
% if g.game_type_cd == 'overall':
active
<p>
% if g.game_type_cd in overall_stats:
Last Played: <small><span class="abstime" data-epoch="${overall_stats[g.game_type_cd].last_played_epoch}" title="${overall_stats[g.game_type_cd].last_played.strftime('%a, %d %b %Y %H:%M:%S UTC')}"> ${overall_stats[g.game_type_cd].last_played_fuzzy} </span> <br /></small>
+ % else:
+ <small><br /></small>
% endif
- Games Played: <small>${g.games} <br /></small>
+ Games Played:
+ % if g.game_type_cd == 'overall':
+ <small><a href="${request.route_url("player_game_index", player_id=player.player_id)}" title="View recent games">
+ % else:
+ <small><a href="${request.route_url("player_game_index", player_id=player.player_id, _query={'type':g.game_type_cd})}" title="View recent ${overall_stats[g.game_type_cd].game_type_descr} games">
+ % endif
+ ${g.games}</a> <br /></small>
Playing Time: <small>${overall_stats[g.game_type_cd].total_playing_time} <br /></small>
% if g.game_type_cd in fav_maps:
- Favorite Map: <small>${fav_maps[g.game_type_cd].map_name} <br /></small>
+ Favorite Map: <small><a href="${request.route_url("map_info", id=fav_maps[g.game_type_cd].map_id)}" title="Go to the detail page for this map">${fav_maps[g.game_type_cd].map_name}</a> <br /></small>
+ % else:
+ <small><br /></small>
% endif
% if g.game_type_cd == 'ctf':
% if overall_stats[g.game_type_cd].total_captures is not None:
- <small><a href="${request.route_url("player_captimes", id=player.player_id)}">Fastest flag captures...</a></small>
+ <small><a href="${request.route_url("player_captimes", id=player.player_id)}">Fastest flag captures...</a> <br /></small>
+ % else:
+ <small><br /></small>
% endif
% else:
<small><br /></small>
% if overall_stats[g.game_type_cd].k_d_ratio is not None:
Kill Ratio: <small>${round(overall_stats[g.game_type_cd].k_d_ratio,2)} (${overall_stats[g.game_type_cd].total_kills} kills, ${overall_stats[g.game_type_cd].total_deaths} deaths) <br /></small>
% endif
+ % else:
+ <small><br /></small>
% endif
% if g.game_type_cd in elos:
% else:
Elo: <small>${round(elos[g.game_type_cd].elo,2)} (${elos[g.game_type_cd].games} games) <br /></small>
% endif
+ % else:
+ <small><br /></small>
% endif
% if g.game_type_cd in ranks:
% if g.game_type_cd == 'overall':
- Best Rank: <small>${ranks[g.game_type_cd].rank} of ${ranks[g.game_type_cd].max_rank} (${ranks[g.game_type_cd].game_type_cd}, percentile: ${round(ranks[g.game_type_cd].percentile,2)})<br /></small>
-
+ Best Rank: <small>${ranks[g.game_type_cd].rank} of ${ranks[g.game_type_cd].max_rank} (${ranks[g.game_type_cd].game_type_cd}, percentile: ${round(ranks[g.game_type_cd].percentile,2)}) <br /></small>
% else:
- Rank: <small>${ranks[g.game_type_cd].rank} of ${ranks[g.game_type_cd].max_rank} (percentile: ${round(ranks[g.game_type_cd].percentile,2)})<br /></small>
+ Rank:
+ <small>
+ <a href="
+ % if ranks[g.game_type_cd].rank % 20 == 0:
+ ${request.route_url('rank_index', game_type_cd=g.game_type_cd, _query={'page':ranks[g.game_type_cd].rank/20})}
+
+ % else:
+ ${request.route_url('rank_index', game_type_cd=g.game_type_cd, _query={'page':ranks[g.game_type_cd].rank/20+1})}
+
+ % endif
+ " title="Player rank page for this player">
+ ${ranks[g.game_type_cd].rank} of ${ranks[g.game_type_cd].max_rank}</a>
+ (percentile: ${round(ranks[g.game_type_cd].percentile,2)})
+ <br />
+ </small>
% endif
+ % else:
+ <small><br /></small>
% endif
% if g.game_type_cd == 'ctf':
- % if overall_stats[g.game_type_cd].cap_ratio is not None:
+ % if overall_stats[g.game_type_cd].cap_ratio is not None:
Cap Ratio: <small>${round(overall_stats[g.game_type_cd].cap_ratio,2)} (${overall_stats[g.game_type_cd].total_captures} captures, ${overall_stats[g.game_type_cd].total_pickups} pickups) <br /></small>
+ % else:
+ <small><br /></small>
% endif
% else:
<small><br /></small>
</p>
</div>
</div>
+ % endif
% endfor
</div>
</div>
<ul id="gbtab" class="nav nav-tabs">
% for g in games_played:
<li>
- <a href="#tab-${g.game_type_cd}" data-toggle="tab" alt="${g.game_type_cd}" title="">
+ <a href="#tab-${g.game_type_cd}" data-toggle="tab" alt="${g.game_type_cd}" title="${overall_stats[g.game_type_cd].game_type_descr}">
<span class="sprite sprite-${g.game_type_cd}"> </span><br />
${g.game_type_cd} <br />
<small>(${g.games})</small>
</tbody>
</table>
% if total_games > 10:
- <p><a href="${request.route_url("player_game_index", player_id=player.player_id, page=1)}" title="Game index for ${player.nick}">More...</a></p>
+ <p><a href="${request.route_url("player_game_index", player_id=player.player_id, page=1)}" title="Game index for ${player.stripped_nick}">More...</a></p>
% endif
</div>
</div>
% else:
<table id="rank-index-table" class="table table-hover table-condensed" border="1">
<tr>
- <th>Rank</th>
- <th>Nick</th>
- <th>Elo</th>
+ <th style="width:40px;">Rank</th>
+ <th style="width:420px;">Nick</th>
+ <th style="width:90px;">Elo</th>
</tr>
<% i = 1 %>
% for rank in ranks:
<tr>
<td>${rank.rank}</td>
- <td><a href="${request.route_url("player_info", id=rank.player_id)}" title="Go to this player's info page">${rank.nick_html_colors()|n}</a></th>
+ <td class="nostretch" style="max-width:420px;"><a href="${request.route_url("player_info", id=rank.player_id)}" title="Go to this player's info page">${rank.nick_html_colors()|n}</a></th>
<td>${int(round(rank.elo))}</th>
</tr>
<% i += 1 %>
-<%def name="scoreboard(game_type_cd, pgstats, teams=None, show_elo=False, show_latency=False)">
-##teams: { scoreboardpos : ( teamname, teamscore, playercount ) }
-% if teamscores:
-<table class="table table-condensed">
-<tbody><tr class="teamscores">
-% for team,score in sorted(teamscores.items(), key=lambda x:x[1], reverse=True):
- <td class="${team} teamscore" width="${100/len(teamscores)}%">${team.capitalize()} Team: ${score}</td>
-% endfor
- </tr></tbody>
-</table>
-% endif
-
-<table class="table table-hover table-condensed">
+<%def name="scoreboard(game_type_cd, pgstats, show_elo=False, show_latency=False)">
+<table class="table table-hover table-condensed">
${scoreboard_header(game_type_cd, pgstats[0])}
<tbody>
% for pgstat in pgstats:
- <tr class="${pgstat.team_html_color()}">
- <td class="nostretch">
- % if pgstat.player_id > 2:
- <a href="${request.route_url("player_info", id=pgstat.player_id)}"
- title="Go to the info page for this player">
- <span class="nick">${pgstat.nick_html_colors()|n}</span>
- </a>
- % else:
- <span class="nick">${pgstat.nick_html_colors()|n}</span>
+ <tr class="${pgstat.team_html_color()}">
+ % if show_latency and pgstat.avg_latency is not None:
+ <td class="tdcenter">
+ ${int(round(pgstat.avg_latency))}
+ </td>
+ % elif show_latency:
+ <td class="tdcenter">-</td>
% endif
- </td>
- % if show_latency and pgstat.avg_latency is not None:
- <td class="scoreboard-entry">
- ${int(round(pgstat.avg_latency))}
- </td>
- % elif show_latency:
- <td class="scoreboard-entry"></td>
- % endif
- ${scoreboard_row(game_type_cd, pgstat)}
- % if game_type_cd != 'cts':
- <td class="scoreboard-entry">${pgstat.score}</td>
- % endif
- % if show_elo:
- % if pgstat.elo_delta is not None:
- <td class="scoreboard-entry">${round(pgstat.elo_delta,2)}</td>
- % else:
- <td class="scoreboard-entry">-</td>
- % endif
- % endif
- ##% if teams:
- ##% if teams.has_key(pgstat.scoreboardpos):
- ##<td class="scoreboard-entry teamscore" rowspan="${teams[pgstat.scoreboardpos].playercount}">${teams[pgstat.scoreboardpos].teamscore}</td>
- ##% endif
- ##% endif
- </tr>
+
+ <td class="player-nick">
+ % if pgstat.player_id > 2:
+ <a href="${request.route_url("player_info", id=pgstat.player_id)}"
+ title="Go to the info page for this player">
+ <span class="nick">${pgstat.nick_html_colors()|n}</span>
+ </a>
+ % else:
+ <span class="nick">${pgstat.nick_html_colors()|n}</span>
+ % endif
+ </td>
+
+ ${scoreboard_row(game_type_cd, pgstat)}
+
+ % if game_type_cd != 'cts':
+ <td class="player-score">${pgstat.score}</td>
+ % endif
+
+ % if show_elo:
+ % if pgstat.elo_delta is not None:
+ <td>${round(pgstat.elo_delta,2)}</td>
+ % else:
+ <td>-</td>
+ % endif
+ % endif
+ </tr>
% endfor
</tbody>
</table>
% if game_type_cd == 'as':
<thead>
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="deaths">Deaths</th>
<th class="suicides">Suicides</th>
<th class="objectives">Objectives</th>
<th class="score">Score</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd in 'ca' 'dm' 'duel' 'rune' 'tdm':
<thead>
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="deaths">Deaths</th>
<th class="suicides">Suicides</th>
<th class="score">Score</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd == 'cq':
<thead>
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="deaths">Deaths</th>
<th class="captured">Captured</th>
<th class="released">Released</th>
<th class="score">Score</th>
- ##% if show_elo:
- ##<th>Elo Change</th>
- ##% endif
+ % if show_elo:
+ <th>Elo Change</th>
+ % endif
</tr>
</thead>
% endif
% if game_type_cd == 'cts':
<thead>
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="fastest">Fastest Time</th>
<th class="deaths">Deaths</th>
</tr>
% if game_type_cd == 'ctf':
<thead class="ctf ${pgstat.team_html_color()}">
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="captures">Captures</th>
<th class="pickups">Pickups</th>
<th class="fck" title="Flag Carrier Kill">FCK</th>
<th class="returns">Returns</th>
<th class="score">Score</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd == 'dom':
<thead class="dom ${pgstat.team_html_color()}">
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="deaths">Deaths</th>
<th class="takes">Takes</th>
<th class="ticks">Ticks</th>
<th class="score">Score</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd in 'ft' 'freezetag':
<thead class="freezetag ${pgstat.team_html_color()}">
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="deaths">Deaths</th>
<th class="revivals">Revivals</th>
<th class="score">Score</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd in 'ka' 'keepaway':
<thead>
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="deaths">Deaths</th>
<th class="pickups">Pickups</th>
<th class="bctime">BC Time</th>
<th class="bckills">BC Kills</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
+ <th class="score">Score</th>
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd == 'kh':
<thead class="kh ${pgstat.team_html_color()}">
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="kills">Kills</th>
<th class="deaths">Deaths</th>
<th class="pickups">Pickups</th>
<th class="destroys">Destroys</th>
<th class="kckills">KC Kills</th>
<th class="score">Score</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd in 'nb' 'nexball':
<thead class="nb ${pgstat.team_html_color()}">
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="goals">Goals</th>
<th class="faults">Faults</th>
<th class="score">Score</th>
- ##% if teams:
- ##<th class="teamscore">Teamscore</th>
- ##% endif
% if show_elo:
<th>Elo Change</th>
% endif
% if game_type_cd == 'rc':
<thead>
<tr>
- <th class="nick">Nick</th>
% if show_latency:
<th class="ping">Ping</th>
% endif
+ <th class="nick">Nick</th>
<th class="laps">Laps</th>
<th class="fastest">Fastest Lap</th>
<th class="time">Time</th>
##### SCOREBOARD ROWS #####
<%def name="scoreboard_row(game_type_cd, pgstat)">
% if game_type_cd == 'as':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.deaths}</td>
-<td class="scoreboard-entry">${pgstat.suicides}</td>
-<td class="scoreboard-entry">${pgstat.collects}</td>
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.deaths}</td>
+ <td>${pgstat.suicides}</td>
+ <td>${pgstat.collects}</td>
% endif
% if game_type_cd in 'ca' 'dm' 'duel' 'rune' 'tdm':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.deaths}</td>
-<td class="scoreboard-entry">${pgstat.suicides}</td>
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.deaths}</td>
+ <td>${pgstat.suicides}</td>
% endif
% if game_type_cd == 'cq':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.deaths}</td>
-<td class="scoreboard-entry">${pgstat.captures}</td>
-<td class="scoreboard-entry">${pgstat.drops}</td>
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.deaths}</td>
+ <td>${pgstat.captures}</td>
+ <td>${pgstat.drops}</td>
% endif
% if game_type_cd == 'cts':
-% if pgstat.fastest is not None:
-<td class="scoreboard-entry">${round(float(pgstat.fastest.seconds) + (pgstat.fastest.microseconds/1000000.0), 2)}</td>
-% else:
-<td class="scoreboard-entry">-</td>
-% endif
-<td class="scoreboard-entry">${pgstat.deaths}</td>
+ % if pgstat.fastest is not None:
+ <td>${round(float(pgstat.fastest.seconds) + (pgstat.fastest.microseconds/1000000.0), 2)}</td>
+ % else:
+ <td>-</td>
+ % endif
+
+ <td>${pgstat.deaths}</td>
% endif
% if game_type_cd == 'ctf':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.captures}</td>
-<td class="scoreboard-entry">${pgstat.pickups}</td>
-<td class="scoreboard-entry">${pgstat.carrier_frags}</td>
-<td class="scoreboard-entry">${pgstat.returns}</td>
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.captures}</td>
+ <td>${pgstat.pickups}</td>
+ <td>${pgstat.carrier_frags}</td>
+ <td>${pgstat.returns}</td>
% endif
% if game_type_cd == 'dom':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.deaths}</td>
-<td class="scoreboard-entry">${pgstat.pickups}</td>
-<td class="scoreboard-entry">${pgstat.drops}</td>
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.deaths}</td>
+ <td>${pgstat.pickups}</td>
+ <td>${pgstat.drops}</td>
% endif
% if game_type_cd in 'ft' 'freezetag':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.deaths}</td>
-<td class="scoreboard-entry">${pgstat.revivals}</td>
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.deaths}</td>
+ <td>${pgstat.revivals}</td>
% endif
% if game_type_cd in 'ka' 'keepaway':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.deaths}</td>
-<td class="scoreboard-entry">${pgstat.pickups}</td>
-
-% if pgstat.time is not None:
-<td class="scoreboard-entry">${round(float(pgstat.time.seconds) + (pgstat.time.microseconds/1000000.0), 2)}</td>
-% else:
-<td class="scoreboard-entry">-</td>
-% endif
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.deaths}</td>
+ <td>${pgstat.pickups}</td>
+
+ % if pgstat.time is not None:
+ <td>${round(float(pgstat.time.seconds) + (pgstat.time.microseconds/1000000.0), 2)}</td>
+ % else:
+ <td>-</td>
+ % endif
-<td class="scoreboard-entry">${pgstat.fckills}</td>
+ <td>${pgstat.carrier_frags}</td>
% endif
% if game_type_cd == 'kh':
-<td class="scoreboard-entry">${pgstat.kills}</td>
-<td class="scoreboard-entry">${pgstat.deaths}</td>
-<td class="scoreboard-entry">${pgstat.pickups}</td>
-<td class="scoreboard-entry">${pgstat.captures}</td>
-<td class="scoreboard-entry">${pgstat.drops}</td>
-<td class="scoreboard-entry">${pgstat.pushes}</td>
-<td class="scoreboard-entry">${pgstat.destroys}</td>
-<td class="scoreboard-entry">${pgstat.carrier_frags}</td>
+ <td>${pgstat.kills}</td>
+ <td>${pgstat.deaths}</td>
+ <td>${pgstat.pickups}</td>
+ <td>${pgstat.captures}</td>
+ <td>${pgstat.drops}</td>
+ <td>${pgstat.pushes}</td>
+ <td>${pgstat.destroys}</td>
+ <td>${pgstat.carrier_frags}</td>
% endif
% if game_type_cd in 'nb' 'nexball':
-<td class="scoreboard-entry">${pgstat.captures}</td>
-<td class="scoreboard-entry">${pgstat.drops}</td>
+ <td>${pgstat.captures}</td>
+ <td>${pgstat.drops}</td>
% endif
% if game_type_cd == 'rc':
-<td class="scoreboard-entry">${pgstat.laps}</td>
+ <td>${pgstat.laps}</td>
-% if pgstat.fastest is not None:
-<td class="scoreboard-entry">${round(float(pgstat.fastest.seconds) + (pgstat.fastest.microseconds/1000000.0), 2)}</td>
-% else:
-<td class="scoreboard-entry">-</td>
-% endif
+ % if pgstat.fastest is not None:
+ <td>${round(float(pgstat.fastest.seconds) + (pgstat.fastest.microseconds/1000000.0), 2)}</td>
+ % else:
+ <td>-</td>
+ % endif
-% if pgstat.time is not None:
-<td class="scoreboard-entry">${round(float(pgstat.time.seconds) + (pgstat.time.microseconds/1000000.0), 2)}</td>
-% else:
-<td class="scoreboard-entry">-</td>
-% endif
+ % if pgstat.time is not None:
+ <td>${round(float(pgstat.time.seconds) + (pgstat.time.microseconds/1000000.0), 2)}</td>
+ % else:
+ <td>-</td>
+ % endif
% endif
</%def>
<td><a href="${request.route_url("server_info", id=server.server_id)}" title="Go to this server's info page">${server.name}</a></th>
<td><span class="abstime" data-epoch="${server.epoch()}" title="${server.create_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${server.fuzzy_date()}</span></td>
<td class="tdcenter">
- <a href="${request.route_url("game_finder", _query={'server_id':server.server_id})}" title="View recent games on this server">
+ <a href="${request.route_url("game_index", _query={'server_id':server.server_id})}" title="View recent games on this server">
<i class="glyphicon glyphicon-list"></i>
</a>
</td>
<td><a href="${request.route_url("map_info", id=map.map_id)}" title="Go to this map's info page">${map.name}</a></th>
<td><span class="abstime" data-epoch="${map.epoch()}" title="${map.create_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${map.fuzzy_date()}</span></td>
<td class="tdcenter">
- <a href="${request.route_url("game_finder", _query={'map_id':map.map_id})}" title="View recent games on this map">
+ <a href="${request.route_url("game_index", _query={'map_id':map.map_id})}" title="View recent games on this map">
<i class="glyphicon glyphicon-list"></i>
</a>
</td>
<td><a href="${request.route_url("server_info", id=server.server_id)}" title="Go to this server's info page">${server.name}</a></th>
<td><span class="abstime" data-epoch="${server.epoch()}" title="${server.create_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${server.fuzzy_date()}</span></td>
<td class="tdcenter">
- <a href="${request.route_url("game_finder", _query={'server_id':server.server_id})}" title="View recent games on this server">
+ <a href="${request.route_url("game_index", _query={'server_id':server.server_id})}" title="View recent games on this server">
<i class="glyphicon glyphicon-list"></i>
</a>
</td>
<div class="span12">
<h2>${server.name}</h2>
<p>
- IP Address: ${server.ip_addr} <br />
+ IP Address:
+ % if server.port is not None:
+ ${server.ip_addr}:${server.port}
+ % else:
+ ${server.ip_addr}
+ % endif
+ <br />
Revision: ${server.revision} <br />
Added <span class="abstime" data-epoch="${server.epoch()}" title="${server.create_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${server.fuzzy_date()}</span> <br />
</p>
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Nick</th>
- <th>Score</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Nick</th>
+ <th style="width:90px;">Score</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if score_player_id != '-':
- <td><a href="${request.route_url('player_info', id=score_player_id)}" title="Go to the player info page for this player">${score_nick|n}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('player_info', id=score_player_id)}" title="Go to the player info page for this player">${score_nick|n}</a></td>
% else:
- <td>${score_nick}</td>
+ <td class="nostretch" style="max-width:150px;">${score_nick}</td>
% endif
<td>${score_value}</td>
</tr>
% endfor
</tbody>
</table>
- <p class="note">*Most active stats are from the past 7 days</p>
</div> <!-- /span4 -->
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Nick</th>
- <th>Playing Time</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Nick</th>
+ <th style="width:90px;">Playing Time</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if player_id != '-':
- <td><a href="${request.route_url('player_info', id=player_id)}" title="Go to the player info page for this player">${nick|n}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('player_info', id=player_id)}" title="Go to the player info page for this player">${nick|n}</a></td>
% else:
- <td>${nick}</td>
+ <td class="nostretch" style="max-width:150px;">${nick}</td>
% endif
<td>${alivetime}</td>
</tr>
<table class="table table-hover table-condensed">
<thead>
<tr>
- <th>#</th>
- <th>Map</th>
- <th># Games</th>
+ <th style="width:40px;">#</th>
+ <th style="width:150px;">Map</th>
+ <th style="width:120px;"># Games</th>
</tr>
</thead>
<tbody>
<tr>
<td>${i}</td>
% if map_id != '-':
- <td><a href="${request.route_url('map_info', id=map_id)}" title="Go to the map info page for ${name}">${name}</a></td>
+ <td class="nostretch" style="max-width:150px;"><a href="${request.route_url('map_info', id=map_id)}" title="Go to the map info page for ${name}">${name}</a></td>
% else:
- <td>${name}</td>
+ <td class="nostretch" style="max-width:150px;">${name}</td>
% endif
<td>${count}</td>
</tr>
</div> <!-- /span4 -->
</div> <!-- /row -->
-
+<div class="row">
+ <div class="span12">
+ <p class="note">*Most active stats are from the past 7 days</p>
+ </div>
+</div>
% if len(recent_games) > 0:
<td class="tdcenter"><span class="sprite sprite-${rg.game_type_cd}" alt="${rg.game_type_cd}" title="${rg.game_type_descr}"></span></td>
<td><a href="${request.route_url('map_info', id=rg.map_id)}" title="Go to the map detail page for this map">${rg.map_name}</a></td>
<td><span class="abstime" data-epoch="${rg.epoch}" title="${rg.start_dt.strftime('%a, %d %b %Y %H:%M:%S UTC')}">${rg.fuzzy_date}</span></td>
- <td>
+ <td class="nostretch">
% if rg.player_id > 2:
<a href="${request.route_url('player_info', id=rg.player_id)}" title="Go to the player info page for this player">${rg.nick_html_colors|n}</a>
% else:
% endfor
</tbody>
</table>
- <p><a href="${request.route_url('game_finder', _query={'server_id':server.server_id})}">More...</a></p>
+ <p><a href="${request.route_url('game_index', _query={'server_id':server.server_id})}">More...</a></p>
</div>
</div>
% endif
+import logging
+import pyramid.httpexceptions
import re
from colorsys import rgb_to_hls, hls_to_rgb
from cgi import escape as html_escape
from datetime import datetime, timedelta
from decimal import Decimal
from collections import namedtuple
+from xonstat.d0_blind_id import d0_blind_id_verify
+
+
+log = logging.getLogger(__name__)
+
# Map of special chars to ascii from Darkplace's console.c.
_qfont_table = [
result[key] = to_json(value.to_dict())
return result
+
+def is_leap_year(today_dt=None):
+ if today_dt is None:
+ today_dt = datetime.utcnow()
+
+ if today_dt.year % 400 == 0:
+ leap_year = True
+ elif today_dt.year % 100 == 0:
+ leap_year = False
+ elif today_dt.year % 4 == 0:
+ leap_year = True
+ else:
+ leap_year = False
+
+ return leap_year
+
+
+def is_cake_day(create_dt, today_dt=None):
+ cake_day = False
+
+ if today_dt is None:
+ today_dt = datetime.utcnow()
+
+ # cakes are given on the first anniversary, not the actual create date!
+ if datetime.date(today_dt) != datetime.date(create_dt):
+ if today_dt.day == create_dt.day and today_dt.month == create_dt.month:
+ cake_day = True
+
+ # leap year people get their cakes on March 1
+ if not is_leap_year(today_dt) and create_dt.month == 2 and create_dt.day == 29:
+ if today_dt.month == 3 and today_dt.day == 1:
+ cake_day = True
+
+ return cake_day
+
+
+def verify_request(request):
+ """Verify requests using the d0_blind_id library"""
+
+ # first determine if we should be verifying or not
+ val_verify_requests = request.registry.settings.get('xonstat.verify_requests', 'true')
+ if val_verify_requests == "true":
+ flg_verify_requests = True
+ else:
+ flg_verify_requests = False
+
+ try:
+ (idfp, status) = d0_blind_id_verify(
+ sig=request.headers['X-D0-Blind-Id-Detached-Signature'],
+ querystring='',
+ postdata=request.body)
+
+ log.debug('\nidfp: {0}\nstatus: {1}'.format(idfp, status))
+ except:
+ idfp = None
+ status = None
+
+ if flg_verify_requests and not idfp:
+ log.debug("ERROR: Unverified request")
+ raise pyramid.httpexceptions.HTTPUnauthorized("Unverified request")
+
+ return (idfp, status)
from xonstat.views.search import search_q, search
from xonstat.views.search import search_json
+from xonstat.views.exceptions import notfound
+
from xonstat.views.main import main_index
--- /dev/null
+import logging
+import random
+
+def notfound(request):
+ return {'rand': int(random.random() * 100)}
import logging
import re
import time
+from collections import OrderedDict
from pyramid.response import Response
from sqlalchemy import desc, func, over
from collections import namedtuple
else:
show_elo = False
- if request.params.has_key('show_latency'):
- show_latency = True
- else:
- show_latency = False
+ show_latency = False
try:
notfound = False
order_by(PlayerGameStat.score).\
all()
+ # if at least one player has a valid latency, we'll show the column
+ for pgstat in pgstats:
+ if pgstat.avg_latency is not None:
+ show_latency = True
+
+ q = DBSession.query(TeamGameStat).\
+ filter(TeamGameStat.game_id == game_id)
+ if game.game_type_cd == 'ctf':
+ q = q.order_by(TeamGameStat.caps.desc())
+ elif game.game_type_cd == 'ca':
+ q = q.order_by(TeamGameStat.rounds.desc())
+ # dom -> ticks, rc -> laps, nb -> goals, as -> objectives
+
+ q = q.order_by(TeamGameStat.score.desc())
+
+ tgstats = q.all()
+
+ stats_by_team = OrderedDict()
+ for pgstat in pgstats:
+ if pgstat.team not in stats_by_team.keys():
+ stats_by_team[pgstat.team] = []
+ stats_by_team[pgstat.team].append(pgstat)
+
captimes = []
if game.game_type_cd == 'ctf':
for pgstat in pgstats:
if pgstat.fastest is not None:
captimes.append(pgstat)
-
captimes = sorted(captimes, key=lambda x:x.fastest)
teamscores = {}
map = None
gametype = None
pgstats = None
+ tgstats = None
pwstats = None
captimes = None
teams = None
show_elo = False
show_latency = False
+ stats_by_team = None
raise inst
return {'game':game,
'map':map,
'gametype':gametype,
'pgstats':pgstats,
+ 'tgstats':tgstats,
'pwstats':pwstats,
'captimes':captimes,
'teams':teams,
'teamscores':teamscores,
'show_elo':show_elo,
'show_latency':show_latency,
+ 'stats_by_team':stats_by_team,
}
filter(PlayerGameStat.player_id==player_id)
else:
recent_games_q = recent_games_q.\
- filter(PlayerGameStat.rank==1).\
+ filter(PlayerGameStat.scoreboardpos==1).\
filter(Game.game_id==pgstat_alias.game_id).\
filter(pgstat_alias.player_id==player_id)
else:
recent_games_q = recent_games_q.\
- filter(PlayerGameStat.rank==1)
+ filter(PlayerGameStat.scoreboardpos==1)
if game_type_cd is not None:
recent_games_q = recent_games_q.\
def map_captimes_data(request):
map_id = int(request.matchdict['id'])
-
+
MapCaptimes = namedtuple('PlayerCaptimes', ['fastest_cap', 'create_dt', 'create_dt_epoch', 'create_dt_fuzzy',
'player_id', 'player_nick', 'player_nick_stripped', 'player_nick_html',
'game_id', 'server_id', 'server_name'])
"AND pgs.player_id = ct.player_id "
"AND pgs.game_id = ct.game_id "
"ORDER BY ct.fastest_cap "
+ "LIMIT 25"
).params(map_id=map_id).all()
mmap = DBSession.query(Map).filter_by(map_id=map_id).one()
from webhelpers.paginate import Page
from xonstat.models import *
from xonstat.util import page_url, to_json, pretty_date, datetime_seconds
+from xonstat.util import is_cake_day, verify_request
from xonstat.views.helpers import RecentGame, recent_games_q
log = logging.getLogger(__name__)
- cap_ratio (ctf only)
- total_carrier_frags (ctf only)
- game_type_cd
+ - game_type_descr
The key to the dictionary is the game type code. There is also an
"overall" game_type_cd which sums the totals and computes the total ratios.
OverallStats = namedtuple('OverallStats', ['total_kills', 'total_deaths',
'k_d_ratio', 'last_played', 'last_played_epoch', 'last_played_fuzzy',
'total_playing_time', 'total_playing_time_secs', 'total_pickups', 'total_captures', 'cap_ratio',
- 'total_carrier_frags', 'game_type_cd'])
+ 'total_carrier_frags', 'game_type_cd', 'game_type_descr'])
- raw_stats = DBSession.query('game_type_cd', 'total_kills',
- 'total_deaths', 'last_played', 'total_playing_time',
+ raw_stats = DBSession.query('game_type_cd', 'game_type_descr',
+ 'total_kills', 'total_deaths', 'last_played', 'total_playing_time',
'total_pickups', 'total_captures', 'total_carrier_frags').\
from_statement(
"SELECT g.game_type_cd, "
+ "gt.descr game_type_descr, "
"Sum(pgs.kills) total_kills, "
"Sum(pgs.deaths) total_deaths, "
"Max(pgs.create_dt) last_played, "
"Sum(pgs.captures) total_captures, "
"Sum(pgs.carrier_frags) total_carrier_frags "
"FROM games g, "
+ "cd_game_type gt, "
"player_game_stats pgs "
"WHERE g.game_id = pgs.game_id "
+ "AND g.game_type_cd = gt.game_type_cd "
"AND pgs.player_id = :player_id "
- "GROUP BY g.game_type_cd "
+ "GROUP BY g.game_type_cd, game_type_descr "
"UNION "
- "SELECT 'overall' game_type_cd, "
+ "SELECT 'overall' game_type_cd, "
+ "'Overall' game_type_descr, "
"Sum(pgs.kills) total_kills, "
"Sum(pgs.deaths) total_deaths, "
"Max(pgs.create_dt) last_played, "
"Sum(pgs.pickups) total_pickups, "
"Sum(pgs.captures) total_captures, "
"Sum(pgs.carrier_frags) total_carrier_frags "
- "FROM games g, "
- "player_game_stats pgs "
- "WHERE g.game_id = pgs.game_id "
- "AND pgs.player_id = :player_id "
+ "FROM player_game_stats pgs "
+ "WHERE pgs.player_id = :player_id "
).params(player_id=player_id).all()
# to be indexed by game_type_cd
total_captures=row.total_captures,
cap_ratio=cap_ratio,
total_carrier_frags=row.total_carrier_frags,
- game_type_cd=row.game_type_cd)
+ game_type_cd=row.game_type_cd,
+ game_type_descr=row.game_type_descr)
overall_stats[row.game_type_cd] = os
total_captures = os.total_captures,
cap_ratio = os.cap_ratio,
total_carrier_frags = os.total_carrier_frags,
- game_type_cd = os.game_type_cd)
+ game_type_cd = os.game_type_cd,
+ game_type_descr = os.game_type_descr)
return overall_stats
Provides a list of recent games for a player. Uses the recent_games_q helper.
"""
# recent games played in descending order
- rgs = recent_games_q(player_id=player_id).limit(10).all()
+ rgs = recent_games_q(player_id=player_id, force_player_id=True).limit(10).all()
recent_games = [RecentGame(row) for row in rgs]
return recent_games
ranks = get_ranks(player_id)
recent_games = get_recent_games(player_id)
recent_weapons = get_recent_weapons(player_id)
+ cake_day = is_cake_day(player.create_dt)
except Exception as e:
player = None
ranks = None
recent_games = None
recent_weapons = []
+ cake_day = False
+ ## do not raise exceptions here (only for debugging)
+ # raise e
return {'player':player,
'games_played':games_played,
'elos':elos,
'ranks':ranks,
'recent_games':recent_games,
- 'recent_weapons':recent_weapons
+ 'recent_weapons':recent_weapons,
+ 'cake_day':cake_day,
}
rgs_q = recent_games_q(player_id=player.player_id,
force_player_id=True, game_type_cd=game_type_cd)
- games = Page(rgs_q, current_page, items_per_page=10, url=page_url)
+ games = Page(rgs_q, current_page, items_per_page=20, url=page_url)
# replace the items in the canned pagination class with more rich ones
games.items = [RecentGame(row) for row in games.items]
def player_hashkey_info_data(request):
- hashkey = request.matchdict['hashkey']
+ (idfp, status) = verify_request(request)
+
+ # if config is to *not* verify requests and we get nothing back, this
+ # query will return nothing and we'll 404.
try:
player = DBSession.query(Player).\
filter(Player.player_id == Hashkey.player_id).\
filter(Player.active_ind == True).\
- filter(Hashkey.hashkey == hashkey).one()
+ filter(Hashkey.hashkey == idfp).one()
games_played = get_games_played(player.player_id)
overall_stats = get_overall_stats(player.player_id)
if player_id <= 2:
player_id = -1;
- #player_captimes = DBSession.query(PlayerCaptime).\
- # filter(PlayerCaptime.player_id==player_id).\
- # order_by(PlayerCaptime.fastest_cap).\
- # all()
-
PlayerCaptimes = namedtuple('PlayerCaptimes', ['fastest_cap', 'create_dt', 'create_dt_epoch', 'create_dt_fuzzy',
'player_id', 'game_id', 'map_id', 'map_name', 'server_id', 'server_name'])
from pyramid.response import Response\r
from sqlalchemy import Sequence\r
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound\r
-from collections import namedtuple\r
-from xonstat.d0_blind_id import d0_blind_id_verify\r
from xonstat.elo import process_elos\r
from xonstat.models import *\r
-from xonstat.util import strip_colors, qfont_decode\r
+from xonstat.util import strip_colors, qfont_decode, verify_request\r
\r
\r
log = logging.getLogger(__name__)\r
players = []\r
teams = []\r
\r
- last_key = None\r
+ # we're not in either stanza to start\r
+ in_P = in_Q = False\r
+\r
for line in body.split('\n'):\r
try:\r
(key, value) = line.strip().split(' ', 1)\r
# Server (S) and Nick (n) fields can have international characters.\r
if key in 'S' 'n':\r
value = unicode(value, 'utf-8')\r
- last_key = key\r
\r
- if key not in 'Q' 'P' 'n' 'e' 't' 'i':\r
+ if key not in 'P' 'Q' 'n' 'e' 't' 'i':\r
game_meta[key] = value\r
- last_key = key\r
-\r
- if key in 'P' 'Q':\r
- # if we were working on a player or team record already,\r
- # append it and work on a new one (only set team info)\r
- if len(events) > 0:\r
- if last_key == 'P':\r
- players.append(events)\r
- elif last_key == 'Q':\r
- teams.append(events)\r
+\r
+ if key == 'Q' or key == 'P':\r
+ #log.debug('Found a {0}'.format(key))\r
+ #log.debug('in_Q: {0}'.format(in_Q))\r
+ #log.debug('in_P: {0}'.format(in_P))\r
+ #log.debug('events: {0}'.format(events))\r
+\r
+ # check where we were before and append events accordingly\r
+ if in_Q and len(events) > 0:\r
+ #log.debug('creating a team (Q) entry')\r
+ teams.append(events)\r
events = {}\r
+ elif in_P and len(events) > 0:\r
+ #log.debug('creating a player (P) entry')\r
+ players.append(events)\r
+ events = {}\r
+\r
+ if key == 'P':\r
+ #log.debug('key == P')\r
+ in_P = True\r
+ in_Q = False\r
+ elif key == 'Q':\r
+ #log.debug('key == Q')\r
+ in_P = False\r
+ in_Q = True\r
\r
events[key] = value\r
- last_key = key\r
\r
if key == 'e':\r
(subkey, subvalue) = value.split(' ', 1)\r
# no key/value pair - move on to the next line\r
pass\r
\r
- # add the last player we were working on\r
- if len(events) > 0:\r
- if last_key == 'P':\r
- players.append(events)\r
- elif last_key == 'Q':\r
- teams.append(events)\r
+ # add the last entity we were working on\r
+ if in_P and len(events) > 0:\r
+ players.append(events)\r
+ elif in_Q and len(events) > 0:\r
+ teams.append(events)\r
\r
return (game_meta, players, teams)\r
\r
return is_supported\r
\r
\r
-def verify_request(request):\r
- """Verify requests using the d0_blind_id library"""\r
-\r
- # first determine if we should be verifying or not\r
- val_verify_requests = request.registry.settings.get('xonstat.verify_requests', 'true')\r
- if val_verify_requests == "true":\r
- flg_verify_requests = True\r
- else:\r
- flg_verify_requests = False\r
-\r
- try:\r
- (idfp, status) = d0_blind_id_verify(\r
- sig=request.headers['X-D0-Blind-Id-Detached-Signature'],\r
- querystring='',\r
- postdata=request.body)\r
-\r
- log.debug('\nidfp: {0}\nstatus: {1}'.format(idfp, status))\r
- except:\r
- idfp = None\r
- status = None\r
-\r
- if flg_verify_requests and not idfp:\r
- log.debug("ERROR: Unverified request")\r
- raise pyramid.httpexceptions.HTTPUnauthorized("Unverified request")\r
-\r
- return (idfp, status)\r
-\r
-\r
def do_precondition_checks(request, game_meta, raw_players):\r
"""Precondition checks for ALL gametypes.\r
These do not require a database connection."""\r
"""\r
Determines if a given set of events correspond with a non-bot\r
"""\r
- if not events['P'].startswith('bot#'):\r
+ if not events['P'].startswith('bot'):\r
return True\r
else:\r
return False\r
return pgstat\r
\r
\r
-def create_game_stat(session, game_meta, game, server, gmap, player, teams, events):\r
+def create_game_stat(session, game_meta, game, server, gmap, player, events):\r
"""Game stats handler for all game types"""\r
\r
game_type_cd = game.game_type_cd\r
update_fastest_cap(session, player.player_id, game.game_id,\r
gmap.map_id, pgstat.fastest)\r
\r
- pgstat.teamscore = teams[pgstat.team].score\r
-\r
# there is no "winning team" field, so we have to derive it\r
if wins and pgstat.team is not None and game.winner is None:\r
game.winner = pgstat.team\r
return pgstat\r
\r
\r
+def create_default_team_stat(session, game_type_cd):\r
+ """Creates a blanked-out teamstat record for the given game type"""\r
+\r
+ # this is what we have to do to get partitioned records in - grab the\r
+ # sequence value first, then insert using the explicit ID (vs autogenerate)\r
+ seq = Sequence('team_game_stats_team_game_stat_id_seq')\r
+ teamstat_id = session.execute(seq)\r
+ teamstat = TeamGameStat(team_game_stat_id=teamstat_id,\r
+ create_dt=datetime.datetime.utcnow())\r
+\r
+ # all team game modes have a score, so we'll zero that out always\r
+ teamstat.score = 0\r
+\r
+ if game_type_cd in 'ca' 'ft' 'lms' 'ka':\r
+ teamstat.rounds = 0\r
+\r
+ if game_type_cd == 'ctf':\r
+ teamstat.caps = 0\r
+\r
+ return teamstat\r
+\r
+\r
+def create_team_stat(session, game, events):\r
+ """Team stats handler for all game types"""\r
+\r
+ try:\r
+ teamstat = create_default_team_stat(session, game.game_type_cd)\r
+ teamstat.game_id = game.game_id\r
+\r
+ # we should have a team ID if we have a 'Q' event\r
+ if re.match(r'^team#\d+$', events.get('Q', '')):\r
+ team = int(events.get('Q').replace('team#', ''))\r
+ teamstat.team = team\r
+\r
+ # gametype-specific stuff is handled here. if passed to us, we store it\r
+ for (key,value) in events.items():\r
+ if key == 'scoreboard-score': teamstat.score = int(round(float(value)))\r
+ if key == 'scoreboard-caps': teamstat.caps = int(value)\r
+ if key == 'scoreboard-rounds': teamstat.rounds = int(value)\r
+\r
+ session.add(teamstat)\r
+ except Exception as e:\r
+ raise e\r
+\r
+ return teamstat\r
+\r
+\r
def create_weapon_stats(session, game_meta, game, player, pgstat, events):\r
"""Weapon stats handler for all game types"""\r
pwstats = []\r
duration = duration,\r
mod = game_meta.get('O', None))\r
\r
- TeamInfo = namedtuple("TeamInfo", ['team','score'])\r
- teams = {}\r
- for events in raw_teams:\r
- team = events['Q']\r
- if team.startswith("team#"):\r
- t = int(team[5:])\r
- for (key,value) in events.items():\r
- if key == 'scoreboard-teamscore':\r
- teams[t] = TeamInfo(team=t, score=int(value))\r
-\r
for events in raw_players:\r
player = get_or_create_player(\r
session = session,\r
nick = events.get('n', None))\r
\r
pgstat = create_game_stat(session, game_meta, game, server,\r
- gmap, player, teams, events)\r
+ gmap, player, events)\r
\r
if should_do_weapon_stats(game_type_cd) and player.player_id > 1:\r
pwstats = create_weapon_stats(session, game_meta, game, player,\r
pgstat, events)\r
\r
+ for events in raw_teams:\r
+ try:\r
+ teamstat = create_team_stat(session, game, events)\r
+ except Exception as e:\r
+ raise e\r
+\r
if should_do_elos(game_type_cd):\r
create_elos(session, game)\r
\r
except Exception as e:\r
if session:\r
session.rollback()\r
- return e\r
+ raise e\r