Add tag handling in old menu

This commit is contained in:
Griatch 2018-07-27 13:34:20 +02:00
parent 50c54501f1
commit e210e87490
2 changed files with 123 additions and 77 deletions

View file

@ -863,7 +863,7 @@ def _add_attr(caller, attr_string, **kwargs):
locks = '' locks = ''
if 'delete' in kwargs: if 'delete' in kwargs:
attrname = attr_string attrname = attr_string.lower().strip()
elif '=' in attr_string: elif '=' in attr_string:
attrname, value = (part.strip() for part in attr_string.split('=', 1)) attrname, value = (part.strip() for part in attr_string.split('=', 1))
attrname = attrname.lower() attrname = attrname.lower()
@ -892,17 +892,12 @@ def _add_attr(caller, attr_string, **kwargs):
# replace existing attribute with the same name in the prototype # replace existing attribute with the same name in the prototype
ind = [tup[0] for tup in attrs].index(attrname) ind = [tup[0] for tup in attrs].index(attrname)
attrs[ind] = attr_tuple attrs[ind] = attr_tuple
text = "Edited Attribute '{}'.".format(attrname)
except ValueError: except ValueError:
attrs.append(attr_tuple) attrs.append(attr_tuple)
text = "Added Attribute " + _display_attribute(attr_tuple)
_set_prototype_value(caller, "attrs", attrs) _set_prototype_value(caller, "attrs", attrs)
text = kwargs.get('text')
if not text:
if 'edit' in kwargs:
text = "Edited " + _display_attribute(attr_tuple)
else:
text = "Added " + _display_attribute(attr_tuple)
else: else:
text = "Attribute must be given as 'attrname[;category;locks] = <value>'." text = "Attribute must be given as 'attrname[;category;locks] = <value>'."
@ -929,7 +924,12 @@ def _attrs_actions(caller, raw_inp, **kwargs):
raw_inp, choices, ('examine', 'e'), ('remove', 'r', 'delete', 'd')) raw_inp, choices, ('examine', 'e'), ('remove', 'r', 'delete', 'd'))
if attrstr is None: if attrstr is None:
attrstr = raw_inp attrstr = raw_inp
attrname, _ = attrstr.split("=", 1) try:
attrname, _ = attrstr.split("=", 1)
except ValueError:
caller.msg("|rNeed to enter the attribute on the form attrname=value.|n")
return "node_attrs"
attrname = attrname.strip() attrname = attrname.strip()
attr_tup = _get_tup_by_attrname(caller, attrname) attr_tup = _get_tup_by_attrname(caller, attrname)
@ -938,7 +938,7 @@ def _attrs_actions(caller, raw_inp, **kwargs):
return "node_examine_entity", \ return "node_examine_entity", \
{"text": _display_attribute(attr_tup), "back": "attrs"} {"text": _display_attribute(attr_tup), "back": "attrs"}
elif action == 'remove': elif action == 'remove':
res = _add_attr(caller, attr_tup, delete=True) res = _add_attr(caller, attrname, delete=True)
caller.msg(res) caller.msg(res)
else: else:
res = _add_attr(caller, raw_inp) res = _add_attr(caller, raw_inp)
@ -964,9 +964,9 @@ def node_attrs(caller):
helptext = """ helptext = """
Most commonly, Attributes don't need any categories or locks. If using locks, the lock-types Most commonly, Attributes don't need any categories or locks. If using locks, the lock-types
'attredit', 'attrread' are used to limiting editing and viewing of the Attribute. Putting 'attredit' and 'attrread' are used to limit editing and viewing of the Attribute. Putting
the lock-type `attrcreate` in the |clocks|n prototype key can be used to restrict builders the lock-type `attrcreate` in the |clocks|n prototype key can be used to restrict builders
to add new Attributes. from adding new Attributes.
|c$protfuncs |c$protfuncs
@ -986,83 +986,128 @@ def node_attrs(caller):
def _caller_tags(caller): def _caller_tags(caller):
prototype = _get_menu_prototype(caller) prototype = _get_menu_prototype(caller)
tags = prototype.get("tags", []) tags = [tup[0] for tup in prototype.get("tags", [])]
return tags return tags
def _get_tup_by_tagname(caller, tagname):
prototype = _get_menu_prototype(caller)
tags = prototype.get("tags", [])
try:
inp = [tup[0] for tup in tags].index(tagname)
return tags[inp]
except ValueError:
return None
def _display_tag(tag_tuple): def _display_tag(tag_tuple):
"""Pretty-print attribute tuple""" """Pretty-print tag tuple"""
tagkey, category, data = tag_tuple tagkey, category, data = tag_tuple
out = ("Tag: '{tagkey}' (category: {category}{dat})".format( out = ("Tag: '{tagkey}' (category: {category}{dat})".format(
tagkey=tagkey, category=category, dat=", data: {}".format(data) if data else "")) tagkey=tagkey, category=category, dat=", data: {}".format(data) if data else ""))
return out return out
def _add_tag(caller, tag, **kwargs): def _add_tag(caller, tag_string, **kwargs):
""" """
Add tags to the system, parsing this syntax: Add tags to the system, parsing input
tagname
tagname;category Args:
tagname;category;data caller (Object): Caller of menu.
tag_string (str): Input from user on one of these forms
tagname
tagname;category
tagname;category;data
Kwargs:
delete (str): If this is set, tag_string is considered
the name of the tag to delete.
Returns:
result (str): Result string of action.
""" """
tag = tag_string.strip().lower()
tag = tag.strip().lower()
category = None category = None
data = "" data = ""
tagtuple = tag.split(";", 2) if 'delete' in kwargs:
ntuple = len(tagtuple) tag = tag_string.lower().strip()
else:
nameparts = tag.split(";", 2)
ntuple = len(nameparts)
if ntuple == 2:
tag, category = nameparts
elif ntuple > 2:
tag, category, data = nameparts[:3]
if ntuple == 2: tag_tuple = (tag.lower(), category.lower() if category else None, data)
tag, category = tagtuple
elif ntuple > 2:
tag, category, data = tagtuple
tag_tuple = (tag, category, data)
if tag: if tag:
prot = _get_menu_prototype(caller) prot = _get_menu_prototype(caller)
tags = prot.get('tags', []) tags = prot.get('tags', [])
old_tag = kwargs.get("edit", None) old_tag = _get_tup_by_tagname(caller, tag)
if not old_tag: if 'delete' in kwargs:
if old_tag:
tags.pop(tags.index(old_tag))
text = "Removed tag '{}'".format(tag)
else:
text = "Found no tag to remove."
elif not old_tag:
# a fresh, new tag # a fresh, new tag
tags.append(tag_tuple) tags.append(tag_tuple)
text = "Added Tag '{}'".format(tag)
else: else:
# old tag exists; editing a tag means removing the old and replacing with new # old tag exists; editing a tag means replacing old with new
try: ind = tags.index(old_tag)
ind = [tup[0] for tup in tags].index(old_tag) tags[ind] = tag_tuple
del tags[ind] text = "Edited Tag '{}'".format(tag)
if tags:
tags.insert(ind, tag_tuple)
else:
tags = [tag_tuple]
except IndexError:
pass
_set_prototype_value(caller, "tags", tags) _set_prototype_value(caller, "tags", tags)
text = kwargs.get('text')
if not text:
if 'edit' in kwargs:
text = "Edited " + _display_tag(tag_tuple)
else:
text = "Added " + _display_tag(tag_tuple)
else: else:
text = "Tag must be given as 'tag[;category;data]." text = "Tag must be given as 'tag[;category;data]'."
options = {"key": "_default", return text
"goto": lambda caller: None}
return text, options
def _edit_tag(caller, old_tag, new_tag, **kwargs): def _tag_select(caller, tagname):
return _add_tag(caller, new_tag, edit=old_tag) tag_tup = _get_tup_by_tagname(caller, tagname)
if tag_tup:
return "node_examine_entity", \
{"text": _display_tag(tag_tup), "back": "attrs"}
else:
caller.msg("Tag not found.")
return "node_attrs"
@list_node(_caller_tags) def _tags_actions(caller, raw_inp, **kwargs):
"""Parse actions for tags listing"""
choices = kwargs.get("available_choices", [])
tagname, action = _default_parse(
raw_inp, choices, ('examine', 'e'), ('remove', 'r', 'delete', 'd'))
if tagname is None:
tagname = raw_inp.lower().strip()
tag_tup = _get_tup_by_tagname(caller, tagname)
if tag_tup:
if action == 'examine':
return "node_examine_entity", \
{"text": _display_tag(tag_tup), 'back': 'tags'}
elif action == 'remove':
res = _add_tag(caller, tagname, delete=True)
caller.msg(res)
else:
res = _add_tag(caller, raw_inp)
caller.msg(res)
return "node_tags"
@list_node(_caller_tags, _tag_select)
def node_tags(caller): def node_tags(caller):
text = """ text = """
|cTags|n are used to group objects so they can quickly be found later. Enter tags on one of |cTags|n are used to group objects so they can quickly be found later. Enter tags on one of
@ -1071,8 +1116,8 @@ def node_tags(caller):
tagname;category tagname;category
tagname;category;data tagname;category;data
{current} {actions}
""".format(current=_get_current_value(caller, 'tags')) """.format(actions=_format_list_actions("examine", "remove", prefix="Actions: "))
helptext = """ helptext = """
Tags are shared between all objects with that tag. So the 'data' field (which is not Tags are shared between all objects with that tag. So the 'data' field (which is not
@ -1082,10 +1127,12 @@ def node_tags(caller):
All objects created with this prototype will automatically get assigned a tag named the same All objects created with this prototype will automatically get assigned a tag named the same
as the |cprototype_key|n and with a category "{tag_category}". This allows the spawner to as the |cprototype_key|n and with a category "{tag_category}". This allows the spawner to
optionally update previously spawned objects when their prototype changes. optionally update previously spawned objects when their prototype changes.
""".format(protlib._PROTOTYPE_TAG_CATEGORY) """.format(tag_category=protlib._PROTOTYPE_TAG_CATEGORY)
text = (text, helptext) text = (text, helptext)
options = _wizard_options("tags", "attrs", "locks") options = _wizard_options("tags", "attrs", "locks")
options.append({"key": "_default",
"goto": _tags_actions})
return text, options return text, options

View file

@ -427,8 +427,6 @@ class TestMenuModule(EvenniaTest):
with mock.patch("evennia.utils.utils.get_all_typeclasses", with mock.patch("evennia.utils.utils.get_all_typeclasses",
new=mock.MagicMock(return_value={"foo": None, "bar": None})): new=mock.MagicMock(return_value={"foo": None, "bar": None})):
self.assertEqual(olc_menus._all_typeclasses(caller), ["bar", "foo"]) self.assertEqual(olc_menus._all_typeclasses(caller), ["bar", "foo"])
self.assertTrue(olc_menus._typeclass_examine(
caller, "evennia.objects.objects.DefaultObject").startswith("Typeclass |y"))
self.assertEqual(olc_menus._typeclass_select( self.assertEqual(olc_menus._typeclass_select(
caller, "evennia.objects.objects.DefaultObject"), "node_key") caller, "evennia.objects.objects.DefaultObject"), "node_key")
@ -441,34 +439,35 @@ class TestMenuModule(EvenniaTest):
# attr helpers # attr helpers
self.assertEqual(olc_menus._caller_attrs(caller), []) self.assertEqual(olc_menus._caller_attrs(caller), [])
self.assertEqual(olc_menus._add_attr(caller, "test1=foo1"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_attr(caller, "test1=foo1"), Something)
self.assertEqual(olc_menus._add_attr(caller, "test2;cat1=foo2"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_attr(caller, "test2;cat1=foo2"), Something)
self.assertEqual(olc_menus._add_attr(caller, "test3;cat2;edit:false()=foo3"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_attr(caller, "test3;cat2;edit:false()=foo3"), Something)
self.assertEqual(olc_menus._add_attr(caller, "test4;cat3;set:true();edit:false()=foo4"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_attr(caller, "test4;cat3;set:true();edit:false()=foo4"), Something)
self.assertEqual(olc_menus._add_attr(caller, "test5;cat4;set:true();edit:false()=123"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_attr(caller, "test5;cat4;set:true();edit:false()=123"), Something)
self.assertEqual(olc_menus._caller_attrs( self.assertEqual(olc_menus._get_menu_prototype(caller)['attrs'],
caller),
[("test1", "foo1", None, ''), [("test1", "foo1", None, ''),
("test2", "foo2", "cat1", ''), ("test2", "foo2", "cat1", ''),
("test3", "foo3", "cat2", "edit:false()"), ("test3", "foo3", "cat2", "edit:false()"),
("test4", "foo4", "cat3", "set:true();edit:false()"), ("test4", "foo4", "cat3", "set:true();edit:false()"),
("test5", '123', "cat4", "set:true();edit:false()")]) ("test5", '123', "cat4", "set:true();edit:false()")])
self.assertEqual(olc_menus._edit_attr(caller, "test1", "1;cat5;edit:all()"), (Something, {"key": "_default", "goto": Something}))
self.assertEqual(olc_menus._examine_attr(caller, "test1"), Something)
# tag helpers # tag helpers
self.assertEqual(olc_menus._caller_tags(caller), []) self.assertEqual(olc_menus._caller_tags(caller), [])
self.assertEqual(olc_menus._add_tag(caller, "foo1"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_tag(caller, "foo1"), Something)
self.assertEqual(olc_menus._add_tag(caller, "foo2;cat1"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_tag(caller, "foo2;cat1"), Something)
self.assertEqual(olc_menus._add_tag(caller, "foo3;cat2;dat1"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_tag(caller, "foo3;cat2;dat1"), Something)
self.assertEqual(olc_menus._caller_tags( self.assertEqual(olc_menus._caller_tags(caller), ['foo1', 'foo2', 'foo3'])
caller), self.assertEqual(olc_menus._get_menu_prototype(caller)['tags'],
[('foo1', None, ""), [('foo1', None, ""),
('foo2', 'cat1', ""), ('foo2', 'cat1', ""),
('foo3', 'cat2', "dat1")]) ('foo3', 'cat2', "dat1")])
self.assertEqual(olc_menus._edit_tag(caller, "foo1", "bar1;cat1"), (Something, {"key": "_default", "goto": Something})) self.assertEqual(olc_menus._add_tag(caller, "foo1", delete=True), "Removed tag 'foo1'")
self.assertEqual(olc_menus._display_tag(olc_menus._caller_tags(caller)[0]), Something) self.assertEqual(olc_menus._get_menu_prototype(caller)['tags'],
self.assertEqual(olc_menus._caller_tags(caller)[0], ("bar1", "cat1", "")) [('foo2', 'cat1', ""),
('foo3', 'cat2', "dat1")])
self.assertEqual(olc_menus._display_tag(olc_menus._get_menu_prototype(caller)['tags'][0]), Something)
self.assertEqual(olc_menus._caller_tags(caller), ["foo2", "foo3"])
protlib.save_prototype(**self.test_prot) protlib.save_prototype(**self.test_prot)