Added prettytable (http://code.google.com/p/prettytable/) as a replacement for utils.format_table and updated almost all places where the old formatter was used. The code becomes much simpler and shorter with prettytable, there are some situations, such as the contrib/menusystem where the old format_table works well for dynamically creating any number of columns and rows on the fly.

This commit is contained in:
Griatch 2013-04-12 22:06:41 +02:00
parent 0fddf433dc
commit 8969017aaa
10 changed files with 1802 additions and 451 deletions

View file

@ -10,7 +10,7 @@ from django.contrib.auth.models import User
from src.players.models import PlayerDB
from src.server.sessionhandler import SESSIONS
from src.server.models import ServerConfig
from src.utils import utils
from src.utils import utils, prettytable
from src.commands.default.muxcommand import MuxCommand
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
@ -106,29 +106,17 @@ IPREGEX = re.compile(r"[0-9*]{1,3}\.[0-9*]{1,3}\.[0-9*]{1,3}\.[0-9*]{1,3}")
def list_bans(banlist):
"""
Helper function to display a list of active bans. Input argument
is the banlist read into the two commands @ban and @undban below.
is the banlist read into the two commands @ban and @unban below.
"""
if not banlist:
return "No active bans were found."
table = [["id"], ["name/ip"], ["date"], ["reason"]]
table[0].extend([str(i+1) for i in range(len(banlist))])
for ban in banlist:
if ban[0]:
table[1].append(ban[0])
else:
table[1].append(ban[1])
table[2].extend([ban[3] for ban in banlist])
table[3].extend([ban[4] for ban in banlist])
ftable = utils.format_table(table, 4)
string = "{wActive bans:{x"
for irow, row in enumerate(ftable):
if irow == 0:
srow = "\n" + "".join(row)
srow = "{w%s{n" % srow.rstrip()
else:
srow = "\n" + "{w%s{n" % row[0] + "".join(row[1:])
string += srow.rstrip()
table = prettytable.PrettyTable(["{wid", "{wname/ip", "{wdate", "{wreason"])
for inum, ban in enumerate(banlist):
table.add_row([str(inum+1),
ban[0] and ban[0] or ban[1],
ban[3], ban[4]])
string = "{wActive bans:{n\n%s" % table
return string
class CmdBan(MuxCommand):

View file

@ -39,6 +39,7 @@ class CharacterCmdSet(CmdSet):
self.add(system.CmdPy())
self.add(system.CmdScripts())
self.add(system.CmdObjects())
self.add(system.CmdPlayers())
self.add(system.CmdService())
self.add(system.CmdAbout())
self.add(system.CmdTime())

View file

@ -11,7 +11,7 @@ from django.conf import settings
from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection
from src.comms import irc, imc2, rss
from src.comms.channelhandler import CHANNELHANDLER
from src.utils import create, utils
from src.utils import create, utils, prettytable
from src.commands.default.muxcommand import MuxCommand, MuxPlayerCommand
# limit symbol import for API
@ -33,12 +33,12 @@ def find_channel(caller, channelname, silent=False, noaliases=False):
if channels:
return channels[0]
if not silent:
self.msg("Channel '%s' not found." % channelname)
caller.msg("Channel '%s' not found." % channelname)
return None
elif len(channels) > 1:
matches = ", ".join(["%s(%s)" % (chan.key, chan.id) for chan in channels])
if not silent:
self.msg("Multiple channels match (be more specific): \n%s" % matches)
caller.msg("Multiple channels match (be more specific): \n%s" % matches)
return None
return channels[0]
@ -238,6 +238,7 @@ class CmdChannels(MuxCommand):
Lists all channels available to you, wether you listen to them or not.
Use 'comlist" to only view your current channel subscriptions.
Use addcom/delcom to join and leave channels
"""
key = "@channels"
aliases = ["@clist", "channels", "comlist", "chanlist", "channellist", "all channels"]
@ -257,46 +258,27 @@ class CmdChannels(MuxCommand):
# all channel we are already subscribed to
subs = [conn.channel for conn in PlayerChannelConnection.objects.get_all_player_connections(caller)]
if self.cmdstring != "comlist":
string = "\nChannels available:"
cols = [[" "], ["Channel"], ["Aliases"], ["Perms"], ["Description"]]
for chan in channels:
if chan in subs:
cols[0].append(">")
else:
cols[0].append(" ")
cols[1].append(chan.key)
cols[2].append(",".join(chan.aliases))
cols[3].append(str(chan.locks))
cols[4].append(chan.desc)
# put into table
for ir, row in enumerate(utils.format_table(cols)):
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
self.msg(string)
string = "\nChannel subscriptions:"
if not subs:
string += "(None)"
else:
nicks = [nick for nick in caller.nicks.get(nick_type="channel")]
cols = [[" "], ["Channel"], ["Aliases"], ["Description"]]
if self.cmdstring == "comlist":
# just display the subscribed channels with no extra info
comtable = prettytable.PrettyTable(["{wchannel","{wmy aliases", "{wdescription"])
for chan in subs:
cols[0].append(" ")
cols[1].append(chan.key)
cols[2].append(",".join([nick.db_nick for nick in nicks
if nick.db_real.lower() == chan.key.lower()] + chan.aliases))
cols[3].append(chan.desc)
# put into table
for ir, row in enumerate(utils.format_table(cols)):
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
self.msg(string)
clower = chan.key.lower()
nicks = [nick for nick in caller.nicks.get(nick_type="channel")]
comtable.add_row(["%s%s" % (chan.key, chan.aliases and "(%s)" % ",".join(chan.aliases) or ""),
"%s".join(nick.db_nick for nick in nicks if nick.db_real.lower()==clower()),
chan.desc])
caller.msg("\n{wChannel subscriptions{n (use {w@channels{n to list all, {waddcom{n/{wdelcom{n to sub/unsub):{n\n%s" % comtable)
else:
# full listing (of channels caller is able to listen to)
comtable = prettytable.PrettyTable(["{wsub","{wchannel","{wmy aliases","{wlocks","{wdescription"])
for chan in channels:
nicks = [nick for nick in caller.nicks.get(nick_type="channel")]
comtable.add_row([chan in subs and "{gYes{n" or "{rNo{n",
"%s%s" % (chan.key, chan.aliases and "(%s)" % ",".join(chan.aliases) or ""),
"%s".join(nick.db_nick for nick in nicks if nick.db_real.lower()==clower()),
chan.locks,
chan.desc])
caller.msg("\n{wAvailable channels{n (use {wcomlist{n,{waddcom{n and {wdelcom{n to manage subscriptions):\n%s" % comtable)
class CmdCdestroy(MuxCommand):
"""
@ -774,17 +756,10 @@ class CmdIRC2Chan(MuxCommand):
# show all connections
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_')
if connections:
cols = [["Evennia channel"], ["IRC channel"]]
table = prettytable.PrettyTable(["Evennia channel", "IRC channel"])
for conn in connections:
cols[0].append(conn.channel.key)
cols[1].append(" ".join(conn.external_config.split('|')))
ftable = utils.format_table(cols)
string = ""
for ir, row in enumerate(ftable):
if ir == 0:
string += "{w%s{n" % "".join(row)
else:
string += "\n" + "".join(row)
table.add_row([conn.channel.key, " ".join(conn.external_config.split('|'))])
string = "{wIRC connections:{n\n%s" % table
self.msg(string)
else:
self.msg("No connections found.")
@ -863,18 +838,10 @@ class CmdIMC2Chan(MuxCommand):
# show all connections
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='imc2_')
if connections:
cols = [["Evennia channel"], ["<->"], ["IMC channel"]]
table = prettytable.PrettyTable(["Evennia channel", "IMC channel"])
for conn in connections:
cols[0].append(conn.channel.key)
cols[1].append("")
cols[2].append(conn.external_config)
ftable = utils.format_table(cols)
string = ""
for ir, row in enumerate(ftable):
if ir == 0:
string += "{w%s{n" % "".join(row)
else:
string += "\n" + "".join(row)
table.add_row([conn.channel.key, conn.external_config])
string = "{wIMC connections:{n\n%s" % table
self.msg(string)
else:
self.msg("No connections found.")
@ -966,20 +933,11 @@ class CmdIMCInfo(MuxCommand):
string = ""
nmuds = 0
for network in networks:
string += "\n {GMuds registered on %s:{n" % network
cols = [["Name"], ["Url"], ["Host"], ["Port"]]
table = prettytable.PrettyTable(["Name", "Url", "Host", "Port"])
for mud in (mud for mud in muds if mud.networkname == network):
nmuds += 1
cols[0].append(mud.name)
cols[1].append(mud.url)
cols[2].append(mud.host)
cols[3].append(mud.port)
ftable = utils.format_table(cols)
for ir, row in enumerate(ftable):
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
table.add_row([mud.name, mud.url, mud.host, mud.port])
string += "\n{wMuds registered on %s:{n\n%s" % (network, table)
string += "\n %i Muds found." % nmuds
self.msg(string)
@ -999,24 +957,13 @@ class CmdIMCInfo(MuxCommand):
channels = IMC2_CHANLIST.get_channel_list()
string = ""
nchans = 0
string += "\n {GChannels on %s:{n" % IMC2_CLIENT.factory.network
cols = [["Full name"], ["Name"], ["Owner"], ["Perm"], ["Policy"]]
for channel in channels:
table = prettytable.PrettyTable(["Full name", "Name", "Owner", "Perm", "Policy"])
for chan in channels:
nchans += 1
cols[0].append(channel.name)
cols[1].append(channel.localname)
cols[2].append(channel.owner)
cols[3].append(channel.level)
cols[4].append(channel.policy)
ftable = utils.format_table(cols)
for ir, row in enumerate(ftable):
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
string += "\n %i Channels found." % nchans
table.add_row([chan.name, chan.localname, chan.owner, chan.level, chan.policy])
string += "\n{wChannels on %s:{n\n%s" % (IMC2_CLIENT.factory.network, table)
string += "\n%i Channels found." % nchans
self.msg(string)
else:
# no valid inputs
string = "Usage: imcinfo|imcchanlist|imclist"
@ -1104,17 +1051,10 @@ class CmdRSS2Chan(MuxCommand):
# show all connections
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='rss_')
if connections:
cols = [["Evennia-channel"], ["RSS-url"]]
table = prettytable.PrettyTable(["Evennia channel", "RSS url"])
for conn in connections:
cols[0].append(conn.channel.key)
cols[1].append(conn.external_config.split('|')[0])
ftable = utils.format_table(cols)
string = ""
for ir, row in enumerate(ftable):
if ir == 0:
string += "{w%s{n" % "".join(row)
else:
string += "\n" + "".join(row)
table.add_row([conn.channel.key, conn.external_config.split('|')[0]])
string = "{wConnections to RSS:{n\n%s" % table
self.msg(string)
else:
self.msg("No connections found.")

