Add search-object functionality to olc menu
This commit is contained in:
parent
69ae055e20
commit
e5f63f8431
4 changed files with 266 additions and 66 deletions
|
|
@ -7,7 +7,9 @@ OLC Prototype menu nodes
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from random import choice
|
from random import choice
|
||||||
|
from django.db.models import Q
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from evennia.objects.models import ObjectDB
|
||||||
from evennia.utils.evmenu import EvMenu, list_node
|
from evennia.utils.evmenu import EvMenu, list_node
|
||||||
from evennia.utils import evmore
|
from evennia.utils import evmore
|
||||||
from evennia.utils.ansi import strip_ansi
|
from evennia.utils.ansi import strip_ansi
|
||||||
|
|
@ -273,7 +275,11 @@ def _get_current_value(caller, keyname, formatter=str):
|
||||||
flat_prot = _get_flat_menu_prototype(caller)
|
flat_prot = _get_flat_menu_prototype(caller)
|
||||||
if keyname in flat_prot:
|
if keyname in flat_prot:
|
||||||
# value in flattened prot
|
# value in flattened prot
|
||||||
return "Current {} (|binherited|n): {}".format(keyname, formatter(flat_prot[keyname]))
|
if keyname == 'prototype_key':
|
||||||
|
# we don't inherit prototype_keys
|
||||||
|
return "[No prototype_key set] (|rnot inherited|n)"
|
||||||
|
else:
|
||||||
|
return "Current {} (|binherited|n): {}".format(keyname, formatter(flat_prot[keyname]))
|
||||||
return "[No {} set]".format(keyname)
|
return "[No {} set]".format(keyname)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -305,6 +311,180 @@ def _default_parse(raw_inp, choices, *args):
|
||||||
|
|
||||||
# Menu nodes ------------------------------
|
# Menu nodes ------------------------------
|
||||||
|
|
||||||
|
# helper nodes
|
||||||
|
|
||||||
|
# validate prototype (available as option from all nodes)
|
||||||
|
|
||||||
|
def node_validate_prototype(caller, raw_string, **kwargs):
|
||||||
|
"""General node to view and validate a protototype"""
|
||||||
|
prototype = _get_flat_menu_prototype(caller, validate=False)
|
||||||
|
prev_node = kwargs.get("back", "index")
|
||||||
|
|
||||||
|
_, text = _validate_prototype(prototype)
|
||||||
|
|
||||||
|
helptext = """
|
||||||
|
The validator checks if the prototype's various values are on the expected form. It also tests
|
||||||
|
any $protfuncs.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
text = (text, helptext)
|
||||||
|
|
||||||
|
options = _wizard_options(None, prev_node, None)
|
||||||
|
options.append({"key": "_default",
|
||||||
|
"goto": "node_" + prev_node})
|
||||||
|
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def node_examine_entity(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
General node to view a text and then return to previous node. Kwargs should contain "text" for
|
||||||
|
the text to show and 'back" pointing to the node to return to.
|
||||||
|
"""
|
||||||
|
text = kwargs.get("text", "Nothing was found here.")
|
||||||
|
helptext = "Use |wback|n to return to the previous node."
|
||||||
|
prev_node = kwargs.get('back', 'index')
|
||||||
|
|
||||||
|
text = (text, helptext)
|
||||||
|
|
||||||
|
options = _wizard_options(None, prev_node, None)
|
||||||
|
options.append({"key": "_default",
|
||||||
|
"goto": "node_" + prev_node})
|
||||||
|
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def _search_object(caller):
|
||||||
|
"update search term based on query stored on menu; store match too"
|
||||||
|
try:
|
||||||
|
searchstring = caller.ndb._menutree.olc_search_object_term.strip()
|
||||||
|
caller.ndb._menutree.olc_search_object_matches = []
|
||||||
|
except AttributeError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if not searchstring:
|
||||||
|
caller.msg("Must specify a search criterion.")
|
||||||
|
return []
|
||||||
|
|
||||||
|
is_dbref = utils.dbref(searchstring)
|
||||||
|
is_account = searchstring.startswith("*")
|
||||||
|
|
||||||
|
if is_dbref or is_account:
|
||||||
|
|
||||||
|
if is_dbref:
|
||||||
|
# a dbref search
|
||||||
|
results = caller.search(searchstring, global_search=True, quiet=True)
|
||||||
|
else:
|
||||||
|
# an account search
|
||||||
|
searchstring = searchstring.lstrip("*")
|
||||||
|
results = caller.search_account(searchstring, quiet=True)
|
||||||
|
else:
|
||||||
|
keyquery = Q(db_key__istartswith=searchstring)
|
||||||
|
aliasquery = Q(db_tags__db_key__istartswith=searchstring,
|
||||||
|
db_tags__db_tagtype__iexact="alias")
|
||||||
|
results = ObjectDB.objects.filter(keyquery | aliasquery).distinct()
|
||||||
|
|
||||||
|
caller.msg("Searching for '{}' ...".format(searchstring))
|
||||||
|
caller.ndb._menutree.olc_search_object_matches = results
|
||||||
|
return ["{}(#{})".format(obj.key, obj.id) for obj in results]
|
||||||
|
|
||||||
|
|
||||||
|
def _object_select(caller, obj_entry, **kwargs):
|
||||||
|
choices = kwargs['available_choices']
|
||||||
|
num = choices.index(obj_entry)
|
||||||
|
matches = caller.ndb._menutree.olc_search_object_matches
|
||||||
|
obj = matches[num]
|
||||||
|
|
||||||
|
if not obj.access(caller, 'examine'):
|
||||||
|
caller.msg("|rYou don't have 'examine' access on this object.|n")
|
||||||
|
del caller.ndb._menutree.olc_search_object_term
|
||||||
|
return "node_search_object"
|
||||||
|
|
||||||
|
prot = spawner.prototype_from_object(obj)
|
||||||
|
txt = protlib.prototype_to_str(prot)
|
||||||
|
return "node_examine_entity", {"text": txt, "back": "search_object"}
|
||||||
|
|
||||||
|
|
||||||
|
def _object_actions(caller, raw_inp, **kwargs):
|
||||||
|
"All this does is to queue a search query"
|
||||||
|
choices = kwargs['available_choices']
|
||||||
|
obj_entry, action = _default_parse(
|
||||||
|
raw_inp, choices, ("examine", "e"), ("create prototype from object", "create", "c"))
|
||||||
|
|
||||||
|
if obj_entry:
|
||||||
|
|
||||||
|
num = choices.index(obj_entry)
|
||||||
|
matches = caller.ndb._menutree.olc_search_object_matches
|
||||||
|
obj = matches[num]
|
||||||
|
prot = spawner.prototype_from_object(obj)
|
||||||
|
|
||||||
|
if action == "examine":
|
||||||
|
|
||||||
|
if not obj.access(caller, 'examine'):
|
||||||
|
caller.msg("\n|rYou don't have 'examine' access on this object.|n")
|
||||||
|
del caller.ndb._menutree.olc_search_object_term
|
||||||
|
return "node_search_object"
|
||||||
|
|
||||||
|
txt = protlib.prototype_to_str(prot)
|
||||||
|
return "node_examine_entity", {"text": txt, "back": "search_object"}
|
||||||
|
else:
|
||||||
|
# load prototype
|
||||||
|
|
||||||
|
if not obj.access(caller, 'control'):
|
||||||
|
caller.msg("|rYou don't have access to do this with this object.|n")
|
||||||
|
del caller.ndb._menutree.olc_search_object_term
|
||||||
|
return "node_search_object"
|
||||||
|
|
||||||
|
_set_menu_prototype(caller, prot)
|
||||||
|
caller.msg("Created prototype from object.")
|
||||||
|
return "node_index"
|
||||||
|
else:
|
||||||
|
caller.ndb._menutree.olc_search_object_term = raw_inp
|
||||||
|
return "node_search_object", kwargs
|
||||||
|
|
||||||
|
|
||||||
|
@list_node(_search_object, _object_select)
|
||||||
|
def node_search_object(caller, raw_inp, **kwargs):
|
||||||
|
"""
|
||||||
|
Node for searching for an existing object.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
matches = caller.ndb._menutree.olc_search_object_matches
|
||||||
|
except AttributeError:
|
||||||
|
matches = []
|
||||||
|
nmatches = len(matches)
|
||||||
|
prev_node = kwargs.get("back", "index")
|
||||||
|
|
||||||
|
if matches:
|
||||||
|
text = """
|
||||||
|
Found {num} match{post}.
|
||||||
|
|
||||||
|
{actions}
|
||||||
|
(|RWarning: creating a prototype will |roverwrite|r |Rthe current prototype!)|n""".format(
|
||||||
|
num=nmatches, post="es" if nmatches > 1 else "",
|
||||||
|
actions=_format_list_actions(
|
||||||
|
"examine", "create prototype from object", prefix="Actions: "))
|
||||||
|
else:
|
||||||
|
text = "Enter search criterion."
|
||||||
|
|
||||||
|
helptext = """
|
||||||
|
You can search objects by specifying partial key, alias or its exact #dbref. Use *query to
|
||||||
|
search for an Account instead.
|
||||||
|
|
||||||
|
Once having found any matches you can choose to examine it or use |ccreate prototype from
|
||||||
|
object|n. If doing the latter, a prototype will be calculated from the selected object and
|
||||||
|
loaded as the new 'current' prototype. This is useful for having a base to build from but be
|
||||||
|
careful you are not throwing away any existing, unsaved, prototype work!
|
||||||
|
"""
|
||||||
|
|
||||||
|
text = (text, helptext)
|
||||||
|
|
||||||
|
options = _wizard_options(None, prev_node, None)
|
||||||
|
options.append({"key": "_default",
|
||||||
|
"goto": (_object_actions, {"back": prev_node})})
|
||||||
|
|
||||||
|
return text, options
|
||||||
|
|
||||||
# main index (start page) node
|
# main index (start page) node
|
||||||
|
|
||||||
|
|
@ -382,49 +562,9 @@ def node_index(caller):
|
||||||
{"key": ("|wSP|Wawn prototype", "spawn", "sp"),
|
{"key": ("|wSP|Wawn prototype", "spawn", "sp"),
|
||||||
"goto": "node_prototype_spawn"},
|
"goto": "node_prototype_spawn"},
|
||||||
{"key": ("|wLO|Wad prototype", "load", "lo"),
|
{"key": ("|wLO|Wad prototype", "load", "lo"),
|
||||||
"goto": "node_prototype_load"}))
|
"goto": "node_prototype_load"},
|
||||||
|
{"key": ("|wSE|Warch objects|n", "search", "se"),
|
||||||
return text, options
|
"goto": "node_search_object"}))
|
||||||
|
|
||||||
|
|
||||||
# validate prototype (available as option from all nodes)
|
|
||||||
|
|
||||||
def node_validate_prototype(caller, raw_string, **kwargs):
|
|
||||||
"""General node to view and validate a protototype"""
|
|
||||||
prototype = _get_menu_prototype(caller)
|
|
||||||
prev_node = kwargs.get("back", "index")
|
|
||||||
|
|
||||||
_, text = _validate_prototype(prototype)
|
|
||||||
|
|
||||||
helptext = """
|
|
||||||
The validator checks if the prototype's various values are on the expected form. It also tests
|
|
||||||
any $protfuncs.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
text = (text, helptext)
|
|
||||||
|
|
||||||
options = _wizard_options(None, prev_node, None)
|
|
||||||
options.append({"key": "_default",
|
|
||||||
"goto": "node_" + prev_node})
|
|
||||||
|
|
||||||
return text, options
|
|
||||||
|
|
||||||
|
|
||||||
def node_examine_entity(caller, raw_string, **kwargs):
|
|
||||||
"""
|
|
||||||
General node to view a text and then return to previous node. Kwargs should contain "text" for
|
|
||||||
the text to show and 'back" pointing to the node to return to.
|
|
||||||
"""
|
|
||||||
text = kwargs.get("text", "Nothing was found here.")
|
|
||||||
helptext = "Use |wback|n to return to the previous node."
|
|
||||||
prev_node = kwargs.get('back', 'index')
|
|
||||||
|
|
||||||
text = (text, helptext)
|
|
||||||
|
|
||||||
options = _wizard_options(None, prev_node, None)
|
|
||||||
options.append({"key": "_default",
|
|
||||||
"goto": "node_" + prev_node})
|
|
||||||
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
@ -811,7 +951,7 @@ def node_aliases(caller):
|
||||||
|
|
||||||
def _caller_attrs(caller):
|
def _caller_attrs(caller):
|
||||||
prototype = _get_menu_prototype(caller)
|
prototype = _get_menu_prototype(caller)
|
||||||
attrs = ["{}={}".format(tup[0], utils.crop(utils.to_str(tup[1]), width=10))
|
attrs = ["{}={}".format(tup[0], utils.crop(utils.to_str(tup[1], force_string=True), width=10))
|
||||||
for tup in prototype.get("attrs", [])]
|
for tup in prototype.get("attrs", [])]
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
@ -1837,7 +1977,7 @@ class OLCMenu(EvMenu):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
olc_keys = ("index", "forward", "back", "previous", "next", "validate prototype",
|
olc_keys = ("index", "forward", "back", "previous", "next", "validate prototype",
|
||||||
"save prototype", "load prototype", "spawn prototype")
|
"save prototype", "load prototype", "spawn prototype", "search objects")
|
||||||
olc_options = []
|
olc_options = []
|
||||||
other_options = []
|
other_options = []
|
||||||
for key, desc in optionlist:
|
for key, desc in optionlist:
|
||||||
|
|
@ -1878,6 +2018,7 @@ def start_olc(caller, session=None, prototype=None):
|
||||||
menudata = {"node_index": node_index,
|
menudata = {"node_index": node_index,
|
||||||
"node_validate_prototype": node_validate_prototype,
|
"node_validate_prototype": node_validate_prototype,
|
||||||
"node_examine_entity": node_examine_entity,
|
"node_examine_entity": node_examine_entity,
|
||||||
|
"node_search_object": node_search_object,
|
||||||
"node_prototype_key": node_prototype_key,
|
"node_prototype_key": node_prototype_key,
|
||||||
"node_prototype_parent": node_prototype_parent,
|
"node_prototype_parent": node_prototype_parent,
|
||||||
"node_typeclass": node_typeclass,
|
"node_typeclass": node_typeclass,
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,8 @@ def value_to_obj_or_any(value):
|
||||||
stype = type(value)
|
stype = type(value)
|
||||||
if is_iter(value):
|
if is_iter(value):
|
||||||
if stype == dict:
|
if stype == dict:
|
||||||
return {value_to_obj_or_any(key): value_to_obj_or_any(val) for key, val in value.items()}
|
return {value_to_obj_or_any(key):
|
||||||
|
value_to_obj_or_any(val) for key, val in value.items()}
|
||||||
else:
|
else:
|
||||||
return stype([value_to_obj_or_any(val) for val in value])
|
return stype([value_to_obj_or_any(val) for val in value])
|
||||||
obj = dbid_to_obj(value, ObjectDB)
|
obj = dbid_to_obj(value, ObjectDB)
|
||||||
|
|
@ -165,18 +166,70 @@ def prototype_to_str(prototype):
|
||||||
prototype (dict): The prototype.
|
prototype (dict): The prototype.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
header = (
|
header = """
|
||||||
"|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n"
|
|cprototype-key:|n {prototype_key}, |c-tags:|n {prototype_tags}, |c-locks:|n {prototype_locks}|n
|
||||||
"|cdesc:|n {} \n|cprototype:|n ".format(
|
|c-desc|n: {prototype_desc}
|
||||||
prototype.get('prototype_key', None),
|
|cprototype-parent:|n {prototype_parent}
|
||||||
", ".join(prototype.get('prototype_tags', ['None'])),
|
\n""".format(
|
||||||
prototype.get('prototype_locks', None),
|
prototype_key=prototype.get('prototype_key', '|r[UNSET](required)|n'),
|
||||||
prototype.get('prototype_desc', None)))
|
prototype_tags=prototype.get('prototype_tags', '|wNone|n'),
|
||||||
proto = ("{{\n {} \n}}".format(
|
prototype_locks=prototype.get('prototype_locks', '|wNone|n'),
|
||||||
"\n ".join(
|
prototype_desc=prototype.get('prototype_desc', '|wNone|n'),
|
||||||
"{!r}: {!r},".format(key, value) for key, value in
|
prototype_parent=prototype.get('prototype_parent', '|wNone|n'))
|
||||||
sorted(prototype.items()) if key not in _PROTOTYPE_META_NAMES)).rstrip(","))
|
|
||||||
return header + proto
|
key = prototype.get('key', '')
|
||||||
|
if key:
|
||||||
|
key = "|ckey:|n {key}".format(key=key)
|
||||||
|
aliases = prototype.get("aliases", '')
|
||||||
|
if aliases:
|
||||||
|
aliases = "|caliases:|n {aliases}".format(
|
||||||
|
aliases=", ".join(aliases))
|
||||||
|
attrs = prototype.get("attrs", '')
|
||||||
|
if attrs:
|
||||||
|
out = []
|
||||||
|
for (attrkey, value, category, locks) in attrs:
|
||||||
|
locks = ", ".join(lock for lock in locks if lock)
|
||||||
|
category = "|ccategory:|n {}".format(category) if category else ''
|
||||||
|
cat_locks = ""
|
||||||
|
if category or locks:
|
||||||
|
cat_locks = "(|ccategory:|n {category}, ".format(
|
||||||
|
category=category if category else "|wNone|n")
|
||||||
|
out.append(
|
||||||
|
"{attrkey} "
|
||||||
|
"{cat_locks}\n"
|
||||||
|
" |c=|n {value}".format(
|
||||||
|
attrkey=attrkey,
|
||||||
|
cat_locks=cat_locks,
|
||||||
|
locks=locks if locks else "|wNone|n",
|
||||||
|
value=value))
|
||||||
|
attrs = "|cattrs:|n\n {attrs}".format(attrs="\n ".join(out))
|
||||||
|
tags = prototype.get('tags', '')
|
||||||
|
if tags:
|
||||||
|
out = []
|
||||||
|
for (tagkey, category, data) in tags:
|
||||||
|
out.append("{tagkey} (category: {category}{dat})".format(
|
||||||
|
tagkey=tagkey, category=category, dat=", data: {}".format(data) if data else ""))
|
||||||
|
tags = "|ctags:|n\n {tags}".format(tags="\n ".join(out))
|
||||||
|
locks = prototype.get('locks', '')
|
||||||
|
if locks:
|
||||||
|
locks = "|clocks:|n\n {locks}".format(locks="\n ".join(locks.split(";")))
|
||||||
|
permissions = prototype.get("permissions", '')
|
||||||
|
if permissions:
|
||||||
|
permissions = "|cpermissions:|n {perms}".format(perms=", ".join(permissions))
|
||||||
|
location = prototype.get("location", '')
|
||||||
|
if location:
|
||||||
|
location = "|clocation:|n {location}".format(location=location)
|
||||||
|
home = prototype.get("home", '')
|
||||||
|
if home:
|
||||||
|
home = "|chome:|n {home}".format(home=home)
|
||||||
|
destination = prototype.get("destination", '')
|
||||||
|
if destination:
|
||||||
|
destination = "|cdestination:|n {destination}".format(destination=destination)
|
||||||
|
|
||||||
|
body = "\n".join(part for part in (key, aliases, attrs, tags, locks, permissions,
|
||||||
|
location, home, destination) if part)
|
||||||
|
|
||||||
|
return header.lstrip() + body.strip()
|
||||||
|
|
||||||
|
|
||||||
def check_permission(prototype_key, action, default=True):
|
def check_permission(prototype_key, action, default=True):
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,8 @@ def _get_prototype(dic, prot, protparents):
|
||||||
for infinite recursion here.
|
for infinite recursion here.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# we don't overload the prototype_key
|
||||||
|
prototype_key = prot.get('prototype_key', None)
|
||||||
if "prototype_parent" in dic:
|
if "prototype_parent" in dic:
|
||||||
# move backwards through the inheritance
|
# move backwards through the inheritance
|
||||||
for prototype in make_iter(dic["prototype_parent"]):
|
for prototype in make_iter(dic["prototype_parent"]):
|
||||||
|
|
@ -157,6 +159,7 @@ def _get_prototype(dic, prot, protparents):
|
||||||
new_prot = _get_prototype(protparents.get(prototype.lower(), {}), prot, protparents)
|
new_prot = _get_prototype(protparents.get(prototype.lower(), {}), prot, protparents)
|
||||||
prot.update(new_prot)
|
prot.update(new_prot)
|
||||||
prot.update(dic)
|
prot.update(dic)
|
||||||
|
prot['prototype_key'] = prototype_key
|
||||||
prot.pop("prototype_parent", None) # we don't need this anymore
|
prot.pop("prototype_parent", None) # we don't need this anymore
|
||||||
return prot
|
return prot
|
||||||
|
|
||||||
|
|
@ -217,19 +220,19 @@ def prototype_from_object(obj):
|
||||||
|
|
||||||
location = obj.db_location
|
location = obj.db_location
|
||||||
if location:
|
if location:
|
||||||
prot['location'] = location
|
prot['location'] = location.dbref
|
||||||
home = obj.db_home
|
home = obj.db_home
|
||||||
if home:
|
if home:
|
||||||
prot['home'] = home
|
prot['home'] = home.dbref
|
||||||
destination = obj.db_destination
|
destination = obj.db_destination
|
||||||
if destination:
|
if destination:
|
||||||
prot['destination'] = destination
|
prot['destination'] = destination.dbref
|
||||||
locks = obj.locks.all()
|
locks = obj.locks.all()
|
||||||
if locks:
|
if locks:
|
||||||
prot['locks'] = locks
|
prot['locks'] = ";".join(locks)
|
||||||
perms = obj.permissions.get()
|
perms = obj.permissions.get()
|
||||||
if perms:
|
if perms:
|
||||||
prot['permissions'] = perms
|
prot['permissions'] = make_iter(perms)
|
||||||
aliases = obj.aliases.get()
|
aliases = obj.aliases.get()
|
||||||
if aliases:
|
if aliases:
|
||||||
prot['aliases'] = aliases
|
prot['aliases'] = aliases
|
||||||
|
|
|
||||||
|
|
@ -1055,7 +1055,10 @@ def list_node(option_generator, select=None, pagesize=10):
|
||||||
else:
|
else:
|
||||||
if callable(select):
|
if callable(select):
|
||||||
try:
|
try:
|
||||||
return select(caller, selection)
|
if bool(getargspec(select).keywords):
|
||||||
|
return select(caller, selection, available_choices=available_choices)
|
||||||
|
else:
|
||||||
|
return select(caller, selection)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
elif select:
|
elif select:
|
||||||
|
|
@ -1124,7 +1127,7 @@ def list_node(option_generator, select=None, pagesize=10):
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
else:
|
else:
|
||||||
if isinstance(decorated_options, {}):
|
if isinstance(decorated_options, dict):
|
||||||
decorated_options = [decorated_options]
|
decorated_options = [decorated_options]
|
||||||
else:
|
else:
|
||||||
decorated_options = make_iter(decorated_options)
|
decorated_options = make_iter(decorated_options)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue