Added the apropos command for broader help searching (uses icontains).

The suggestions: footer used in help gave too narrow results, now using apropos-style search instead.
Bug fix of state-help command to make it accept switches.
Added several new example commands and cleaned up old ones to be more user-friendly.
Added protection in @delevent to make it harder to delete system events.
Some small bug fixes and other cleanup.
This commit is contained in:
Griatch 2009-12-20 12:39:08 +00:00
parent c7cbc4854e
commit 81bec61d7d
12 changed files with 295 additions and 138 deletions

View file

@ -91,7 +91,6 @@ class Command(object):
# create a list with at least two entries.
raw = "%s " % self.raw_input
cmd_words = raw.split(' ')
try:
if '/' in cmd_words[0]:
# if we have switches we directly go for the first command form.
@ -489,8 +488,7 @@ def handle(command, ignore_state=False):
command_table_lookup(command, state_cmd_table)
else:
# Not in a state. Normal operation.
state = None #make sure, in case the object had a malformed statename.
state = None # make sure, in case the object had a malformed statename.
# Check if the user is using a channel command.
match_channel(command)
# See if the user is trying to traverse an exit.

View file

@ -43,14 +43,13 @@ class CommandTable(object):
the help entry. If not given, 'General' is assumed.
priv_help_tuple (tuple) String tuple of permissions required to view this
help entry. If nothing is given, priv_tuple is used.
auto_help_override (bool): Override the value in settings.AUTO_HELP_ENABLED with the
auto_help_override (bool/None): Override the value in settings.AUTO_HELP_ENABLED with the
value given. Use None to not override.
This can be useful when developing a new routine and
has made manual changes to help entries of other
commands in the database (and so do not want to use global
auto-help). It is also used by e.g. the state system
to selectively deactive auto-help.
auto-help).
Note: the auto_help system also supports limited markup. You can divide your __doc__
with markers of any combinations of the forms
[[Title]]

View file

@ -59,7 +59,7 @@ from src.statetable import GLOBAL_STATE_TABLE
STATENAME="_interactive batch processor"
cwhite = r"%cn%ch%cw"
cred = r"%cn%ch%cw"
cred = r"%cn%ch%cr"
cgreen = r"%cn%ci%cg"
cyellow = r"%cn%ch%cy"
cnorm = r"%cn"
@ -172,7 +172,6 @@ def cmd_batchprocess(command):
Interactive mode allows the user more control over the
processing of the file.
"""
#global CMDSTACKS,STACKPTRS,FILENAMES
source_object = command.source_object
@ -183,7 +182,7 @@ def cmd_batchprocess(command):
args = command.command_argument
if not args:
source_object.emit_to("Usage: @batchprocess[/interactive] <filename with full path>")
source_object.emit_to("Usage: @batchprocess[/interactive] <path/to/file>")
return
filename = args.strip()
@ -196,23 +195,19 @@ def cmd_batchprocess(command):
return
switches = command.command_switches
if switches and switches[0] in ['inter','interactive']:
# allow more control over how batch file is executed
# Allow more control over how batch file is executed
if source_object.has_flag("ADMIN_NOSTATE"):
source_object.unset_flag("ADMIN_NOSTATE")
string = cred + "\nOBS: Flag ADMIN_NOSTATE unset in order to "
string += "run Interactive mode. Don't forget to re-set "
string += "it (if you need it) after you're done."
source_object.emit_to(string)
if not source_object.set_state(STATENAME):
# if we failed it is likely because we have
# ADMIN_NOSTATE set.
source_object.unset_flag("ADMIN_NOSTATE")
if not source_object.set_state(STATENAME):
source_object.emit_to("Error in entering the interactive state.")
source_object.set_flag("ADMIN_NOSTATE")
return
else:
string = "OBS: Flag ADMIN_NOSTATE unset in order to "
string += "run Interactive mode. Don't forget to re-set "
string += "it (if you need it) after you're done."
source_object.emit_to(string)
# store work data in cache
# Set interactive state directly
source_object.cache.state = STATENAME
# Store work data in cache
source_object.cache.batch_cmdstack = commands
source_object.cache.batch_stackptr = 0
source_object.cache.batch_filename = filename
@ -234,13 +229,8 @@ def cmd_batchprocess(command):
GLOBAL_CMD_TABLE.add_command("@batchprocess", cmd_batchprocess,
priv_tuple=("genperms.process_control",), help_category="Building")
#interactive state commands
def printfooter():
"prints a nice footer"
#s = "%s\n== nn/bb/jj == pp/ss == ll == rr/rrr == cc/qq == (hh for help) ==" % cgreen
s = ""
return s
# The Interactive batch processor state
def show_curr(source_object,showall=False):
"Show the current command."
@ -264,7 +254,6 @@ def show_curr(source_object,showall=False):
cnorm)
if showall:
s += "\n%s" % command
s += printfooter()
source_object.emit_to(s)
def process_commands(source_object, steps=0):
@ -309,7 +298,7 @@ def exit_state(source_object):
# since clear_state() is protected against exiting the interactive mode
# (to avoid accidental drop-outs by rooms clearing a player's state),
# we have to clear the state directly here.
source_object.state = None
source_object.cache.state = None
def cmd_state_ll(command):
"""
@ -327,7 +316,6 @@ def cmd_state_pp(command):
Process the currently shown command definition.
"""
process_commands(command.source_object)
command.source_object.emit_to(printfooter())
def cmd_state_rr(command):
"""