View file

@ -2,7 +2,7 @@
General Character commands usually availabe to all characters
"""
from django.conf import settings
from src.utils import utils
from src.utils import utils, prettytable
from src.objects.models import ObjectNick as Nick
from src.commands.default.muxcommand import MuxCommand
@ -124,20 +124,13 @@ class CmdNick(MuxCommand):
caller = self.caller
switches = self.switches
nicks = Nick.objects.filter(db_obj=caller.dbobj).exclude(db_type="channel")
if 'list' in switches:
string = "{wDefined Nicks:{n"
cols = [["Type"],["Nickname"],["Translates-to"] ]
table = prettytable.PrettyTable(["{wNickType", "{wNickname", "{wTranslates-to"])
for nick in nicks:
cols[0].append(nick.db_type)
cols[1].append(nick.db_nick)
cols[2].append(nick.db_real)
for ir, row in enumerate(utils.format_table(cols)):
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
table.add_row([nick.db_type, nick.db_nick, nick.db_real])
string = "{wDefined Nicks:{n\n%s" % table
caller.msg(string)
return
if 'clearall' in switches:
@ -197,19 +190,12 @@ class CmdInventory(MuxCommand):
if not items:
string = "You are not carrying anything."
else:
# format item list into nice collumns
cols = [[],[]]
table = prettytable.PrettyTable(["name", "desc"])
table.header = False
table.border = False
for item in items:
cols[0].append(item.name)
desc = item.db.desc
if not desc:
desc = ""
cols[1].append(utils.crop(str(desc)))
# auto-format the columns to make them evenly wide
ftable = utils.format_table(cols)
string = "You are carrying:"
for row in ftable:
string += "\n " + "{C%s{n - %s" % (row[0], row[1])
table.add_row(["{C%s{n" % item.name, item.db.desc and item.db.desc or ""])
string = "{wYou are carrying:\n%s" % table
self.caller.msg(string)
class CmdGet(MuxCommand):

View file

@ -20,7 +20,7 @@ import time
from django.conf import settings
from src.server.sessionhandler import SESSIONS
from src.commands.default.muxcommand import MuxPlayerCommand
from src.utils import utils, create, search
from src.utils import utils, create, search, prettytable
from settings import MAX_NR_CHARACTERS, MULTISESSION_MODE
# limit symbol import for API
@ -302,27 +302,18 @@ class CmdSessions(MuxPlayerCommand):
def func(self):
"Implement function"
# make sure we work on the player, not on the character
player = self.caller
sessions = player.get_all_sessions()
table = [["sessid"], ['protocol'], ["host"], ["puppet/character"], ["location"]]
table = prettytable.PrettyTable(["{wsessid", "{wprotocol", "{whost", "{wpuppet/character", "{wlocation"])
for sess in sorted(sessions, key=lambda x:x.sessid):
sessid = sess.sessid
char = player.get_puppet(sessid)
table[0].append(str(sess.sessid))
table[1].append(str(sess.protocol_key))
table[2].append(type(sess.address)==tuple and sess.address[0] or sess.address)
table[3].append(char and str(char) or "None")
table[4].append(char and str(char.location) or "N/A")
ftable = utils.format_table(table, 5)
string = "{wYour current session(s):{n"
for ir, row in enumerate(ftable):
if ir == 0:
string += "\n" + "{w%s{n" % ("".join(row))
else:
string += "\n" + "".join(row)
table.add_row([str(sessid), str(sess.protocol_key),
type(sess.address)==tuple and sess.address[0] or sess.address,
char and str(char) or "None",
char and str(char.location) or "N/A"])
string = "{wYour current session(s):{n\n%s" % table
self.msg(string)
class CmdWho(MuxPlayerCommand):
@ -354,52 +345,36 @@ class CmdWho(MuxPlayerCommand):
else:
show_session_data = player.check_permstring("Immortals") or player.check_permstring("Wizards")
if show_session_data:
table = [["Player Name"], ["On for"], ["Idle"], ["Room"], ["Cmds"], ["Host"]]
else:
table = [["Player Name"], ["On for"], ["Idle"]]
for session in session_list:
if not session.logged_in:
continue
delta_cmd = time.time() - session.cmd_last_visible
delta_conn = time.time() - session.conn_time
plr_pobject = session.get_puppet()
if not plr_pobject:
plr_pobject = session.get_player()
show_session_data = False
table = [["Player Name"], ["On for"], ["Idle"]]
if show_session_data:
table[0].append(plr_pobject.name[:25])
table[1].append(utils.time_format(delta_conn, 0))
table[2].append(utils.time_format(delta_cmd, 1))
table[3].append(plr_pobject.location and plr_pobject.location.id or "None")
table[4].append(session.cmd_total)
table[5].append(session.address[0])
else:
table[0].append(plr_pobject.name[:25])
table[1].append(utils.time_format(delta_conn,0))
table[2].append(utils.time_format(delta_cmd,1))
stable = []
for row in table: # prettify values
stable.append([str(val).strip() for val in row])
ftable = utils.format_table(stable, 5)
string = ""
for ir, row in enumerate(ftable):
if ir == 0:
string += "\n" + "{w%s{n" % ("".join(row))
else:
string += "\n" + "".join(row)
nplayers = (SESSIONS.player_count())
if nplayers == 1:
string += '\nOne player logged in.'
if show_session_data:
table = prettytable.PrettyTable(["{wPlayer Name","{wOn for", "{wIdle", "{wRoom", "{wCmds", "{wHost"])
for session in session_list:
if not session.logged_in: continue
delta_cmd = time.time() - session.cmd_last_visible
delta_conn = time.time() - session.conn_time
plr_pobject = session.get_puppet()
plr_pobject = plr_pobject or session.get_player()
table.add_row([utils.crop(plr_pobject.name, width=25),
utils.time_format(delta_conn, 0),
utils.time_format(delta_cmd, 1),
hasattr(plr_pobject, "location") and plr_pobject.location or "None",
session.cmd_total, type(session.address==tuple) and session.address[0] or session.address])
else:
string += '\n%d players logged in.' % nplayers
table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{Idle"])
for session in session_list:
if not session.logged_in: continue
delta_cmd = time.time() - session.cmd_last_visible
delta_conn = time.time() - session.conn_time
plr_pobject = session.get_puppet()
plr_pobject = plr_pobject or session.get_player()
table.add_row([utils.crop(plr_pobject.name, width=25),
utils.time.format(delta_conn, 0),
utils,time_format(delta_cmd, 1)])
string = "{wPlayers:\n%s\n%s logged in." % (table, nplayers==1 and "One player" or nplayer)
self.msg(string)
class CmdEncoding(MuxPlayerCommand):
"""
encoding - set a custom text encoding
@ -546,15 +521,29 @@ class CmdColorTest(MuxPlayerCommand):
locks = "cmd:all()"
help_category = "General"
def table_format(self, table):
"""
Helper method to format the ansi/xterm256 tables.
Takes a table of columns [[val,val,...],[val,val,...],...]
"""
if not table:
return [[]]
extra_space = 1
max_widths = [max([len(str(val)) for val in col]) for col in table]
ftable = []
for irow in range(len(table[0])):
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " * extra_space
for icol, col in enumerate(table)])
return ftable
def func(self):
"Show color tables"
player = self.caller
if not self.args or not self.args in ("ansi", "xterm256"):
self.msg("Usage: @color ansi|xterm256")
return
if self.args == "ansi":
if self.args.startswith("a"):
# show ansi 16-color table
from src.utils import ansi
ap = ansi.ANSI_PARSER
# ansi colors
@ -570,7 +559,9 @@ class CmdColorTest(MuxPlayerCommand):
#print string
self.msg(string)
self.msg("({{X and %%cx are black-on-black\n %%r - return, %%t - tab, %%b - space)")
elif self.args == "xterm256":
elif self.args.startswith("x"):
# show xterm256 table
table = [[],[],[],[],[],[],[],[],[],[],[],[]]
for ir in range(6):
for ig in range(6):
@ -581,11 +572,13 @@ class CmdColorTest(MuxPlayerCommand):
table[6+ir].append("%%cb%i%i%i%%c%i%i%i%s{n" % (ir,ig,ib,
5-ir,5-ig,5-ib,
"{{b%i%i%i" % (ir,ig,ib)))
table = utils.format_table(table)
table = self.table_format(table)
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
for row in table:
string += "\n" + "".join(row)
self.msg(string)
self.msg("(e.g. %%c123 and %%cb123 also work)")
else:
# malformed input
self.msg("Usage: @color ansi|xterm256")

View file

@ -17,7 +17,7 @@ from src.server.sessionhandler import SESSIONS
from src.scripts.models import ScriptDB
from src.objects.models import ObjectDB
from src.players.models import PlayerDB
from src.utils import logger, utils, gametime, create, is_pypy
from src.utils import logger, utils, gametime, create, is_pypy, prettytable
from src.commands.default.muxcommand import MuxCommand
# delayed imports
@ -210,47 +210,20 @@ def format_script_list(scripts):
if not scripts:
return "<No scripts>"
table = [["id"], ["obj"], ["key"], ["intval"], ["next"], ["rept"], ["db"], ["typeclass"], ["desc"]]
table = prettytable.PrettyTable(["{wid","{wobj","{wkey","{wintval","{wnext","{wrept","{wdb"," {wtypeclass","{wdesc"],align='r')
table.align = 'r'
for script in scripts:
table[0].append(script.id)
if not hasattr(script, 'obj') or not script.obj:
table[1].append("<Global>")
else:
table[1].append(script.obj.key)
table[2].append(script.key)
if not hasattr(script, 'interval') or script.interval < 0:
table[3].append("--")
else:
table[3].append("%ss" % script.interval)
next = script.time_until_next_repeat()
if not next:
table[4].append("--")
else:
table[4].append("%ss" % next)
if not hasattr(script, 'repeats') or not script.repeats:
table[5].append("--")
else:
table[5].append("%s" % script.repeats)
if script.persistent:
table[6].append("*")
else:
table[6].append("-")
typeclass_path = script.typeclass_path.rsplit('.', 1)
table[7].append("%s" % typeclass_path[-1])
table[8].append(script.desc)
ftable = utils.format_table(table)
string = ""
for irow, row in enumerate(ftable):
if irow == 0:
srow = "\n" + "".join(row)
srow = "{w%s{n" % srow.rstrip()
else:
srow = "\n" + "{w%s{n" % row[0] + "".join(row[1:])
string += srow.rstrip()
return string.strip()
nextrep = script.time_until_next_repeat()
table.add_row([script.id,
(not hasattr(script, 'obj') or not script.obj) and "<Global>" or script.obj.key,
script.key,
(not hasattr(script, 'interval') or script.interval < 0) and "--" or "%ss" % script.interval,
not nextrep and "--" or "%ss" % nextrep,
(not hasattr(script, 'repeats') or not script.repeats) and "--" or "%i" % script.repeats,
script.persistent and "*" or "-",
script.typeclass_path.rsplit('.', 1)[-1],
script.desc])
return "%s" % table
class CmdScripts(MuxCommand):
@ -352,7 +325,7 @@ class CmdScripts(MuxCommand):
class CmdObjects(MuxCommand):
"""
Give a summary of object types in database
@objects - Give a summary of object types in database
Usage:
@objects [<nr>]
@ -376,54 +349,81 @@ class CmdObjects(MuxCommand):
else:
nlim = 10
string = "\n{wDatabase totals:{n"
nplayers = PlayerDB.objects.count()
nobjs = ObjectDB.objects.count()
base_char_typeclass = settings.BASE_CHARACTER_TYPECLASS
nchars = ObjectDB.objects.filter(db_typeclass_path=base_char_typeclass).count()
nrooms = ObjectDB.objects.filter(db_location__isnull=True).exclude(db_typeclass_path=base_char_typeclass).count()
nexits = ObjectDB.objects.filter(db_location__isnull=False, db_destination__isnull=False).count()
nother = nobjs - nchars - nrooms - nexits
string += "\n{wPlayers:{n %i" % nplayers
string += "\n{wObjects:{n %i" % nobjs
string += "\n{w Characters (BASE_CHARACTER_TYPECLASS):{n %i" % nchars
string += "\n{w Rooms (location==None):{n %i" % nrooms
string += "\n{w Exits (destination!=None):{n %i" % nexits
string += "\n{w Other:{n %i\n" % (nobjs - nchars - nrooms - nexits)
# total object sum table
totaltable = prettytable.PrettyTable(["{wtype","{wcomment","{wcount", "{w%%"])
totaltable.align = 'l'
totaltable.add_row(["Characters", "(BASE_CHARACTER_TYPECLASS)", nchars, "%.2f" % ((float(nchars)/nobjs)*100)])
totaltable.add_row(["Rooms", "(location=None)", nrooms, "%.2f" % ((float(nrooms)/nobjs)*100)])
totaltable.add_row(["Exits", "(destination!=None)", nexits, "%.2f" % ((float(nexits)/nobjs)*100)])
totaltable.add_row(["Other", "", nother, "%.2f" % ((float(nother)/nobjs)*100)])
# typeclass table
typetable = prettytable.PrettyTable(["{wtypeclass","{wcount", "{w%%"])
typetable.align = 'l'
dbtotals = ObjectDB.objects.object_totals()
table = [["Count"], ["Typeclass"]]
for path, count in dbtotals.items():
table[0].append(count)
table[1].append(path)
ftable = utils.format_table(table, 3)
for irow, row in enumerate(ftable):
srow = "\n" + "".join(row)
srow = srow.rstrip()
if irow == 0:
srow = "{w%s{n" % srow
string += srow
typetable.add_row([path, count, "%.2f" % ((float(count)/nobjs)*100)])
string += "\n\n{wLast %s Objects created:{n" % min(nobjs, nlim)
# last N table
objs = ObjectDB.objects.all().order_by("db_date_created")[max(0, nobjs - nlim):]
latesttable = prettytable.PrettyTable(["{wcreated","{wdbref","{wname","{wtypeclass"])
latesttable.align = 'l'
for obj in objs:
latesttable.add_row([utils.datetime_format(obj.date_created), obj.dbref, obj.key, obj.typeclass.path])
table = [["Created"], ["dbref"], ["name"], ["typeclass"]]
for i, obj in enumerate(objs):
table[0].append(utils.datetime_format(obj.date_created))
table[1].append(obj.dbref)
table[2].append(obj.key)
table[3].append(str(obj.typeclass.path))
ftable = utils.format_table(table, 5)
for irow, row in enumerate(ftable):
srow = "\n" + "".join(row)
srow = srow.rstrip()
if irow == 0:
srow = "{w%s{n" % srow
string += srow
string = "\n{wObject subtype totals (out of %i Objects):{n\n%s" % (nobjs, totaltable)
string += "\n{wObject typeclass distribution:{n\n%s" % typetable
string += "\n{wLast %s Objects created:{n\n%s" % (min(nobjs, nlim), latesttable)
caller.msg(string)
class CmdPlayers(MuxCommand):
"""
@players - give a summary of all registed Players
Usage:
@players [nr]
Lists statistics about the Players registered with the game.
It will list the <nr> amount of latest registered players
If not given, <nr> defaults to 10.
"""
key = "@players"
aliases = ["@listplayers"]
locks = "cmd:perm(listplayers) or perm(Admins)"
def func(self):
"List the players"
caller = self.caller
if self.args and self.args.is_digit():
nlim = int(self.args)
else:
nlim = 10
nplayers = PlayerDB.objects.count()
# typeclass table
dbtotals = PlayerDB.objects.object_totals()
typetable = prettytable.PrettyTable(["{wtypeclass", "{wcount", "{w%%"])
typetable.align = 'l'
for path, count in dbtotals.items():
typetable.add_row([path, count, "%.2f" % ((float(count)/nplayers)*100)])
# last N table
plyrs = PlayerDB.objects.all().order_by("db_date_created")[max(0, nplayers - nlim):]
latesttable = prettytable.PrettyTable(["{wcreated", "{wdbref","{wname","{wtypeclass"])
latesttable.align = 'l'
for ply in plyrs:
latesttable.add_row([utils.datetime_format(ply.date_created), ply.dbref, ply.key, ply.typeclass.path])
string = "\n{wPlayer typeclass distribution:{n\n%s" % typetable
string += "\n{wLast %s Players created:{n\n%s" % (min(nplayers, nlim), latesttable)
caller.msg(string)
class CmdService(MuxCommand):
"""
@ -468,18 +468,11 @@ class CmdService(MuxCommand):
if not switches or switches[0] == "list":
# Just display the list of installed services and their
# status, then exit.
string = "-" * 78
string += "\n{wServices{n (use @services/start|stop|delete):"
table = prettytable.PrettyTable(["{wService{n (use @services/start|stop|delete)", "{wstatus"])
table.align = 'l'
for service in service_collection.services:
if service.running:
status = 'Running'
string += '\n * {g%s{n (%s)' % (service.name, status)
else:
status = 'Inactive'
string += '\n {R%s{n (%s)' % (service.name, status)
string += "\n" + "-" * 78
caller.msg(string)
table.add_row([service.name, service.running and "{gRunning" or "{rNot Running"])
caller.msg(str(table))
return
# Get the service to start / stop
@ -581,7 +574,7 @@ class CmdTime(MuxCommand):
Usage:
@time
Server local time.
Server time statistics.
"""
key = "@time"
aliases = "@uptime"
@ -589,30 +582,14 @@ class CmdTime(MuxCommand):
help_category = "System"
def func(self):
"Show times."
table = [["Current server uptime:",
"Total server running time:",
"Total in-game time (realtime x %g):" % (gametime.TIMEFACTOR),
"Server time stamp:"
],
[utils.time_format(time.time() - SESSIONS.server.start_time, 3),
utils.time_format(gametime.runtime(format=False), 2),
utils.time_format(gametime.gametime(format=False), 2),
datetime.datetime.now()
]]
if utils.host_os_is('posix'):
loadavg = os.getloadavg()
table[0].append("Server load (per minute):")
table[1].append("%g" % (loadavg[0]))
stable = []
for col in table:
stable.append([str(val).strip() for val in col])
ftable = utils.format_table(stable, 5)
string = ""
for row in ftable:
string += "\n " + "{w%s{n" % row[0] + "".join(row[1:])
self.caller.msg(string)
"Show server time data in a table."
table = prettytable.PrettyTable(["{wserver time statistic","{wtime"])
table.align = 'l'
table.add_row(["Current server uptime", utils.time_format(time.time() - SESSIONS.server.start_time, 3)])
table.add_row(["Total server running time", utils.time_format(gametime.runtime(format=False), 2)])
table.add_row(["Total in-game time (realtime x %g" % (gametime.TIMEFACTOR), utils.time_format(gametime.gametime(format=False), 2)])
table.add_row(["Server time stamp", datetime.datetime.now()])
self.caller.msg(str(table))
class CmdServerLoad(MuxCommand):
"""
@ -656,92 +633,60 @@ class CmdServerLoad(MuxCommand):
if not utils.host_os_is('posix'):
string = "Process listings are only available under Linux/Unix."
else:
global _resource, _idmapper
if not _resource:
import resource as _resource
if not _idmapper:
from src.utils.idmapper import base as _idmapper
caller.msg(string)
return
import resource
loadavg = os.getloadavg()
psize = _resource.getpagesize()
pid = os.getpid()
rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1024.0
vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1024.0
global _resource, _idmapper
if not _resource:
import resource as _resource
if not _idmapper:
from src.utils.idmapper import base as _idmapper
rusage = resource.getrusage(resource.RUSAGE_SELF)
table = [["Server load (1 min):",
"Process ID:",
"Bytes per page:",
"CPU time used:",
"Resident memory:",
"Virtual memory:",
"Page faults:",
"Disk I/O:",
"Network I/O:",
"Context switching:"
],
["%g" % loadavg[0],
"%10d" % pid,
"%10d " % psize,
"%s (%gs)" % (utils.time_format(rusage.ru_utime), rusage.ru_utime),
#"%10d shared" % rusage.ru_ixrss,
#"%10d pages" % rusage.ru_maxrss,
"%10.2f MB" % rmem,
"%10.2f MB" % vmem,
"%10d hard" % rusage.ru_majflt,
"%10d reads" % rusage.ru_inblock,
"%10d in" % rusage.ru_msgrcv,
"%10d vol" % rusage.ru_nvcsw
],
["", "", "",
"(user: %gs)" % rusage.ru_stime,
"", #"%10d private" % rusage.ru_idrss,
"", #"%10d bytes" % (rusage.ru_maxrss * psize),
"%10d soft" % rusage.ru_minflt,
"%10d writes" % rusage.ru_oublock,
"%10d out" % rusage.ru_msgsnd,
"%10d forced" % rusage.ru_nivcsw
],
["", "", "", "",
"", #"%10d stack" % rusage.ru_isrss,
"",
"%10d swapouts" % rusage.ru_nswap,
"", "",
"%10d sigs" % rusage.ru_nsignals
]
]
stable = []
for col in table:
stable.append([str(val).strip() for val in col])
ftable = utils.format_table(stable, 5)
string = ""
for row in ftable:
string += "\n " + "{w%s{n" % row[0] + "".join(row[1:])
import resource
loadavg = os.getloadavg()
psize = _resource.getpagesize()
pid = os.getpid()
rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1024.0 # resident memory
vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1024.0 # virtual memory
pmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "%mem")).read()) # percent of resident memory to total
rusage = resource.getrusage(resource.RUSAGE_SELF)
if not is_pypy:
# Cache size measurements are not available on PyPy because it lacks sys.getsizeof
# load table
loadtable = prettytable.PrettyTable(["property", "statistic"])
loadtable.align = 'l'
loadtable.add_row(["Server load (1 min)","%g" % loadavg[0]])
loadtable.add_row(["Process ID","%g" % pid]),
loadtable.add_row(["Bytes per page","%g " % psize])
loadtable.add_row(["CPU time used (total)", "%s (%gs)" % (utils.time_format(rusage.ru_utime), rusage.ru_utime)])
loadtable.add_row(["CPU time used (user)", "%s (%gs)" % (utils.time_format(rusage.ru_stime), rusage.ru_stime)])
loadtable.add_row(["Memory usage","%g MB (%g%%)" % (rmem, pmem)])
loadtable.add_row(["Virtual address space\n {x(resident+swap+caching){n", "%g MB" % vmem])
loadtable.add_row(["Page faults","%g hard, %g soft, %g swapouts" % (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap)])
loadtable.add_row(["Disk I/O", "%g reads, %g writes" % (rusage.ru_inblock, rusage.ru_oublock)])
loadtable.add_row(["Network I/O", "%g in, %g out" % (rusage.ru_msgrcv, rusage.ru_msgsnd)])
loadtable.add_row(["Context switching", "%g vol, %g forced, %g signals" % (rusage.ru_nvcsw, rusage.ru_nivcsw, rusage.ru_nsignals)])
# object cache size
cachedict = _idmapper.cache_size()
totcache = cachedict["_total"]
string += "\n{w Database entity (idmapper) cache usage:{n %5.2f MB (%i items)" % (totcache[1], totcache[0])
sorted_cache = sorted([(key, tup[0], tup[1]) for key, tup in cachedict.items() if key !="_total" and tup[0] > 0],
key=lambda tup: tup[2], reverse=True)
table = [[tup[0] for tup in sorted_cache],
["%5.2f MB" % tup[2] for tup in sorted_cache],
["%i item(s)" % tup[1] for tup in sorted_cache]]
ftable = utils.format_table(table, 5)
for row in ftable:
string += "\n " + row[0] + row[1] + row[2]
# get sizes of other caches
attr_cache_info, field_cache_info, prop_cache_info = get_cache_sizes()
#size = sum([sum([getsizeof(obj) for obj in dic.values()]) for dic in _attribute_cache.values()])/1024.0
#count = sum([len(dic) for dic in _attribute_cache.values()])
string += "\n{w On-entity Attribute cache usage:{n %5.2f MB (%i attrs)" % (attr_cache_info[1], attr_cache_info[0])
string += "\n{w On-entity Field cache usage:{n %5.2f MB (%i fields)" % (field_cache_info[1], field_cache_info[0])
string += "\n{w On-entity Property cache usage:{n %5.2f MB (%i props)" % (prop_cache_info[1], prop_cache_info[0])
string = "{wServer CPU and Memory load:{n\n%s" % loadtable
if not is_pypy:
# Cache size measurements are not available on PyPy because it lacks sys.getsizeof
# object cache size
cachedict = _idmapper.cache_size()
totcache = cachedict["_total"]
sorted_cache = sorted([(key, tup[0], tup[1]) for key, tup in cachedict.items() if key !="_total" and tup[0] > 0],
key=lambda tup: tup[2], reverse=True)
memtable = prettytable.PrettyTable(["entity name", "number", "cache (MB)", "idmapper %%"])
memtable.align = 'l'
for tup in sorted_cache:
memtable.add_row([tup[0], "%i" % tup [1], "%5.2f" % tup[2], "%.2f" % (float(tup[2]/totcache[1])*100)])
# get sizes of other caches
attr_cache_info, field_cache_info, prop_cache_info = get_cache_sizes()
string += "\n{w Entity idmapper cache usage:{n %5.2f MB (%i items)\n%s" % (totcache[1], totcache[0], memtable)
string += "\n{w On-entity Attribute cache usage:{n %5.2f MB (%i attrs)" % (attr_cache_info[1], attr_cache_info[0])
string += "\n{w On-entity Field cache usage:{n %5.2f MB (%i fields)" % (field_cache_info[1], field_cache_info[0])
string += "\n{w On-entity Property cache usage:{n %5.2f MB (%i props)" % (prop_cache_info[1], prop_cache_info[0])
caller.msg(string)