def agent_from_entity(self, relation, entity_id):
        """Create a (potentially grounded) INDRA Agent object from a given
        Medscan entity describing the subject or object.

        Uses helper functions to convert a Medscan URN to an INDRA db_refs
        grounding dictionary.

        If the entity has properties indicating that it is a protein with
        a mutation or modification, then constructs the needed ModCondition
        or MutCondition.

        Parameters
        ----------
        relation : MedscanRelation
            The current relation being processed
        entity_id : str
            The ID of the entity to process

        Returns
        -------
        agent : indra.statements.Agent
            A potentially grounded INDRA agent representing this entity
        """
        # Extract sentence tags mapping ids to the text. We refer to this
        # mapping only if the entity doesn't appear in the grounded entity
        # list
        tags = _extract_sentence_tags(relation.tagged_sentence)

        if entity_id is None:
            return None
        self.num_entities += 1

        entity_id = _extract_id(entity_id)

        if entity_id not in relation.entities and \
                entity_id not in tags:
            # Could not find the entity in either the list of grounded
            # entities of the items tagged in the sentence. Happens for
            # a very small percentage of the dataset.
            self.num_entities_not_found += 1
            return None

        if entity_id not in relation.entities:
            # The entity is not in the grounded entity list
            # Instead, make an ungrounded entity, with TEXT corresponding to
            # the words with the given entity id tagged in the sentence.
            entity_data = tags[entity_id]
            db_refs = {'TEXT': entity_data['text']}
            ag = Agent(normalize_medscan_name(db_refs['TEXT']),
                       db_refs=db_refs)
            return ag, entity_data['bounds']
        else:
            entity = relation.entities[entity_id]
            bounds = (entity.ch_start, entity.ch_end)

            prop = entity.properties
            if len(prop.keys()) == 2 and 'Protein' in prop \
               and 'Mutation' in prop:
                # Handle the special case where the entity is a protein
                # with a mutation or modification, with those details
                # described in the entity properties
                protein = prop['Protein']
                assert(len(protein) == 1)
                protein = protein[0]

                mutation = prop['Mutation']
                assert(len(mutation) == 1)
                mutation = mutation[0]

                db_refs, db_name = _urn_to_db_refs(protein.urn)

                if db_refs is None:
                    return None
                db_refs['TEXT'] = protein.name

                if db_name is None:
                    agent_name = db_refs['TEXT']
                else:
                    agent_name = db_name

                # Check mutation.type. Only some types correspond to situations
                # that can be represented in INDRA; return None if we cannot
                # map to an INDRA statement (which will block processing of
                # the statement in process_relation).
                if mutation.type == 'AASite':
                    # Do not handle this
                    # Example:
                    # MedscanEntity(name='D1', urn='urn:agi-aa:D1',
                    # type='AASite', properties=None)
                    return None
                elif mutation.type == 'Mutation':
                    # Convert mutation properties to an INDRA MutCondition
                    r_old, pos, r_new = _parse_mut_string(mutation.name)
                    if r_old is None:
                        logger.warning('Could not parse mutation string: ' +
                                       mutation.name)
                        # Don't create an agent
                        return None
                    else:
                        try:
                            cond = MutCondition(pos, r_old, r_new)
                            ag = Agent(normalize_medscan_name(agent_name),
                                       db_refs=db_refs, mutations=[cond])
                            return ag, bounds
                        except BaseException:
                            logger.warning('Could not parse mutation ' +
                                           'string: ' + mutation.name)
                            return None
                elif mutation.type == 'MethSite':
                    # Convert methylation site information to an INDRA
                    # ModCondition
                    res, pos = _parse_mod_string(mutation.name)
                    if res is None:
                        return None
                    cond = ModCondition('methylation', res, pos)
                    ag = Agent(normalize_medscan_name(agent_name),
                               db_refs=db_refs, mods=[cond])
                    return ag, bounds

                    # Example:
                    # MedscanEntity(name='R457',
                    # urn='urn:agi-s-llid:R457-2185', type='MethSite',
                    # properties=None)
                elif mutation.type == 'PhosphoSite':
                    # Convert phosphorylation site information to an INDRA
                    # ModCondition
                    res, pos = _parse_mod_string(mutation.name)
                    if res is None:
                        return None
                    cond = ModCondition('phosphorylation', res, pos)
                    ag = Agent(normalize_medscan_name(agent_name),
                               db_refs=db_refs, mods=[cond])
                    return ag, bounds

                    # Example:
                    # MedscanEntity(name='S455',
                    # urn='urn:agi-s-llid:S455-47', type='PhosphoSite',
                    # properties=None)
                    pass
                elif mutation.type == 'Lysine':
                    # Ambiguous whether this is a methylation or
                    # demethylation; skip

                    # Example:
                    # MedscanEntity(name='K150',
                    # urn='urn:agi-s-llid:K150-5624', type='Lysine',
                    # properties=None)
                    return None
                else:
                    logger.warning('Processor currently cannot process ' +
                                   'mutations of type ' + mutation.type)
            else:
                # Handle the more common case where we just ground the entity
                # without mutation or modification information
                db_refs, db_name = _urn_to_db_refs(entity.urn)
                if db_refs is None:
                    return None
                db_refs['TEXT'] = entity.name

                if db_name is None:
                    agent_name = db_refs['TEXT']
                else:
                    agent_name = db_name

                ag = Agent(normalize_medscan_name(agent_name),
                           db_refs=db_refs)
                return ag, bounds
Create a (potentially grounded) INDRA Agent object from a given
        Medscan entity describing the subject or object.

        Uses helper functions to convert a Medscan URN to an INDRA db_refs
        grounding dictionary.

        If the entity has properties indicating that it is a protein with
        a mutation or modification, then constructs the needed ModCondition
        or MutCondition.

        Parameters
        ----------
        relation : MedscanRelation
            The current relation being processed
        entity_id : str
            The ID of the entity to process

        Returns
        -------
        agent : indra.statements.Agent
            A potentially grounded INDRA agent representing this entity