def test_update_fusion_with_composite_annotation_in_replacement(state):
    state.change(+F.parse('gene.A')**F.parse('gene.B'))
    state.change(
        F.parse('gene.B') > CompositeAnnotation(F.parse('gene.C'),
                                                F.parse('gene.D')))

    assert state.changes == (+F.parse('gene.A')**CompositeAnnotation(
        F.parse('gene.C'), F.parse('gene.D')), )
def change_annotation(annotation, site=None, replacement=None):
    """
    Apply a change to an annotation.

    - If ``site`` is specified, looks for ``site`` and replaces with ``replacement``.
    - If ``site`` is not specified, create a :class:`CompositeAnnotation` of ``annotation`` and ``replacement``.
    """
    if not (site or replacement):
        raise ValueError()

    if site:
        if isinstance(annotation, (Fusion, CompositeAnnotation, Plasmid)):
            if not annotation.contains(site):
                if replacement:
                    return CompositeAnnotation(annotation, replacement)
                return annotation
            elif isinstance(annotation, (CompositeAnnotation, Plasmid)) \
                    or isinstance(site, (Feature, CompositeAnnotation)):
                if replacement is None:
                    annotations = tuple(b for b in annotation if b != site)
                else:
                    annotations = tuple(replacement if b == site else b
                                        for b in annotation
                                        if b != replacement)

                if isinstance(annotation, Fusion):
                    return Fusion.fuse(annotations)
                return CompositeAnnotation(*annotations)
            elif isinstance(site, Fusion):
                before_index = annotation.index(site)
                upstream = annotation.annotations[:before_index]
                downstream = annotation.annotations[before_index + len(site):]

                if replacement is None:
                    return Fusion.fuse(upstream + downstream)
                else:
                    return Fusion.fuse(upstream + (replacement, ) + downstream)
            else:
                raise NotImplementedError()
        elif isinstance(annotation, Feature):
            if annotation == site:
                return replacement
            elif replacement:
                return CompositeAnnotation(annotation, replacement)
            return annotation
        else:
            raise NotImplementedError()

    elif isinstance(annotation, (Feature, Fusion)):
        return CompositeAnnotation(annotation, replacement)
    elif isinstance(annotation, (CompositeAnnotation, Plasmid)):
        if any(a == replacement for a in annotation):  # ignore duplicates
            return annotation
        return CompositeAnnotation(annotation, replacement)
    else:
        raise NotImplementedError()
def test_parse_composite_annotation(parse):
    assert [
        Change(after=CompositeAnnotation(Feature('geneA'), Feature('geneB')))
    ] == parse('+{geneA, geneB}')
    assert [
        Change(before=CompositeAnnotation(Feature('geneA'), Feature('geneB')))
    ] == parse('-{geneA, geneB}')

    assert [
        Change(before=Feature('geneX'),
               after=Fusion(
                   CompositeAnnotation(Feature('geneA'), Feature('geneB')),
                   Feature('geneX')))
    ] == parse('geneX>{geneA, geneB}:geneX')
def test_added_fusion_features():
    genotype = Genotype.parse(
        '+geneA -geneB:geneC +geneB:geneC +{geneA, geneB}')
    assert genotype.added_fusion_features == {
        Feature('geneA'),
        CompositeAnnotation(Feature('geneA'), Feature('geneB'))
    }
def test_integrate_plasmid_with_fusion(state):
    state.change(
        F('site') > Plasmid('pA', [F.parse('gene.A')**F.parse('gene.B')]))

    assert state.changes == (F('site') > Plasmid(
        'pA', [F.parse('gene.A')**F.parse('gene.B')]), )

    state.change(F.parse('gene.A')**F.parse('gene.B') > F.parse('gene.C'))
    assert state.changes == (F('site') > CompositeAnnotation(
        F.parse('gene.C')), )
def test_replace_feature_in_composite_annotation():
    assert change_annotation(CompositeAnnotation(F('a'), F('b')), F('a'), None) == CompositeAnnotation(F('b'))

    assert change_annotation(CompositeAnnotation(F('b')), F('a'), None) == CompositeAnnotation(F('b'))

    assert change_annotation(CompositeAnnotation(F('a') ** F('b'),
                                                 F('c')),
                             F('a') ** F('b'),
                             F('d')) == CompositeAnnotation(F('d'), F('c'))

    assert change_annotation(CompositeAnnotation(F('a') ** F('b'),
                                                 F('c')),
                             F('a') ** F('b'),
                             F('c')) == CompositeAnnotation(F('c'))
def test_insert_feature_in_fusion():
    assert change_annotation(F('a') ** F('b'), None, F('x')) == CompositeAnnotation(F('a') ** F('b'), F('x'))
 def COMPOSITE_ANNOTATION(self, ast):
     return CompositeAnnotation(*ast)