Takes an `annotation` dictionary which is
        expected to have a string as the value of annotation['author']['name']
        This function will remove all annotations from obj that:
            1. have the same author/name, and
            2. have no messages that are flagged as messages to be preserved (values for 'preserve'
                that evaluate to true)

def add_or_replace_annotation(self,  # pylint: disable=R0201
                                  obj,
                                  annotation,
                                  agent,
                                  add_agent_only=False):
        """Takes an `annotation` dictionary which is
        expected to have a string as the value of annotation['author']['name']
        This function will remove all annotations from obj that:
            1. have the same author/name, and
            2. have no messages that are flagged as messages to be preserved (values for 'preserve'
                that evaluate to true)
        """
        nex = get_nexml_el(obj)
        nvers = detect_nexson_version(obj)
        _LOG.debug('detected version as ' + nvers)
        agents_obj = find_val_literal_meta_first(nex, 'ot:agents', nvers)
        if not agents_obj:
            agents_obj = add_literal_meta(nex, 'ot:agents', {'agent': []}, nvers)
        agents_list = agents_obj.setdefault('agent', [])
        found_agent = False
        aid = agent['@id']
        for a in agents_list:
            if a.get('@id') == aid:
                found_agent = True
                break
        if not found_agent:
            agents_list.append(agent)
        if add_agent_only:
            delete_same_agent_annotation(obj, annotation)
        else:
            replace_same_agent_annotation(obj, annotation)