Source code for ECAgent.Tags

"""Tags Module for ECAgent
==========================

This library contains all "Tag" functionality. In ECAgent, a tag is a unique ``(int,str)`` pair that is used to
associate similar agents together. Tags are not always useful, but they shine when a model has multiple agent types that
share a lot of similar components (i.e. Where component template matching doesn't work). Tags may also be useful
when building containers of similar agent types.

Tags are stored in a ``TagLibrary``. By default a global ``TagLibrary`` exists that can be accessed  directly through
the  ``Tags`` module.

To add a tag to the global ``TagLibrary``, do the following::

    import ECAgent.Tags as Tags

    Tags.add_tag("Tag1")

This will register ``Tag1`` with the ``TagLibrary``. ``Tag1`` will then be assigned a unique integer value which can
be accessed as follows::

    Tags.Tag1

If you want to get the ``str`` representation of a tag::

    Tags.get_tag_name(Tags.Tag1)

Which will return ``"Tag1"``.

Creating a local TagLibrary
---------------------------

If you do not want to use the global TagLibrary to store tags, you can create a local TagLibrary by doing the
following::

    import ECAgent.Tags as Tags

    local_tags = Tags.TagLibrary()

You will then be able to use all of the functionality discussed previously. To add a tag::

    local_tags.add_tag("Tag1")

To get the unique integer value of a Tag::

    local_tags.Tag1

To get the ``str`` representation of a Tag::

    local_tags.get_tag_name(local_tags.Tag1)

Tags and Agents
---------------

Tags are not particularly useful by themselves. To assign a tag to an ``Agent``, you can either do it at
initialization::

    import ECAgent.Core as Core
    import ECAgent.Tags as Tags

    Tags.add_tag('PREY')

    model = Core.model()
    p1 = Core.Agent('p1', model, tag = Tags.PREY)
    model.environment.add_agent(p1)

Or you can assign it post-initialization::

    p1.tag = Tags.PREY

To get a list of all agents with a particular tag, use the ``Environment.get_agents`` method. Specifying the tag as
follows::

    prey = environment.getAgents(tag = Tags.PREY)
"""


[docs]class TagLibrary: """Tag Library Class. It stores a list of agent tags and their unique identifiers. Attributes ---------- NONE : int The default tag for all agents. It always has a value of 0. """ def __init__(self): self.NONE = 0 self._tag_counter = 1 self._tag_names = ['NONE'] def __len__(self) -> int: """Returns the number of tags stored in the ``TagLibrary`` *Note* that the ``NONE`` tag is included so the value returned will be least 1. Returns ------- int The number of tags in the ``TagLibrary`` """ return self._tag_counter
[docs] def add_tag(self, tag_name: str): """Adds a tag to the ``TagLibrary`` with the name ``tag_name``. Parameters ---------- tag_name : str The name of the tag being created. Raises ------ DuplicateTagError If a tag_name that already exists is used. """ # Check for duplicates if tag_name in self.__dict__: raise DuplicateTagError(tag_name) else: self.__dict__[tag_name] = self._tag_counter self._tag_counter += 1 self._tag_names.append(tag_name)
[docs] def get_tag_name(self, tag_id: int) -> str: """Returns a ``str`` representation of a tag. Parameters ---------- tag_id : int The id of the tag. Returns ------- str The ``str`` representation of the tag. Raises ------ TagNotFoundError If the tag cannot be found. """ if tag_id < 0 or tag_id > self._tag_counter - 1: raise TagNotFoundError(tag_id) else: return self._tag_names[tag_id]
[docs] def itemize(self) -> list: """Returns a ``list`` of tags in the ``TagLibrary`` and their values. Returns ------- list In the following form: ``[('NONE', 0), ('TAG1', 1),..., ('TAGN', N)]`` """ return [(key, val) for val, key in enumerate(self._tag_names)]
[docs]class DuplicateTagError(Exception): """Exception raised when a duplicate Tag is created. Attributes: ----------- tag_name : str The name of tag created. message : str Explanation of error. """ def __init__(self, tag_name: str): """ Parameters ---------- tag_name : str The name of tag created. """ self.tag_name = tag_name self.message = f'Tag with name "{tag_name}" already exists.' super(DuplicateTagError, self).__init__(self.message)
[docs]class TagNotFoundError(Exception): """Exception raised when a Tag can't be found. Attributes: ----------- tag_id : int | str The name of searched tag. message : str Explanation of error. """ def __init__(self, tag_id): """ Parameters ---------- tag_id : int | str The id of searched tag. """ self.tag_id = tag_id self.message = f'Tag with id "{tag_id}" does not exist.' super(TagNotFoundError, self).__init__(self.message)
_module_library = TagLibrary()
[docs]def add_tag(tag_name: str): """Adds a tag to the global ``TagLibrary`` with the name ``tag_name``. Parameters ---------- tag_name : str The name of the tag being created. Raises ------ DuplicateTagError If a tag_name that already exists is used. """ _module_library.add_tag(tag_name)
[docs]def get_tag_name(tag_id: int) -> str: """Returns a ``str`` representation of a tag. The tag is searched for in the global ``TagLibrary`` Parameters ---------- tag_id : int The id of the tag. Returns ------- str The ``str`` representation of the tag. Raises ------ TagNotFoundError If the tag cannot be found. """ return _module_library.get_tag_name(tag_id)
def __getattr__(tag_name: str) -> int: """Returns the value of a tag with a matching ``tag_name`` in the global ``TagLibrary`` ---------- int The unique id for a tag with a ``name == tag_name``. Raises ------ TagNotFoundError If tag does not exist. """ if tag_name not in _module_library.__dict__: raise TagNotFoundError(tag_name) else: return _module_library.__dict__[tag_name]
[docs]def itemize() -> list: """Returns a ``list`` of tags in the global ``TagLibrary`` and their values. Returns ------- list In the following form: ``[('NONE', 0), ('TAG1', 1),..., ('TAGN', N)]`` """ return _module_library.itemize()