Reworked the rpsystem with a working search replacement mechanism (allowing to search objects by sdesc)

This commit is contained in:
Griatch 2015-09-23 12:57:55 +02:00
parent 793a307867
commit c34561dba2

View file

@ -262,7 +262,7 @@ def parse_language(speaker, emote):
def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False): def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False):
""" """
Read a textraw emote and parse it into an intermediary Read a textraw emote and parse it into an intermediary
format for distributing to all observers. format for distributing to all observers.
@ -272,16 +272,20 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
recog data will be considered in the parsing. recog data will be considered in the parsing.
candidates (iterable): A list of objects valid for referencing candidates (iterable): A list of objects valid for referencing
in the emote. in the emote.
emote (str): The incoming emote from the caller. string (str): The string (like an emote) we want to analyze for keywords.
map_obj (bool, optional): Return a mapping including the real db search_mode (bool, optional): If `True`, the "emote" is a query string
object rather than the sdesc. we want to analyze. If so, the return value is changed.
Returns: Returns:
(emote, mapping) (tuple): A tuple where the emote is the emote (emote, mapping) (tuple): If `search_mode` is `False`
string, with all references replaced with (default), a tuple where the emote is the emote string, with
internal-representation {#dbref} markers and mapping is a all references replaced with internal-representation {#dbref}
dictionary `{"#dbref":"sdesc", ...}` if `map_obj` is markers and mapping is a dictionary `{"#dbref":"sdesc", ...}`
`False` (default) and `{"#dbref":obj,...}` otherwise. if `map_obj` is `False` (default) and `{"#dbref":obj,...}`
otherwise.
result (list): If `search_mode` is `True` we are
performing a search query on `string`, looking for a specific
object. A list with zero, one or more matches.
Raises: Raises:
EmoteException: For various ref-matching errors. EmoteException: For various ref-matching errors.
@ -296,31 +300,26 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
- says, "..." are - says, "..." are
""" """
# Load all candidate regex tuples [(regex, obj, sdesc/recog),...]
# escape mapping syntax on the form {#id} if it exists already in emote, candidate_regexes = \
# if so it is replaced with just "id". [(_RE_SELF_REF, sender, sender.sdesc.get())] + \
emote = _RE_REF.sub("\1", emote) [sender.recog.get_regex_tuple(obj)
# build all candidate regex tuples
candidate_regexes = [sender.recog.get_regex_tuple(obj)
for obj in candidates if hasattr(obj, "recog")] + \ for obj in candidates if hasattr(obj, "recog")] + \
[obj.sdesc.get_regex_tuple() [obj.sdesc.get_regex_tuple()
for obj in candidates if hasattr(obj, "sdesc")] for obj in candidates if hasattr(obj, "sdesc")]
# filter out non-found data # filter out non-found data
candidate_regexes = [tup for tup in candidate_regexes if tup] candidate_regexes = [tup for tup in candidate_regexes if tup]
# handle self-reference first # escape mapping syntax on the form {#id} if it exists already in emote,
objlist = [] # if so it is replaced with just "id".
mapping = {} string = _RE_REF.sub("\1", string)
if _RE_SELF_REF.search(emote):
key = "#%i" % sender.id
emote = _RE_SELF_REF.sub("{%s}" % key, emote)
mapping[key] = sender if map_obj else (sender.db.sdesc or sender.key)
objlist.append(sender)
# we now loop over all references and analyze them # we now loop over all references and analyze them
mapping = {}
errors = [] errors = []
for marker_match in reversed(list(_RE_OBJ_REF_START.finditer(emote))): obj = None
nmatches = 0
for marker_match in reversed(list(_RE_OBJ_REF_START.finditer(string))):
# we scan backwards so we can replace in-situ without messing # we scan backwards so we can replace in-situ without messing
# up later occurrences. Given a marker match, query from # up later occurrences. Given a marker match, query from
# start index forward for all candidates. # start index forward for all candidates.
@ -332,57 +331,68 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
# +1 for _NUM_SEP, if defined # +1 for _NUM_SEP, if defined
istart = istart0 #+ (len(num_identifier) + 1 if num_identifier else 0) istart = istart0 #+ (len(num_identifier) + 1 if num_identifier else 0)
print "marker match:", marker_match.group(), istart0, istart, emote[istart:] print "marker match:", marker_match.group(), istart0, istart, string[istart:]
#print "candidates:", [tup[2] for tup in candidate_regexes] #print "candidates:", [tup[2] for tup in candidate_regexes]
# loop over all candidate regexes and match against the string following the match # loop over all candidate regexes and match against the string following the match
matches = ((reg.match(emote[istart:]), obj, text) for reg, obj, text in candidate_regexes) matches = ((reg.match(string[istart:]), obj, text) for reg, obj, text in candidate_regexes)
# score matches by how long part of the string was matched # score matches by how long part of the string was matched
matches = [(match.end() if match else -1, obj, text) for match, obj, text in matches] matches = [(match.end() if match else -1, obj, text) for match, obj, text in matches]
print "matches:", istart, matches print "matches:", istart, matches
maxscore = max(score for score, obj, text in matches) maxscore = max(score for score, obj, text in matches)
# analyze result
if maxscore == -1:
# No matches
errors.append(_EMOTE_NOMATCH_ERROR.format(ref=marker_match.group()))
continue
# we have a valid maxscore, extract all matches with this value # we have a valid maxscore, extract all matches with this value
bestmatches = [(obj, text) for score, obj, text in matches if maxscore == score] bestmatches = [(obj, text) for score, obj, text in matches if maxscore == score != -1]
nmatches = len(bestmatches) nmatches = len(bestmatches)
print "nmatches:", nmatches, bestmatches
if nmatches == 1: if not nmatches:
# no matches
obj = None
nmatches = 0
elif nmatches == 1:
# an exact match. # an exact match.
obj = bestmatches[0][0] obj = bestmatches[0][0]
if nmatches: nmatches = 1
# several matches have the same score. elif all(bestmatches[0][0].id == obj.id for obj, text in bestmatches):
inum = max(0, int(num_identifier) - 1) if num_identifier else None # multi-match but all reference the same obj (could happen
if all(bestmatches[0][0].id == obj.id for obj, text in bestmatches): # with clashing recogs/sdescs)
# multi-matches all references the same obj (could happen with clashing recogs/sdescs) obj = bestmatches[0][0]
obj = bestmatches[0][0] nmatches = 1
else:
# multi-match.
# was a numerical identifier given to help us separate the multi-match?
inum = min(max(0, int(num_identifier) - 1), nmatches-1) if num_identifier else None
if inum is not None:
# A valid inum is given. Use this to separate data
obj = bestmatches[inum][0]
nmatches = 1
else: else:
# was a numerical identifier given to help us separate the multi-match? # no identifier given - a real multimatch
if inum is None or inum > nmatches: obj = bestmatches
# no match or invalid match id given
refname = marker_match.group()
reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, _RE_PREFIX.sub("", refname), text)
for inum, (obj, text) in enumerate(bestmatches) if score == maxscore]
errors.append(_EMOTE_MULTIMATCH_ERROR.format(
ref=marker_match.group(), reflist="\n ".join(reflist)))
continue
else:
# A valid inum is given. Use this to separate data
obj = bestmatches[inum][0]
# if we get to this point we have identifed a local object tied to this sdesc or recog marker. if search_mode:
# we replace it with the interal representation on the form {#dbref}. # single-object search
print "replace emote:", istart, maxscore, emote, emote[istart + maxscore:] break
key = "#%i" % obj.id elif nmatches == 0:
emote = emote[:istart0] + "{%s}" % key + emote[istart + maxscore:] errors.append(_EMOTE_NOMATCH_ERROR.format(ref=marker_match.group()))
mapping[key] = obj if map_obj else (obj.db.sdesc or obj.key) elif nmatches == 1:
objlist.append(obj) key = "#%i" % obj.id
string = string[:istart0] + "{%s}" % key + string[istart + maxscore:]
mapping[key] = obj if search_mode else (obj.db.sdesc or obj.key)
else:
refname = marker_match.group()
reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, _RE_PREFIX.sub("", refname), text)
for inum, (obj, text) in enumerate(bestmatches) if score == maxscore]
errors.append(_EMOTE_MULTIMATCH_ERROR.format(
ref=marker_match.group(), reflist="\n ".join(reflist)))
if search_mode:
# return list of object(s) matching
if nmatches == 0:
return []
elif nmatches == 1:
return [obj]
else:
return [tup[0] for tup in obj]
if errors: if errors:
# make sure to not let errors through. # make sure to not let errors through.
@ -390,7 +400,7 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
# at this point all references have been replaced with {#xxx} markers and the mapping contains # at this point all references have been replaced with {#xxx} markers and the mapping contains
# a 1:1 mapping between those inline markers and objects. # a 1:1 mapping between those inline markers and objects.
return emote, mapping return string, mapping
def receive_emote(sender, receiver, emote, sdesc_mapping, language_mapping): def receive_emote(sender, receiver, emote, sdesc_mapping, language_mapping):
@ -624,17 +634,19 @@ class CmdRecog(Command): # assign personal alias to object in room
alias = self.alias alias = self.alias
prefixed_sdesc = sdesc if sdesc.startswith(_PREFIX) else _PREFIX + sdesc prefixed_sdesc = sdesc if sdesc.startswith(_PREFIX) else _PREFIX + sdesc
candidates = caller.location.contents candidates = caller.location.contents
try: matches = parse_sdescs_and_recogs(caller, candidates, prefixed_sdesc, search_mode=True)
_, mapping = parse_sdescs_and_recogs(caller, candidates, prefixed_sdesc, map_obj=True) nmatches = len(matches)
# handle 0, 1 and >1 matches
except EmoteError as err: if nmatches == 0:
# errors are handled already here caller.msg(_EMOTE_NOMATCH_ERROR.format(ref=sdesc))
caller.msg(err.message) elif nmatches > 1:
return reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, _RE_PREFIX.sub("", sdesc), obj.sdesc.get())
obj = mapping.values()[0] for inum, obj in enumerate(matches)]
# we have all we need, add the recog alias caller.msg(_EMOTE_MULTIMATCH_ERROR.format(ref=sdesc,reflist="\n ".join(reflist)))
alias = caller.set_recog(obj, alias) else:
caller.msg("You will now remember {w%s{n as {w%s{n." % (obj.db.sdesc, alias)) # we have all we need, add the recog alias
alias = caller.recog.add(obj, alias)
caller.msg("You will now remember {w%s{n as {w%s{n." % (obj.db.sdesc, alias))
class CmdLanguage(Command): # list available languages class CmdLanguage(Command): # list available languages
@ -795,7 +807,6 @@ class RecogHandler(object):
SdescError: When recog could not be set or sdesc longer SdescError: When recog could not be set or sdesc longer
than `max_length`. than `max_length`.
""" """
# strip emote components from recog # strip emote components from recog
recog = _RE_REF.sub("\1", recog = _RE_REF.sub("\1",
@ -912,13 +923,20 @@ class RPObject(DefaultObject):
if (isinstance(searchdata, basestring) and not if (isinstance(searchdata, basestring) and not
(kwargs.get("global_search") or (kwargs.get("global_search") or
kwargs.get("candidates"))): kwargs.get("candidates"))):
try: matches = parse_sdescs_and_recogs(self, self.location.contents,
_, mapping = parse_sdescs_and_recogs(self, _PREFIX + searchdata, search_mode=True)
self.location.contents, _PREFIX + searchdata, map_obj=True) nmatches = len(matches)
return mapping.values()[0] print "matches:", matches
except EmoteError: if nmatches == 1:
pass return matches[0]
# fall back to original search method elif nmatches > 1:
# multimatch
print matches
reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, searchdata, obj.sdesc.get())
for inum, obj in enumerate(matches)]
self.msg(_EMOTE_MULTIMATCH_ERROR.format(ref=searchdata,reflist="\n ".join(reflist)))
return
# fall back to normal search
return super(RPObject, self).search(searchdata, **kwargs) return super(RPObject, self).search(searchdata, **kwargs)