Add !rolestats command (#344)
This might me a little confusing, because it calculates the number of players that won (similar to !playerstats) instead of the number of times a team won (like !gamestats). This way it gives the proper winrate of a player playing that role, but gives an inflated number of total victories and games in cases where there are multiples of a role in the same game. Maybe it would be better to hide the actual numbers and show only the winrates in order to avoid confusion.
This commit is contained in:
parent
43471dcf03
commit
34da084a94
68
src/db.py
68
src/db.py
@ -463,6 +463,74 @@ def get_game_totals(mode):
|
|||||||
else:
|
else:
|
||||||
return "Total games (\u0002{0}\u0002): {1} | {2}".format(mode, total_games, ", ".join(totals))
|
return "Total games (\u0002{0}\u0002): {1} | {2}".format(mode, total_games, ", ".join(totals))
|
||||||
|
|
||||||
|
def get_role_stats(role, mode=None):
|
||||||
|
conn = _conn()
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
if mode is None:
|
||||||
|
c.execute("""SELECT
|
||||||
|
gpr.role AS role,
|
||||||
|
SUM(gp.team_win) AS team,
|
||||||
|
SUM(gp.indiv_win) AS indiv,
|
||||||
|
SUM(gp.team_win OR gp.indiv_win) AS overall,
|
||||||
|
COUNT(1) AS total
|
||||||
|
FROM game g
|
||||||
|
JOIN game_player gp
|
||||||
|
ON gp.game = g.id
|
||||||
|
JOIN game_player_role gpr
|
||||||
|
ON gpr.game_player = gp.id
|
||||||
|
WHERE role = ?
|
||||||
|
GROUP BY role""", (role,))
|
||||||
|
else:
|
||||||
|
c.execute("""SELECT
|
||||||
|
gpr.role AS role,
|
||||||
|
g.gamemode AS gamemode,
|
||||||
|
SUM(gp.team_win) AS team,
|
||||||
|
SUM(gp.indiv_win) AS indiv,
|
||||||
|
SUM(gp.team_win OR gp.indiv_win) AS overall,
|
||||||
|
COUNT(1) AS total
|
||||||
|
FROM game g
|
||||||
|
JOIN game_player gp
|
||||||
|
ON gp.game = g.id
|
||||||
|
JOIN game_player_role gpr
|
||||||
|
ON gpr.game_player = gp.id
|
||||||
|
WHERE role = ?
|
||||||
|
AND gamemode = ?
|
||||||
|
GROUP BY role, gamemode""", (role, mode))
|
||||||
|
|
||||||
|
row = c.fetchone()
|
||||||
|
if row and row[2] is not None:
|
||||||
|
if mode is None:
|
||||||
|
return ("\u0002{0[0]}\u0002 | Team winners: {0[1]} ({1:.0%}), "
|
||||||
|
"Individual winners: {0[2]} ({2:.0%}), Overall winners: {0[3]} ({3:.0%}), Total games: {0[4]}.").format(row, row[1]/row[4], row[2]/row[4], row[3]/row[4])
|
||||||
|
else:
|
||||||
|
return ("\u0002{0[0]}\u0002 in \u0002{0[1]}\u0002 | Team winners: {0[2]} ({1:.0%}), "
|
||||||
|
"Individual winners: {0[3]} ({2:.0%}), Overall winners: {0[4]} ({3:.0%}), Total games: {0[5]}.").format(row, row[2]/row[5], row[3]/row[5], row[4]/row[5])
|
||||||
|
else:
|
||||||
|
if mode is None:
|
||||||
|
return "No stats for \u0002{0}\u0002.".format(role)
|
||||||
|
else:
|
||||||
|
return "No stats for \u0002{0}\u0002 in \u0002{1}\u0002.".format(role, mode)
|
||||||
|
|
||||||
|
def get_role_totals():
|
||||||
|
conn = _conn()
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute("SELECT COUNT(1) FROM game")
|
||||||
|
total_games = c.fetchone()[0]
|
||||||
|
if not total_games:
|
||||||
|
return "No games played."
|
||||||
|
c.execute("""SELECT
|
||||||
|
gpr.role AS role,
|
||||||
|
COUNT(1) AS count
|
||||||
|
FROM game_player_role gpr
|
||||||
|
GROUP BY role
|
||||||
|
ORDER BY count DESC""")
|
||||||
|
totals = []
|
||||||
|
for row in c:
|
||||||
|
totals.append("\u0002{0}\u0002: {1}".format(*row))
|
||||||
|
return "Total games: {0} | {1}".format(total_games, ", ".join(totals))
|
||||||
|
|
||||||
|
|
||||||
def get_warning_points(acc, hostmask):
|
def get_warning_points(acc, hostmask):
|
||||||
peid, plid = _get_ids(acc, hostmask)
|
peid, plid = _get_ids(acc, hostmask)
|
||||||
conn = _conn()
|
conn = _conn()
|
||||||
|
@ -23,6 +23,7 @@ VOTES_RATE_LIMIT = 60
|
|||||||
ADMINS_RATE_LIMIT = 300
|
ADMINS_RATE_LIMIT = 300
|
||||||
GSTATS_RATE_LIMIT = 0
|
GSTATS_RATE_LIMIT = 0
|
||||||
PSTATS_RATE_LIMIT = 0
|
PSTATS_RATE_LIMIT = 0
|
||||||
|
RSTATS_RATE_LIMIT = 0
|
||||||
TIME_RATE_LIMIT = 10
|
TIME_RATE_LIMIT = 10
|
||||||
START_RATE_LIMIT = 10 # (per-user)
|
START_RATE_LIMIT = 10 # (per-user)
|
||||||
WAIT_RATE_LIMIT = 10 # (per-user)
|
WAIT_RATE_LIMIT = 10 # (per-user)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import itertools
|
import itertools
|
||||||
|
import functools
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -9,7 +10,7 @@ from src.events import Event
|
|||||||
from src.messages import messages
|
from src.messages import messages
|
||||||
|
|
||||||
__all__ = ["pm", "is_fake_nick", "mass_mode", "mass_privmsg", "reply",
|
__all__ = ["pm", "is_fake_nick", "mass_mode", "mass_privmsg", "reply",
|
||||||
"is_user_simple", "is_user_notice", "in_wolflist",
|
"is_user_simple", "is_user_notice", "in_wolflist", "complete_role",
|
||||||
"relay_wolfchat_command", "irc_lower", "irc_equals", "match_hostmask",
|
"relay_wolfchat_command", "irc_lower", "irc_equals", "match_hostmask",
|
||||||
"is_owner", "is_admin", "plural", "singular", "list_players",
|
"is_owner", "is_admin", "plural", "singular", "list_players",
|
||||||
"get_role", "get_roles", "role_order", "break_long_message",
|
"get_role", "get_roles", "role_order", "break_long_message",
|
||||||
@ -375,4 +376,23 @@ def get_victim(cli, nick, victim, in_chan, self_in_list=False, bot_in_list=False
|
|||||||
|
|
||||||
class InvalidModeException(Exception): pass
|
class InvalidModeException(Exception): pass
|
||||||
|
|
||||||
|
def complete_role(var, wrapper, role):
|
||||||
|
if role not in var.ROLE_GUIDE.keys():
|
||||||
|
special_keys = {"lover"}
|
||||||
|
evt = Event("get_role_metadata", {})
|
||||||
|
evt.dispatch(var, "special_keys")
|
||||||
|
special_keys = functools.reduce(lambda x, y: x | y, evt.data.values(), special_keys)
|
||||||
|
if role.lower() in var.ROLE_ALIASES:
|
||||||
|
matches = (var.ROLE_ALIASES[role.lower()],)
|
||||||
|
else:
|
||||||
|
matches = complete_match(role, var.ROLE_GUIDE.keys() | special_keys)
|
||||||
|
if not matches:
|
||||||
|
wrapper.reply(messages["no_such_role"].format(role))
|
||||||
|
return False
|
||||||
|
if len(matches) > 1:
|
||||||
|
wrapper.reply(messages["ambiguous_role"].format(", ".join(matches)))
|
||||||
|
return False
|
||||||
|
return matches[0]
|
||||||
|
return role
|
||||||
|
|
||||||
# vim: set sw=4 expandtab:
|
# vim: set sw=4 expandtab:
|
||||||
|
@ -72,6 +72,7 @@ var.LAST_VOTES = None
|
|||||||
var.LAST_ADMINS = None
|
var.LAST_ADMINS = None
|
||||||
var.LAST_GSTATS = None
|
var.LAST_GSTATS = None
|
||||||
var.LAST_PSTATS = None
|
var.LAST_PSTATS = None
|
||||||
|
var.LAST_RSTATS = None
|
||||||
var.LAST_TIME = None
|
var.LAST_TIME = None
|
||||||
var.LAST_START = {}
|
var.LAST_START = {}
|
||||||
var.LAST_WAIT = {}
|
var.LAST_WAIT = {}
|
||||||
@ -977,6 +978,7 @@ def join_player(var, wrapper, who=None, forced=False, *, sanity=True):
|
|||||||
var.LAST_STATS = None # reset
|
var.LAST_STATS = None # reset
|
||||||
var.LAST_GSTATS = None
|
var.LAST_GSTATS = None
|
||||||
var.LAST_PSTATS = None
|
var.LAST_PSTATS = None
|
||||||
|
var.LAST_RSTATS = None
|
||||||
var.LAST_TIME = None
|
var.LAST_TIME = None
|
||||||
|
|
||||||
with var.WARNING_LOCK:
|
with var.WARNING_LOCK:
|
||||||
@ -5574,6 +5576,46 @@ def my_stats(cli, nick, chan, rest):
|
|||||||
rest = rest.split()
|
rest = rest.split()
|
||||||
player_stats.func(cli, nick, chan, " ".join([nick] + rest))
|
player_stats.func(cli, nick, chan, " ".join([nick] + rest))
|
||||||
|
|
||||||
|
@command("rolestats", "rstats", pm=True)
|
||||||
|
def role_stats(var, wrapper, rest):
|
||||||
|
"""Gets the stats for a given role in a given gamemode or lists role totals across all games if no role is given."""
|
||||||
|
if (wrapper.target != users.Bot and var.LAST_RSTATS and var.RSTATS_RATE_LIMIT and
|
||||||
|
var.LAST_RSTATS + timedelta(seconds=var.RSTATS_RATE_LIMIT) > datetime.now()):
|
||||||
|
wrapper.pm(messages["command_ratelimited"])
|
||||||
|
return
|
||||||
|
|
||||||
|
if wrapper.target != users.Bot:
|
||||||
|
var.LAST_RSTATS = datetime.now()
|
||||||
|
|
||||||
|
if var.PHASE not in ("none", "join") and wrapper.target is not channel.Main:
|
||||||
|
wrapper.pm(messages["stats_wait_for_game_end"])
|
||||||
|
return
|
||||||
|
|
||||||
|
rest = rest.split()
|
||||||
|
if len(rest) == 0:
|
||||||
|
# this is a long message
|
||||||
|
wrapper.pm(db.get_role_totals())
|
||||||
|
elif len(rest) == 1 or (rest[-1] == "all" and rest.pop()):
|
||||||
|
role = complete_role(var, wrapper, " ".join(rest))
|
||||||
|
if role:
|
||||||
|
wrapper.reply(db.get_role_stats(role))
|
||||||
|
else:
|
||||||
|
role = complete_role(var, wrapper, " ".join(rest[:-1]))
|
||||||
|
if not role:
|
||||||
|
return
|
||||||
|
gamemode = rest[-1]
|
||||||
|
if gamemode not in var.GAME_MODES.keys():
|
||||||
|
matches = complete_match(gamemode, var.GAME_MODES.keys())
|
||||||
|
if len(matches) == 1:
|
||||||
|
gamemode = matches[0]
|
||||||
|
if not matches:
|
||||||
|
wrapper.pm(messages["invalid_mode"].format(rest[1]))
|
||||||
|
return
|
||||||
|
if len(matches) > 1:
|
||||||
|
wrapper.pm(messages["ambiguous_mode"].format(rest[1], ", ".join(matches)))
|
||||||
|
return
|
||||||
|
wrapper.reply(db.get_role_stats(role, gamemode))
|
||||||
|
|
||||||
# Called from !game and !join, used to vote for a game mode
|
# Called from !game and !join, used to vote for a game mode
|
||||||
def vote_gamemode(var, wrapper, gamemode, doreply):
|
def vote_gamemode(var, wrapper, gamemode, doreply):
|
||||||
if var.FGAMED:
|
if var.FGAMED:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user