View file

@ -18,7 +18,7 @@ def cmd_password(command):
@password - set your password
Usage:
@paassword <old password> = <new password>
@password <old password> = <new password>
Changes your password. Make sure to pick a safe one.
"""
@ -666,12 +666,17 @@ def cmd_help(command):
help - view help database
Usage:
help <topic>
help[/switches] <topic>
Switch:
apropos - show a list of all topics loosely matching the search criterion
(you can also use the commands 'apropos' or 'suggest' for this).
Examples: help index
help topic
help 345
help/apropos del
Shows the available help on <topic>. Use without <topic> to get the help
index. If more than one topic match your query, you will get a
list of topics to choose between. You can also supply a help entry number
@ -680,6 +685,7 @@ def cmd_help(command):
source_object = command.source_object
topicstr = command.command_argument
switches = command.command_switches
if not command.command_argument:
#display topic index if just help command is given
@ -689,7 +695,7 @@ def cmd_help(command):
#check valid query
source_object.emit_to("Your search query must be at least two letters long.")
return
# speciel help index names. These entries are dynamically
# created upon request.
if topicstr in ['topic','topics']:
@ -706,6 +712,21 @@ def cmd_help(command):
source_object.emit_to(text)
return
if switches and 'apropos' in switches:
# run a loose apropos match
topics = HelpEntry.objects.find_apropos(source_object, topicstr)
if topics:
if len(topics) > 50:
string = "Topics containing string '%s' (first 50):\n" % topicstr
topics = topics[:49]
else:
string = "Topics containing string '%s':\n" % topicstr
string += ", ".join(topic.get_topicname() for topic in topics)
else:
string = "No matches found for %s." % topicstr
source_object.emit_to(string)
return
# not a special help index entry. Do a search for the help entry.
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
@ -765,51 +786,22 @@ def cmd_help(command):
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("help", cmd_help)
def cmd_apropos(command):
"""
apropos - show rough help matches
## def cmd_testevent(command):
## from src import events
## from src import scheduler
## source_object = command.source_object
## if not command.command_argument:
## #event = events.IntervalEvent()
## event = events.IntervalEvent()
## event.repeats = 3
## event.interval = 5
## pid = scheduler.add_event(event)
## source_object.emit_to("event with pid %s added." % pid)
## else:
## pid = command.command_argument
## scheduler.del_event(pid)
## source_object.emit_to("event with pid %s removed (if it existed)." % pid)
## GLOBAL_CMD_TABLE.add_command("testevent", cmd_testevent)
## def cmd_testcache(command):
## from src.cache import cache
## from src import scheduler
## from src import events
## from src import gametime
## source_object = command.source_object
## switches = command.command_switches
## s1 = "Temp_cache_val_OK"
## s2 = "Perm_cache_val_OK"
## s3 = "Perm_cache_val2_OK"
## if switches and "get" in switches:
## cache.load_pcache()
## cache_vol = source_object.cache.testcache
## source_object.emit_to("< volatile cache: %s" % cache_vol)
## cache_perm = source_object.pcache.testcache_perm
## source_object.emit_to("< cache_perm1: %s" % cache_perm)
## cache_perm2 = cache.get_pcache("permtest2")
## source_object.emit_to("< cache_perm2: %s" % cache_perm2)
## else:
## source_object.cache.testcache = s1
## source_object.pcache.testcache_perm = s2
## cache.set_pcache("permtest2", s3)
## source_object.emit_to("> volatile cache: %s" % s1)
## source_object.emit_to("> cache_perm1: %s" % s2)
## source_object.emit_to("> cache_perm2: %s" % s3)
## cache.save_pcache()
## source_object.emit_to("Caches saved.")
## source_object.emit_to("Time: %i" % gametime.time())
## GLOBAL_CMD_TABLE.add_command("testcache", cmd_testcache)
Usage:
apropos <text>
or
suggest <text>
This presents a list of topics very loosely matching your
search text. Use this command when you are searching for
help on a certain concept but don't know any exact
command names. You can also use the normal help command
with the /apropos switch to get the same functionality.
"""
arg = command.command_argument
command.source_object.execute_cmd("help/apropos %s" % arg)
GLOBAL_CMD_TABLE.add_command("apropos", cmd_apropos)
GLOBAL_CMD_TABLE.add_command("suggest", cmd_apropos)

View file

@ -18,6 +18,7 @@ from src import cache
from src import scheduler
def cmd_reload(command):
"""
@reload - reload game subsystems
@ -771,13 +772,18 @@ def cmd_delevent(command):
@delevent - remove events manually
Usage:
@delevent <pid>
@delevent[/switch] <pid>
Switch:
force - by default, certain default low-pid system events are protected
from accidental deletion. This switch overrides this
protection.
Removes an event with the given pid (process ID) from the event scheduler.
To see all active events and their pids, use the @ps command.
"""
source_object = command.source_object
switches = command.command_switches
if not command.command_argument:
source_object.emit_to("Usage: @delevent <pid>")
return
@ -786,6 +792,19 @@ def cmd_delevent(command):
except ValueError:
source_object.emit_to("You must supply a valid pid number.")
return
if pid < 3 and "force" not in switches:
# low-pid protection
string = "Warning:\n"
string += " Pid %i is a low-pid system event. It is usually not\n" % pid
string +=" something you want to delete since crucial\n"
string += " engine functions depend on it. If you were not\n"
string += " mistaken and know what you are doing, give this\n"
string += " command again with the /force switch to override\n"
string += " this protection."
source_object.emit_to(string)
return
event = scheduler.get_event(pid)
if event:
desc = event.description

View file

@ -19,37 +19,24 @@ class HelpEntryManager(models.Manager):
# check permissions
t_query = [topic for topic in t_query if topic.can_view(pobject)]
return t_query
def find_apropos(self, pobject, topicstr):
"""
Do a very loose search, returning all help entries containing
the search criterion in their titles.
"""
topics = self.filter(topicname__icontains=topicstr)
return [topic for topic in topics if topic.can_view(pobject)]
def find_topicsuggestions(self, pobject, topicstr):
"""
Do a fuzzy match, preferably within the category of the
current topic.
"""
basetopic = self.filter(topicname__iexact=topicstr)
if not basetopic:
# this topic does not exist; just reply partial
# matches to the string
topics = self.filter(topicname__icontains=topicstr)
return [topic for topic in topics if topic.can_view(pobject)]
# we know that the topic exists, try to find similar ones within
# its category.
basetopic = basetopic[0]
category = basetopic.category
topics = []
#remove the @
crop = topicstr.lstrip('@')
# first we filter for matches with the full name
topics = self.filter(category__iexact=category).filter(topicname__icontains=crop)
if len(crop) > 4:
# next search with a cropped version of the command.
ttemp = self.filter(category__iexact=category).filter(topicname__icontains=crop[:4])
ttemp = [topic for topic in ttemp if topic not in topics]
topics = list(topics) + list(ttemp)
topics = self.find_apropos(pobject, topicstr)
# we need to clean away the given help entry.
return [topic for topic in topics if topic.topicname.lower() != topicstr.lower()]
return [topic for topic in topics
if topic.topicname.lower() != topicstr.lower()]
def find_topics_with_category(self, pobject, category):
"""
@ -57,3 +44,4 @@ class HelpEntryManager(models.Manager):
"""
t_query = self.filter(category__iexact=category)
return [topic for topic in t_query if topic.can_view(pobject)]

View file

@ -441,6 +441,12 @@ class ObjectManager(models.Manager):
new_object.owner = None
new_object.zone = None
else:
if owner == None:
# if owner is None for a non-player object we are probably
# creating an object from a script. In this case we set
# the owner to be the superuser.
owner = self.get_object_from_dbref("#1")
new_object.owner = owner
if new_object.get_owner().get_zone():
new_object.zone = new_object.get_owner().get_zone()

View file

@ -62,7 +62,7 @@ class Attribute(models.Model):
"""
attr_value = self.attr_value
if self.attr_ispickled:
attr_value = pickle.loads(str(attr_value))
attr_value = pickle.loads(str(attr_value))
return attr_value
def set_value(self, new_value):
@ -82,7 +82,7 @@ class Attribute(models.Model):
self.attr_value = new_value
self.attr_ispickled = ispickled
self.save()
def get_object(self):
"""
Returns the object that the attribute resides on.
@ -315,7 +315,7 @@ class Object(models.Model):
no matter what)
"""
# The Command object has all of the methods for parsing and preparing
# for searching and execution. Send it to the handler once populated.
# for searching and execution. Send it to the handler once populated.
cmdhandler.handle(cmdhandler.Command(self, command_str,
session=session),
ignore_state=ignore_state)
@ -1267,14 +1267,15 @@ class Object(models.Model):
# we never enter other states if we are already in
# the interactive batch processor.
nostate = nostate or self.get_state() == "_interactive batch processor"
nostate = nostate or \
self.get_state() == "_interactive batch processor"
if nostate:
return False
# switch the state
self.cache.state = state_name
return True
def clear_state(self):
"""
Set to no state (return to normal operation)
@ -1284,7 +1285,7 @@ class Object(models.Model):
(batch processor clears the state directly instead)
"""
if not self.state == "_interactive batch processor":
self.state = None
self.cache.state = None
def purge_object(self):
"Completely clears all aspects of the object."

View file

@ -354,10 +354,21 @@ def cmd_state_help(command):
help - view help database
Usage:
help <topic>
help[/switches] <topic>
Switch:
apropos - show a list of all matches containing the search criterion
(you can also use the command 'apropos' for this).
Shows the available help on <topic>. Use without a topic
to get the index.
Examples: help index
help topic
help 345
help/apropos del
Shows the available help on <topic>. Use without <topic> to get the help
index. If more than one topic match your query, you will get a
list of topics to choose between. You can also supply a help entry number
directly if you know it.
"""
source_object = command.source_object
@ -385,7 +396,7 @@ def cmd_state_help(command):
if helptext:
source_object.emit_to("\n%s" % helptext)
else:
source_object.execute_cmd("help %s" % topicstr, ignore_state=True)
source_object.execute_cmd("%s" % command.raw_input, ignore_state=True)
#import this instance into your modules
GLOBAL_STATE_TABLE = StateTable()