From 88d103b55f1b3a49fb52a48690ee433af2579a3c Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 2 Dec 2013 15:40:02 +0100 Subject: [PATCH] Added @tag command for easily manipulating tags. Fixed some bugs at the same time. --- src/commands/default/building.py | 96 +++++++++++++++++++++++- src/commands/default/cmdset_character.py | 1 + src/typeclasses/managers.py | 11 ++- src/typeclasses/models.py | 3 +- 4 files changed, 105 insertions(+), 6 deletions(-) diff --git a/src/commands/default/building.py b/src/commands/default/building.py index f814877c58..f10d6946fd 100644 --- a/src/commands/default/building.py +++ b/src/commands/default/building.py @@ -5,7 +5,7 @@ Building and world design commands """ from django.conf import settings from src.objects.models import ObjectDB -from src.utils import create, utils +from src.utils import create, utils, search from src.utils.ansi import raw from src.commands.default.muxcommand import MuxCommand from src.commands.cmdhandler import get_and_merge_cmdsets @@ -17,7 +17,7 @@ __all__ = ("ObjManipCommand", "CmdSetObjAlias", "CmdCopy", "CmdUnLink", "CmdSetHome", "CmdListCmdSets", "CmdName", "CmdOpen", "CmdSetAttribute", "CmdTypeclass", "CmdWipe", "CmdLock", "CmdExamine", "CmdFind", "CmdTeleport", - "CmdScript") + "CmdScript", "CmdTag") try: # used by @set @@ -2098,3 +2098,95 @@ class CmdScript(MuxCommand): string = "Script started successfully." break caller.msg(string.strip()) + + +class CmdTag(MuxCommand): + """ + handles tagging + + Usage: + @tag[/del] [= [:]] + @tag/search + + Switches: + search - return all objects + del - remove the given tag. If no tag is specified, + clear all tags. + + Manipulates and lists tags on objects. Tags allow for quick + grouping of and searching for objects. If only is given, + list all tags on the object. If /search is used, list objects + with the given tag. + The category can be used for grouping tags themselves. + """ + + key = "@tag" + locks = "cmd:perm(tag) or perm(Builders)" + help_category = "Building" + + def func(self): + "Implement the @tag functionality" + + if not self.args: + self.caller.msg("Usage: @tag[/switches] [|[=[]]") + return + if "search" in self.switches: + # search by tag + objs = search.search_tag(self.args) + nobjs = len(objs) + if nobjs > 0: + string = "Found %i object%s with tag '%s':\n %s" % (nobjs, + "s" if nobjs > 1 else "", + self.args, + ", ".join(o.key for o in objs)) + else: + string = "No objects found with tag %s." % self.args + self.caller.msg(string) + return + if "del" in self.switches: + # remove one or all tags + obj = self.caller.search(self.lhs, global_search=True) + if not obj: + return + if self.rhs: + # remove individual tag + tag = self.rhs + category = None + if ":" in tag: + tag, category = [part.strip() for part in tag.split(":", 1)] + obj.tags.remove(tag, category=category) + string = "Removed tag '%s' from %s (if it existed)" % (tag, obj) + else: + # no tag specified, clear all tags + obj.tags.clear() + string = "Cleared all tags from from %s." % obj + self.caller.msg(string) + return + # no search/deletion + if self.rhs: + # = is found, so we are on the form obj = tag + obj = self.caller.search(self.lhs, global_search=True) + if not obj: + return + tag = self.rhs + category = None + if ":" in tag: + tag, category = [part.strip() for part in tag.split(":", 1)] + # create the tag + obj.tags.add(tag, category=category) + string = "Added tag '%s' to %s." % (tag, obj) + self.caller.msg(string) + else: + # no = found - list tags on object + obj = self.caller.search(self.args, global_search=True) + if not obj: + return + tags = obj.tags.all() + ntags = len(tags) + if ntags: + string = "Tag%s on %s: %s" % ("s" if ntags > 1 else "", + obj, + ", ".join("'%s'" % tag for tag in obj.tags.all())) + else: + string = "No tags attached to %s." % obj + self.caller.msg(string) diff --git a/src/commands/default/cmdset_character.py b/src/commands/default/cmdset_character.py index 0a4723f349..35292bdd8b 100644 --- a/src/commands/default/cmdset_character.py +++ b/src/commands/default/cmdset_character.py @@ -79,6 +79,7 @@ class CharacterCmdSet(CmdSet): self.add(building.CmdLock()) self.add(building.CmdScript()) self.add(building.CmdSetHome()) + self.add(building.CmdTag()) # Batchprocessor commands self.add(batchprocess.CmdBatchCommands()) diff --git a/src/typeclasses/managers.py b/src/typeclasses/managers.py index 004d0351f3..c1a4f47236 100644 --- a/src/typeclasses/managers.py +++ b/src/typeclasses/managers.py @@ -12,7 +12,7 @@ from src.utils.dbserialize import to_pickle __all__ = ("AttributeManager", "TypedObjectManager") _GA = object.__getattribute__ - +_ObjectDB = None # Managers @@ -131,14 +131,19 @@ class TagManager(models.Manager): else: return list(tags) - def get_objs_with_tag(self, objclass, key=None, category=None): + def get_objs_with_tag(self, key=None, category=None, objclass=None): """ Search and return all objects of objclass that has tags matching the given search criteria. - objclass (dbmodel) - the object class to search key (string) - the tag identifier category (string) - the tag category + objclass (dbmodel) - the object class to search. If not given, use ObjectDB. """ + global _ObjectDB + if not objclass: + if not _ObjectDB: + from src.objects.models import ObjectDB as _ObjectDB + objclass = _ObjectDB key_cands = Q(db_tags__db_key__iexact=key.lower().strip()) if key is not None else Q() cat_cands = Q(db_tags__db_category__iexact=category.lower().strip()) if category is not None else Q() return objclass.objects.filter(key_cands & cat_cands) diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index d77c3c411e..551b7b28ab 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -551,7 +551,8 @@ class TagHandler(object): def clear(self): "Remove all tags from the handler" - _GA(self.obj, self._m2m_fieldname).filter(db_category__startswith=self.prefix).clear() + for tag in _GA(self.obj, self._m2m_fieldname).filter(db_category__startswith=self.prefix): + _GA(self.obj, self._m2m_fieldname).remove(tag) self._recache() def all(self):