Esempio n. 1
0
 def newDocument(self, fname=None):
     """Creates a new Document, reusing the DocumentController.
     Tells all of the views to reset and removes all items from
     them
     """
     if fname is not None and self.fileName() == fname:
         setReopen(True)
     self._document.makeNew()
     self._has_no_associated_file = fname is None
     self.win.setWindowTitle(self.documentTitle() + '[*]')
Esempio n. 2
0
 def newDocument(self, fname=None):
     """Creates a new Document, reusing the DocumentController.
     Tells all of the views to reset and removes all items from
     them
     """
     if fname is not None and self.fileName() == fname:
         setReopen(True)
     self._document.makeNew()
     self._has_no_associated_file = fname is None
     self.win.setWindowTitle(self.documentTitle() + '[*]')
Esempio n. 3
0
 def newDocument(self, doc=None, fname=None):
     """Creates a new Document, reusing the DocumentController."""
     if fname is not None and self._filename == fname:
         setReopen(True)
     self._document.resetViews()
     setBatch(True)
     self._document.removeAllParts()  # clear out old parts
     setBatch(False)
     self._document.undoStack().clear()  # reset undostack
     self._filename = fname if fname else "untitled.json"
     self._has_no_associated_file = fname == None
     self._active_part = None
     self.win.setWindowTitle(self.documentTitle() + '[*]')
Esempio n. 4
0
 def newDocument(self, doc=None, fname=None):
     """Creates a new Document, reusing the DocumentController."""
     if fname is not None and self._filename == fname:
         setReopen(True)
     self._document.resetViews()
     setBatch(True)
     self._document.removeAllParts()  # clear out old parts
     setBatch(False)
     self._document.undoStack().clear()  # reset undostack
     self._filename = fname if fname else "untitled.json"
     self._has_no_associated_file = fname == None
     self._active_part = None
     self.win.setWindowTitle(self.documentTitle() + '[*]')
Esempio n. 5
0
def decode(document: DocT, obj: dict, emit_signals: bool = True):
    """Parses a dictionary (obj) created from reading a json file and uses it
    to populate the given document with model data.

    Args:
        document:
        obj:
        emit_signals: whether to signal views

    Raises:
        IOError, AssertionError, TypeError
    """
    num_bases = len(obj['vstrands'][0]['scaf'])
    if num_bases % 32 == 0:
        lattice_type = LatticeEnum.SQUARE
        grid_type = GridEnum.SQUARE
    elif num_bases % 21 == 0:
        lattice_type = LatticeEnum.HONEYCOMB
        grid_type = GridEnum.HONEYCOMB
    else:
        raise IOError("error decoding number of bases")

    part = None
    # DETERMINE MAX ROW,COL
    max_row_json = max_col_json = 0
    for helix in obj['vstrands']:
        max_row_json = max(max_row_json, int(helix['row'])+1)
        max_col_json = max(max_col_json, int(helix['col'])+1)

    # CREATE PART ACCORDING TO LATTICE TYPE
    if lattice_type == LatticeEnum.HONEYCOMB:
        doLattice = HoneycombDnaPart.legacyLatticeCoordToPositionXY
        isEven = HoneycombDnaPart.isEvenParity
    elif lattice_type == LatticeEnum.SQUARE:
        doLattice = SquareDnaPart.legacyLatticeCoordToPositionXY
        isEven = SquareDnaPart.isEvenParity
    else:
        raise TypeError("Lattice type not recognized")
    part = document.createNucleicAcidPart(grid_type=grid_type, use_undostack=False)
    part.setActive(True)
    document.setGridType(grid_type)
    document.setSliceOrGridViewVisible(OrthoViewEnum.SLICE)
    setBatch(True)
    # POPULATE VIRTUAL HELICES
    ordered_id_list = []
    vh_num_to_coord = {}
    min_row, max_row = 10000, -10000
    min_col, max_col = 10000, -10000

    # find row, column limits
    for helix in obj['vstrands']:
        row = helix['row']
        if row < min_row:
            min_row = row
        if row > max_row:
            max_row = row
        col = helix['col']
        if col < min_col:
            min_col = col
        if col > max_col:
            max_col = col
    # end for

    delta_row = (max_row + min_row) // 2
#    # 2 LINES COMMENTED OUT BY NC, doesn't appear to be necessary for honeycomb
#    if delta_row & 0:
    if delta_row & 1:
        delta_row += 1
    delta_column = (max_col + min_col) // 2
#    if delta_column & 0:
    if delta_column & 1:
        delta_column += 1

    print("Found cadnano version 2 file")
    # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row))
    # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column))

    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        # align row and columns to the center 0, 0
        if HoneycombDnaPart.isEvenParity(row, col):
            coord = (row - delta_row, col - delta_column)
        else:
            coord = (row - delta_row, col - delta_column)
        vh_num_to_coord[vh_num] = coord
        ordered_id_list.append(vh_num)
    # end for

    # make sure we retain the original order
    radius = DEFAULT_RADIUS
    for vh_num in sorted(vh_num_to_coord.keys()):
        row, col = vh_num_to_coord[vh_num]
        x, y = doLattice(radius, row, col)
        part.createVirtualHelix(x, y, 0., num_bases,
                                id_num=vh_num, use_undostack=False)
    # zoom to fit
    if emit_signals:
        part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True)
    if not getReopen():
        setBatch(False)
    part.setImportedVHelixOrder(ordered_id_list)
    setReopen(False)
    setBatch(False)

    # INSTALL STRANDS AND COLLECT XOVER LOCATIONS
    scaf_seg = defaultdict(list)
    scaf_xo = defaultdict(list)
    stap_seg = defaultdict(list)
    stap_xo = defaultdict(list)
    try:
        for helix in obj['vstrands']:
            vh_num = helix['num']
            row, col = vh_num_to_coord[vh_num]
            scaf = helix['scaf']
            stap = helix['stap']
            insertions = helix['loop']
            skips = helix['skip']

            if isEven(row, col):
                scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num)
            else:
                stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num)

            # validate file serialization of lists
            assert(len(scaf) == len(stap) and
                   len(scaf) == len(insertions) and
                   len(insertions) == len(skips))

            # read scaffold segments and xovers
            for i in range(len(scaf)):
                five_vh, five_idx, three_vh, three_idx = scaf[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandEnum.SCAFFOLD, vh_num, i, five_vh,
                                       five_idx, three_vh, three_idx):
                    scaf_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    scaf_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandEnum.SCAFFOLD, vh_num, i, three_vh, three_idx):
                    scaf_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(scaf_seg[vh_num]) % 2 == 0)

            # install scaffold segments
            for i in range(0, len(scaf_seg[vh_num]), 2):
                low_idx = scaf_seg[vh_num][i]
                high_idx = scaf_seg[vh_num][i + 1]
                scaf_strand_set.createStrand(low_idx, high_idx, use_undostack=False)

            # read staple segments and xovers
            for i in range(len(stap)):
                five_vh, five_idx, three_vh, three_idx = stap[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandEnum.STAPLE, vh_num, i, five_vh,
                                       five_idx, three_vh, three_idx):
                    stap_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    stap_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandEnum.STAPLE, vh_num, i, three_vh, three_idx):
                    stap_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(stap_seg[vh_num]) % 2 == 0)

            # install staple segments
            for i in range(0, len(stap_seg[vh_num]), 2):
                low_idx = stap_seg[vh_num][i]
                high_idx = stap_seg[vh_num][i + 1]
                stap_strand_set.createStrand(low_idx, high_idx, use_undostack=False)
            part.refreshSegments(vh_num)
        # end for
    except AssertionError:
        print("Unrecognized file format.")
        raise

    # INSTALL XOVERS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]

        if isEven(row, col):
            scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num)
        else:
            stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num)

        # install scaffold xovers
        for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            try:
                strand5p = scaf_strand_set.getStrand(idx5p)
            except Exception:
                print(vh_num, idx5p)
                print(scaf_strand_set.strand_heap)
                print(stap_strand_set.strand_heap)
                raise
            coord = vh_num_to_coord[to_vh_num]
            if isEven(*coord):
                to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num)
            else:
                to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num)
            strand3p = to_scaf_strand_set.getStrand(idx3p)
            part.createXover(strand5p, idx5p,
                             strand3p, idx3p,
                             allow_reordering=True,
                             update_oligo=False,
                             use_undostack=False)

        # install staple xovers
        for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = stap_strand_set.getStrand(idx5p)
            coord = vh_num_to_coord[to_vh_num]
            if isEven(*coord):
                to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num)
            else:
                to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num)
            strand3p = to_stap_strand_set.getStrand(idx3p)
            part.createXover(strand5p, idx5p,
                             strand3p, idx3p,
                             allow_reordering=True,
                             update_oligo=False,
                             use_undostack=False)

    # need to heal all oligo connections into a continuous
    # oligo for the next steps
    RefreshOligosCommand(part).redo()

    # COLORS, INSERTIONS, SKIPS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']

        if isEven(row, col):
            scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num)
        else:
            stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num)

        # install insertions and skips
        for base_idx in range(len(stap)):
            sum_of_insert_skip = insertions[base_idx] + skips[base_idx]
            if sum_of_insert_skip != 0:
                strand = scaf_strand_set.getStrand(base_idx)
                strand.addInsertion(base_idx,
                                    sum_of_insert_skip,
                                    use_undostack=False)
        # end for
        # populate colors
        for base_idx, color_number in helix['stap_colors']:
            color = intToColorHex(color_number)
            strand = stap_strand_set.getStrand(base_idx)
            strand.oligo().applyColor(color, use_undostack=False)

    if 'oligos' in obj:
        for oligo in obj['oligos']:
            vh_num = oligo['vh_num']
            idx = oligo['idx']
            seq = str(oligo['seq']) if oligo['seq'] is not None else ''
            if seq != '':
                coord = vh_num_to_coord[vh_num]
                if isEven(*coord):
                    scaf_ss, stap_ss = part.getStrandSets(vh_num)
                else:
                    stap_ss, scaf_ss = part.getStrandSets(vh_num)
                strand = scaf_ss.getStrand(idx)
                # print "sequence", seq, vh, idx,  strand.oligo()._strand5p
                strand.oligo().applySequence(seq, use_undostack=False)
    if 'modifications' in obj:
        for mod_id, item in obj['modifications'].items():
            if mod_id != 'int_instances' and mod_id != 'ext_instances':
                part.createMod(item, mod_id)
        for key, mid in obj['modifications']['ext_instances'].items():
            # strand, idx, coord, isstaple = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except Exception:
                print(strand, idx)
                raise
        for key, mid in obj['modifications']['int_instances'].items():
            # strand, idx, coord, isstaple  = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except Exception:
                print(strand, idx)
                raise
Esempio n. 6
0
def decode(document: DocT, obj: dict, emit_signals: bool = True):
    """Parses a dictionary (obj) created from reading a json file and uses it
    to populate the given document with model data.

    Args:
        document:
        obj:
        emit_signals: whether to signal views

    Raises:
        AssertionError, TypeError
    """
    num_bases = len(obj['vstrands'][0]['fwd_ss'])

    lattice_type = LatticeEnum.HONEYCOMB

    part = None
    # DETERMINE MAX ROW,COL
    max_row_json = max_col_json = 0
    for helix in obj['vstrands']:
        max_row_json = max(max_row_json, int(helix['row']) + 1)
        max_col_json = max(max_col_json, int(helix['col']) + 1)

    # CREATE PART ACCORDING TO LATTICE TYPE
    if lattice_type == LatticeEnum.HONEYCOMB:
        doLattice = HoneycombDnaPart.latticeCoordToModelXY
        isEven = HoneycombDnaPart.isEvenParity
        grid_type = GridEnum.HONEYCOMB
    elif lattice_type == LatticeEnum.SQUARE:
        doLattice = SquareDnaPart.latticeCoordToModelXY
        isEven = SquareDnaPart.isEvenParity
        grid_type = GridEnum.SQUARE
    else:
        raise TypeError("Lattice type not recognized")
    part = document.createNucleicAcidPart(  use_undostack=False,
                                            is_lattice=True,
                                            grid_type=grid_type)
    part.setActive(True)
    setBatch(True)
    # POPULATE VIRTUAL HELICES
    ordered_id_list = []
    property_dict = {}
    vh_num_to_coord = {}
    min_row, max_row = 10000, -10000
    min_col, max_col = 10000, -10000

    # find row, column limits
    for helix in obj['vstrands']:
        row = helix['row']
        if row < min_row:
            min_row = row
        if row > max_row:
            max_row = row
        col = helix['col']
        if col < min_col:
            min_col = col
        if col > max_col:
            max_col = col
    # end for

    delta_row = (max_row + min_row) // 2
    if delta_row & 1:
        delta_row += 1
    delta_column = (max_col + min_col) // 2
    if delta_column & 1:
        delta_column += 1

    # print("Found cadnano version 2.5 file")
    # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row))
    # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column))

    encoded_keys = ['eulerZ', 'repeats', 'bases_per_repeat',
                    'turns_per_repeat', 'z']
    model_keys = ['eulerZ', 'repeat_hint', 'bases_per_repeat',
                  'turns_per_repeat', 'z']
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        # align row and columns to the center 0, 0
        coord = (row - delta_row, col - delta_column)
        vh_num_to_coord[vh_num] = coord
        ordered_id_list.append(vh_num)
        property_dict[vh_num] = [helix[key] for key in encoded_keys]
    # end for

    radius = DEFAULT_RADIUS
    for vh_num in sorted(vh_num_to_coord.keys()):
        row, col = vh_num_to_coord[vh_num]
        x, y = doLattice(radius, row, col)
        props = property_dict[vh_num]
        z = convertToModelZ(props[-1])
        props[-1] = z
        # print("%d:" % vh_num, x, y, z)
        # print({k:v for k, v in zip(encoded_keys, props)})
        part.createVirtualHelix(x, y, z, num_bases,
                                id_num=vh_num,
                                properties=(model_keys, props),
                                use_undostack=False)
    if not getReopen():
        setBatch(False)
    part.setImportedVHelixOrder(ordered_id_list)
    # zoom to fit
    if emit_signals:
        part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True)
    setReopen(False)
    setBatch(False)

    # INSTALL STRANDS AND COLLECT XOVER LOCATIONS
    fwd_ss_seg = defaultdict(list)
    fwd_ss_xo = defaultdict(list)
    rev_ss_seg = defaultdict(list)
    rev_ss_xo = defaultdict(list)
    try:
        for helix in obj['vstrands']:
            vh_num = helix['num']
            row, col = vh_num_to_coord[vh_num]
            insertions = helix['insertions']
            deletions = helix['deletions']

            if isEven(row, col):
                # parity matters still despite the fwd and rev naming of
                # this format
                fwd_strandset, rev_strandset = part.getStrandSets(vh_num)
                fwd_ss = helix['fwd_ss']
                rev_ss = helix['rev_ss']
            else:
                rev_strandset, fwd_strandset = part.getStrandSets(vh_num)
                rev_ss = helix['fwd_ss']
                fwd_ss = helix['rev_ss']

            # validate file serialization of lists
            assert(len(fwd_ss) == len(rev_ss) and
                   len(fwd_ss) == len(insertions) and
                   len(insertions) == len(deletions))

            # read fwd_strandset segments and xovers
            for i in range(len(fwd_ss)):
                five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = fwd_ss[i]

                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandEnum.SCAFFOLD, vh_num, i,
                                       five_vh, five_idx, three_vh, three_idx):
                    fwd_ss_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    fwd_ss_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandEnum.SCAFFOLD, vh_num, i, three_vh, three_idx):
                    fwd_ss_xo[vh_num].append((i, three_vh, three_strand, three_idx))
            assert (len(fwd_ss_seg[vh_num]) % 2 == 0)

            # install fwd_strandset segments
            for i in range(0, len(fwd_ss_seg[vh_num]), 2):
                low_idx = fwd_ss_seg[vh_num][i]
                high_idx = fwd_ss_seg[vh_num][i + 1]
                fwd_strandset.createStrand(low_idx, high_idx, use_undostack=False)

            # read rev_strandset segments and xovers
            for i in range(len(rev_ss)):
                five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = rev_ss[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandEnum.STAPLE, vh_num, i,
                                       five_vh, five_idx, three_vh, three_idx):
                    rev_ss_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    rev_ss_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandEnum.STAPLE, vh_num, i, three_vh, three_idx):
                    rev_ss_xo[vh_num].append((i, three_vh, three_strand, three_idx))
            assert (len(rev_ss_seg[vh_num]) % 2 == 0)

            # install rev_strandset segments
            for i in range(0, len(rev_ss_seg[vh_num]), 2):
                low_idx = rev_ss_seg[vh_num][i]
                high_idx = rev_ss_seg[vh_num][i + 1]
                rev_strandset.createStrand(low_idx, high_idx, use_undostack=False)
            part.refreshSegments(vh_num)
        # end for
    except AssertionError:
        print("Unrecognized file format.")
        raise

    """ INSTALL XOVERS
    parity matters for the from idx but is already encoded in
    the `to_strand3p` parameter of the tuple in `fwd_ss_xo` and `rev_ss_xo`
    """
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]
        if isEven(row, col):
            fwd_strandset, rev_strandset = part.getStrandSets(vh_num)
        else:
            rev_strandset, fwd_strandset = part.getStrandSets(vh_num)

        # install fwd_strandset xovers
        for (idx5p, to_vh_num, to_strand3p, idx3p) in fwd_ss_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = fwd_strandset.getStrand(idx5p)
            to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num)

            if to_strand3p == 0:
                strand3p = to_fwd_strandset.getStrand(idx3p)
            else:
                strand3p = to_rev_strandset.getStrand(idx3p)
            part.createXover(strand5p, idx5p,
                             strand3p, idx3p,
                             allow_reordering=True,
                             update_oligo=False,
                             use_undostack=False)

        # install rev_strandset xovers
        for (idx5p, to_vh_num, to_strand3p, idx3p) in rev_ss_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = rev_strandset.getStrand(idx5p)
            to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num)
            coord = vh_num_to_coord[to_vh_num]

            if to_strand3p == 0:
                strand3p = to_fwd_strandset.getStrand(idx3p)
            else:
                strand3p = to_rev_strandset.getStrand(idx3p)
            part.createXover(strand5p, idx5p,
                             strand3p, idx3p,
                             allow_reordering=True,
                             update_oligo=False,
                             use_undostack=False)

    # need to heal all oligo connections into a continuous
    # oligo for the next steps
    RefreshOligosCommand(part).redo()

    # COLORS, INSERTIONS, deletions
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]
        fwd_ss = helix['fwd_ss']
        rev_ss = helix['rev_ss']
        insertions = helix['insertions']
        deletions = helix['deletions']

        fwd_strandset, rev_strandset = part.getStrandSets(vh_num)

        # install insertions and deletions
        for base_idx in range(len(rev_ss)):
            sum_of_insert_deletion = insertions[base_idx] + deletions[base_idx]
            if sum_of_insert_deletion != 0:
                strand = fwd_strandset.getStrand(base_idx)
                strand.addInsertion(base_idx,
                                    sum_of_insert_deletion,
                                    use_undostack=False)
        # end for

        # populate colors
        for strand_type, base_idx, color in helix['colors']:
            strandset = fwd_strandset if strand_type == 0 else rev_strandset
            strand = strandset.getStrand(base_idx)
            strand.oligo().applyColor(color, use_undostack=False)

    if 'oligos' in obj:
        for oligo in obj['oligos']:
            vh_num = oligo['vh_num']
            idx = oligo['idx']
            seq = str(oligo['seq']) if oligo['seq'] is not None else ''
            if seq != '':
                coord = vh_num_to_coord[vh_num]
                fwd_strandset, rev_strandset = part.getStrandSets(vh_num)
                strand = fwd_strandset.getStrand(idx)
                strand.oligo().applySequence(seq, use_undostack=False)

    if 'modifications' in obj:
        for mod_id, item in obj['modifications'].items():
            if mod_id != 'int_instances' and mod_id != 'ext_instances':
                part.createMod(item, mod_id)
        for key, mid in obj['modifications']['ext_instances'].items():
            # strand, idx, coord, isstaple = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except Exception:
                print(strand, idx)
                raise
        for key in obj['modifications']['int_instances'].items():
            # strand, idx, coord, isstaple  = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except Exception:
                print(strand, idx)
                raise
Esempio n. 7
0
def decode(document, obj, emit_signals=True):
    """
    Parses a dictionary (obj) created from reading a json file and uses it
    to populate the given document with model data.
    """
    num_bases = len(obj['vstrands'][0]['fwd_ss'])

    lattice_type = LatticeType.HONEYCOMB

    part = None
    # DETERMINE MAX ROW,COL
    max_row_json = max_col_json = 0
    for helix in obj['vstrands']:
        max_row_json = max(max_row_json, int(helix['row']) + 1)
        max_col_json = max(max_col_json, int(helix['col']) + 1)

    # CREATE PART ACCORDING TO LATTICE TYPE
    if lattice_type == LatticeType.HONEYCOMB:
        doLattice = HoneycombDnaPart.latticeCoordToPositionXY
        isEven = HoneycombDnaPart.isEvenParity
    elif lattice_type == LatticeType.SQUARE:
        doLattice = SquareDnaPart.latticeCoordToPositionXY
        isEven = SquareDnaPart.isEvenParity
    else:
        raise TypeError("Lattice type not recognized")
    part = document.createNucleicAcidPart(use_undostack=False)
    part.setActive(True)
    setBatch(True)
    # POPULATE VIRTUAL HELICES
    ordered_id_list = []
    property_dict = {}
    vh_num_to_coord = {}
    min_row, max_row = 10000, -10000
    min_col, max_col = 10000, -10000

    # find row, column limits
    for helix in obj['vstrands']:
        row = helix['row']
        if row < min_row:
            min_row = row
        if row > max_row:
            max_row = row
        col = helix['col']
        if col < min_col:
            min_col = col
        if col > max_col:
            max_col = col
    # end for

    delta_row = (max_row + min_row) // 2
    if delta_row & 1:
        delta_row += 1
    delta_column = (max_col + min_col) // 2
    if delta_column & 1:
        delta_column += 1

    # print("Found cadnano version 2.5 file")
    # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row))
    # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column))

    encoded_keys = [
        'eulerZ', 'repeats', 'bases_per_repeat', 'turns_per_repeat', 'z'
    ]
    model_keys = [
        'eulerZ', 'repeat_hint', 'bases_per_repeat', 'turns_per_repeat', 'z'
    ]
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        # align row and columns to the center 0, 0
        coord = (row - delta_row, col - delta_column)
        vh_num_to_coord[vh_num] = coord
        ordered_id_list.append(vh_num)
        property_dict[vh_num] = [helix[key] for key in encoded_keys]
    # end for

    radius = DEFAULT_RADIUS
    for vh_num in sorted(vh_num_to_coord.keys()):
        row, col = vh_num_to_coord[vh_num]
        x, y = doLattice(radius, row, col)
        props = property_dict[vh_num]
        z = convertToModelZ(props[-1])
        props[-1] = z
        # print("%d:" % vh_num, x, y, z)
        # print({k:v for k, v in zip(encoded_keys, props)})
        part.createVirtualHelix(x,
                                y,
                                z,
                                num_bases,
                                id_num=vh_num,
                                properties=(model_keys, props),
                                use_undostack=False)
    if not getReopen():
        setBatch(False)
    part.setImportedVHelixOrder(ordered_id_list)
    # zoom to fit
    if emit_signals:
        part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True)
    setReopen(False)
    setBatch(False)

    # INSTALL STRANDS AND COLLECT XOVER LOCATIONS
    fwd_ss_seg = defaultdict(list)
    fwd_ss_xo = defaultdict(list)
    rev_ss_seg = defaultdict(list)
    rev_ss_xo = defaultdict(list)
    try:
        for helix in obj['vstrands']:
            vh_num = helix['num']
            row, col = vh_num_to_coord[vh_num]
            insertions = helix['insertions']
            deletions = helix['deletions']

            if isEven(row, col):
                # parity matters still despite the fwd and rev naming of
                # this format
                fwd_strandset, rev_strandset = part.getStrandSets(vh_num)
                fwd_ss = helix['fwd_ss']
                rev_ss = helix['rev_ss']
            else:
                rev_strandset, fwd_strandset = part.getStrandSets(vh_num)
                rev_ss = helix['fwd_ss']
                fwd_ss = helix['rev_ss']

            # validate file serialization of lists
            assert (len(fwd_ss) == len(rev_ss)
                    and len(fwd_ss) == len(insertions)
                    and len(insertions) == len(deletions))

            # read fwd_strandset segments and xovers
            for i in range(len(fwd_ss)):
                five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = fwd_ss[
                    i]

                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,
                                       five_idx, three_vh, three_idx):
                    fwd_ss_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    fwd_ss_seg[vh_num].append(
                        i)  # end segment on a double crossover
                if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh,
                                 three_idx):
                    fwd_ss_xo[vh_num].append(
                        (i, three_vh, three_strand, three_idx))
            assert (len(fwd_ss_seg[vh_num]) % 2 == 0)

            # install fwd_strandset segments
            for i in range(0, len(fwd_ss_seg[vh_num]), 2):
                low_idx = fwd_ss_seg[vh_num][i]
                high_idx = fwd_ss_seg[vh_num][i + 1]
                fwd_strandset.createStrand(low_idx,
                                           high_idx,
                                           use_undostack=False)

            # read rev_strandset segments and xovers
            for i in range(len(rev_ss)):
                five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = rev_ss[
                    i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,
                                       five_idx, three_vh, three_idx):
                    rev_ss_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    rev_ss_seg[vh_num].append(
                        i)  # end segment on a double crossover
                if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh,
                                 three_idx):
                    rev_ss_xo[vh_num].append(
                        (i, three_vh, three_strand, three_idx))
            assert (len(rev_ss_seg[vh_num]) % 2 == 0)

            # install rev_strandset segments
            for i in range(0, len(rev_ss_seg[vh_num]), 2):
                low_idx = rev_ss_seg[vh_num][i]
                high_idx = rev_ss_seg[vh_num][i + 1]
                rev_strandset.createStrand(low_idx,
                                           high_idx,
                                           use_undostack=False)
            part.refreshSegments(vh_num)
        # end for
    except AssertionError:
        print("Unrecognized file format.")
        raise
    """ INSTALL XOVERS
    parity matters for the from idx but is already encoded in
    the `to_strand3p` parameter of the tuple in `fwd_ss_xo` and `rev_ss_xo`
    """
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]
        if isEven(row, col):
            fwd_strandset, rev_strandset = part.getStrandSets(vh_num)
        else:
            rev_strandset, fwd_strandset = part.getStrandSets(vh_num)

        # install fwd_strandset xovers
        for (idx5p, to_vh_num, to_strand3p, idx3p) in fwd_ss_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = fwd_strandset.getStrand(idx5p)
            to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num)

            if to_strand3p == 0:
                strand3p = to_fwd_strandset.getStrand(idx3p)
            else:
                strand3p = to_rev_strandset.getStrand(idx3p)
            part.createXover(strand5p,
                             idx5p,
                             strand3p,
                             idx3p,
                             allow_reordering=True,
                             update_oligo=False,
                             use_undostack=False)

        # install rev_strandset xovers
        for (idx5p, to_vh_num, to_strand3p, idx3p) in rev_ss_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = rev_strandset.getStrand(idx5p)
            to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num)
            coord = vh_num_to_coord[to_vh_num]

            if to_strand3p == 0:
                strand3p = to_fwd_strandset.getStrand(idx3p)
            else:
                strand3p = to_rev_strandset.getStrand(idx3p)
            part.createXover(strand5p,
                             idx5p,
                             strand3p,
                             idx3p,
                             allow_reordering=True,
                             update_oligo=False,
                             use_undostack=False)

    # need to heal all oligo connections into a continuous
    # oligo for the next steps
    RefreshOligosCommand(part).redo()

    # COLORS, INSERTIONS, deletions
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]
        fwd_ss = helix['fwd_ss']
        rev_ss = helix['rev_ss']
        insertions = helix['insertions']
        deletions = helix['deletions']

        fwd_strandset, rev_strandset = part.getStrandSets(vh_num)

        # install insertions and deletions
        for base_idx in range(len(rev_ss)):
            sum_of_insert_deletion = insertions[base_idx] + deletions[base_idx]
            if sum_of_insert_deletion != 0:
                strand = fwd_strandset.getStrand(base_idx)
                strand.addInsertion(base_idx,
                                    sum_of_insert_deletion,
                                    use_undostack=False)
        # end for

        # populate colors
        for strand_type, base_idx, color in helix['colors']:
            strandset = fwd_strandset if strand_type == 0 else rev_strandset
            strand = strandset.getStrand(base_idx)
            strand.oligo().applyColor(color, use_undostack=False)

    if 'oligos' in obj:
        for oligo in obj['oligos']:
            vh_num = oligo['vh_num']
            idx = oligo['idx']
            seq = str(oligo['seq']) if oligo['seq'] is not None else ''
            if seq != '':
                coord = vh_num_to_coord[vh_num]
                fwd_strandset, rev_strandset = part.getStrandSets(vh_num)
                strand = fwd_strandset.getStrand(idx)
                strand.oligo().applySequence(seq, use_undostack=False)

    if 'modifications' in obj:
        for mod_id, item in obj['modifications'].items():
            if mod_id != 'int_instances' and mod_id != 'ext_instances':
                part.createMod(item, mod_id)
        for key, mid in obj['modifications']['ext_instances'].items():
            # strand, idx, coord, isstaple = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except Exception:
                print(strand, idx)
                raise
        for key in obj['modifications']['int_instances'].items():
            # strand, idx, coord, isstaple  = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except Exception:
                print(strand, idx)
                raise
Esempio n. 8
0
def decode(document, obj):
    """
    Parses a dictionary (obj) created from reading a json file and uses it
    to populate the given document with model data.
    """
    num_bases = len(obj['vstrands'][0]['scaf'])
    if num_bases % 32 == 0:
        lattice_type = LatticeType.SQUARE
    elif num_bases % 21 == 0:
        lattice_type = LatticeType.HONEYCOMB
    else:
        raise IOError("error decoding number of bases")

    # DETERMINE MAX ROW,COL
    max_row_json = max_col_json = 0
    for helix in obj['vstrands']:
        max_row_json = max(max_row_json, int(helix['row']) + 1)
        max_col_json = max(max_col_json, int(helix['col']) + 1)

    # CREATE PART ACCORDING TO LATTICE TYPE
    if lattice_type == LatticeType.HONEYCOMB:
        steps = num_bases // 21
        # num_rows = max(30, max_row_json, cadnano.app().prefs.honeycombRows)
        # num_cols = max(32, max_col_json, cadnano.app().prefs.honeycombCols)
        num_rows = max(30, max_row_json, prefs.HONEYCOMB_PART_MAXROWS)
        num_cols = max(32, max_col_json, prefs.HONEYCOMB_PART_MAXCOLS)
        part = document.addHoneycombPart(num_rows, num_cols, steps)
    elif lattice_type == LatticeType.SQUARE:
        is_SQ_100 = True  # check for custom SQ100 format
        for helix in obj['vstrands']:
            if helix['col'] != 0:
                is_SQ_100 = False
                break
        if is_SQ_100:
            # num_rows, num_cols = 100, 1
            num_rows, num_cols = 40, 30
        else:
            num_rows, num_cols = 40, 30
        steps = num_bases // 32
        # num_rows = max(30, max_row_json, cadnano.app().prefs.squareRows)
        # num_cols = max(32, max_col_json, cadnano.app().prefs.squareCols)
        num_rows = max(30, max_row_json, prefs.SQUARE_PART_MAXROWS)
        num_cols = max(32, max_col_json, prefs.SQUARE_PART_MAXCOLS)
        part = document.addSquarePart(num_rows, num_cols, steps)
        # part = SquarePart(document=document, max_row=num_rows, max_col=num_cols, max_steps=steps)
    else:
        raise TypeError("Lattice type not recognized")
    # document._addPart(part, use_undostack=False)
    setBatch(True)
    # POPULATE VIRTUAL HELICES
    ordered_coord_list = []
    vh_num_to_coord = {}
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        coord = (row, col)
        vh_num_to_coord[vh_num] = coord
        ordered_coord_list.append(coord)
    # make sure we retain the original order
    for vh_num in sorted(vh_num_to_coord.keys()):
        row, col = vh_num_to_coord[vh_num]
        part.createVirtualHelix(row, col, use_undostack=False)
    if not getReopen():
        setBatch(False)
    part.setImportedVHelixOrder(ordered_coord_list)
    setReopen(False)
    setBatch(False)

    # INSTALL STRANDS AND COLLECT XOVER LOCATIONS
    num_helices = len(obj['vstrands']) - 1
    scaf_seg = defaultdict(list)
    scaf_xo = defaultdict(list)
    stap_seg = defaultdict(list)
    stap_xo = defaultdict(list)
    try:
        for helix in obj['vstrands']:
            vh_num = helix['num']
            row = helix['row']
            col = helix['col']
            scaf = helix['scaf']
            stap = helix['stap']
            insertions = helix['loop']
            skips = helix['skip']
            vh = part.virtualHelixAtCoord((row, col))
            scaf_strand_set = vh.scaffoldStrandSet()
            stap_strand_set = vh.stapleStrandSet()
            assert(len(scaf) == len(stap) and len(stap) == part.maxBaseIdx() + 1 and\
                   len(scaf) == len(insertions) and len(insertions) == len(skips))
            # read scaffold segments and xovers
            for i in range(len(scaf)):
                five_vh, five_idx, three_vh, three_idx = scaf[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                    scaf_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    scaf_seg[vh_num].append(
                        i)  # end segment on a double crossover
                if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh,
                                 three_idx):
                    scaf_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(scaf_seg[vh_num]) % 2 == 0)
            # install scaffold segments
            for i in range(0, len(scaf_seg[vh_num]), 2):
                low_idx = scaf_seg[vh_num][i]
                high_idx = scaf_seg[vh_num][i + 1]
                scaf_strand_set.createStrand(low_idx,
                                             high_idx,
                                             use_undostack=False)
            # read staple segments and xovers
            for i in range(len(stap)):
                five_vh, five_idx, three_vh, three_idx = stap[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                    stap_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    stap_seg[vh_num].append(
                        i)  # end segment on a double crossover
                if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh,
                                 three_idx):
                    stap_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(stap_seg[vh_num]) % 2 == 0)
            # install staple segments
            for i in range(0, len(stap_seg[vh_num]), 2):
                low_idx = stap_seg[vh_num][i]
                high_idx = stap_seg[vh_num][i + 1]
                stap_strand_set.createStrand(low_idx,
                                             high_idx,
                                             use_undostack=False)
    except AssertionError:
        print("Unrecognized file format.")
        raise

    # INSTALL XOVERS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']
        from_vh = part.virtualHelixAtCoord((row, col))
        scaf_strand_set = from_vh.scaffoldStrandSet()
        stap_strand_set = from_vh.stapleStrandSet()
        # install scaffold xovers
        for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = scaf_strand_set.getStrand(idx5p)
            to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num])
            strand3p = to_vh.scaffoldStrandSet().getStrand(idx3p)
            part.createXover(strand5p,
                             idx5p,
                             strand3p,
                             idx3p,
                             update_oligo=False,
                             use_undostack=False)
        # install staple xovers
        for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = stap_strand_set.getStrand(idx5p)
            to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num])
            strand3p = to_vh.stapleStrandSet().getStrand(idx3p)
            part.createXover(strand5p,
                             idx5p,
                             strand3p,
                             idx3p,
                             update_oligo=False,
                             use_undostack=False)

    # need to heal all oligo connections into a continuous
    # oligo for the next steps
    RefreshOligosCommand(part,
                         include_scaffold=True,
                         colors=(prefs.DEFAULT_SCAF_COLOR,
                                 prefs.DEFAULT_STAP_COLOR)).redo()

    # SET DEFAULT COLOR
    # for oligo in part.oligos():
    #     if oligo.isStaple():
    #         default_color = prefs.DEFAULT_STAP_COLOR
    #     else:
    #         default_color = prefs.DEFAULT_SCAF_COLOR
    #     oligo.applyColor(default_color, use_undostack=False)

    # COLORS, INSERTIONS, SKIPS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']
        vh = part.virtualHelixAtCoord((row, col))
        scaf_strand_set = vh.scaffoldStrandSet()
        stap_strand_set = vh.stapleStrandSet()
        # install insertions and skips
        for base_idx in range(len(stap)):
            sum_of_insert_skip = insertions[base_idx] + skips[base_idx]
            if sum_of_insert_skip != 0:
                strand = scaf_strand_set.getStrand(base_idx)
                strand.addInsertion(base_idx,
                                    sum_of_insert_skip,
                                    use_undostack=False)
        # end for
        # populate colors
        for base_idx, color_number in helix['stap_colors']:
            color = Color((color_number >> 16) & 0xFF,
                          (color_number >> 8) & 0xFF,
                          color_number & 0xFF).name()
            strand = stap_strand_set.getStrand(base_idx)
            strand.oligo().applyColor(color, use_undostack=False)

    if 'oligos' in obj:
        for oligo in obj['oligos']:
            vh_num = oligo['vh_num']
            idx = oligo['idx']
            seq = str(oligo['seq']) if oligo['seq'] is not None else ''
            if seq != '':
                coord = vh_num_to_coord[vhNum]
                vh = part.virtualHelixAtCoord(coord)
                scaf_ss = vh.scaffoldStrandSet()
                # stapStrandSet = vh.stapleStrandSet()
                strand = scaf_ss.getStrand(idx)
                # print "sequence", seq, vh, idx,  strand.oligo()._strand5p
                strand.oligo().applySequence(seq, use_undostack=False)
    if 'modifications' in obj:
        # print("AD", cadnano.app().activeDocument)
        # win = cadnano.app().activeDocument.win
        # modstool = win.pathToolManager.modsTool
        # modstool.connectSignals(part)
        for mod_id, item in obj['modifications'].items():
            if mod_id != 'int_instances' and mod_id != 'ext_instances':
                part.createMod(item, mod_id)
        for key, mid in obj['modifications']['ext_instances'].items():
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(mid, idx, use_undostack=False)
            except:
                print(strand, idx)
                raise
        for key in obj['modifications']['int_instances'].items():
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(mid, idx, use_undostack=False)
            except:
                print(strand, idx)
                raise
Esempio n. 9
0
def decode(document,obj):
    num_bases = len(obj['vstrands'][0]['scaf'])
    if num_bases % 32 == 0:
        lattice_type = LatticeType.SQUARE
    elif num_bases % 21 == 0:
        lattice_type = LatticeType.HONEYCOMB
    else:
        raise IOError("error decoding number of bases")


    # DETERMINE MAX ROW,COL
    max_row_json = max_col_json = 0
    for helix in obj['vstrands']:
        max_row_json = max(max_row_json, int(helix['row'])+1)
        max_col_json = max(max_col_json, int(helix['col'])+1)

    # CREATE PART ACCORDING TO LATTICE TYPE
    if lattice_type == LatticeType.HONEYCOMB:
        steps = num_bases // 21
        # num_rows = max(30, max_row_json, cadnano.app().prefs.honeycombRows)
        # num_cols = max(32, max_col_json, cadnano.app().prefs.honeycombCols)
        num_rows = max(30, max_row_json, prefs.HONEYCOMB_PART_MAXROWS)
        num_cols = max(32, max_col_json, prefs.HONEYCOMB_PART_MAXCOLS)
        part = document.addHoneycombPart(num_rows, num_cols, steps)
    elif lattice_type == LatticeType.SQUARE:
        is_SQ_100 = True  # check for custom SQ100 format
        for helix in obj['vstrands']:
            if helix['col'] != 0:
                is_SQ_100 = False
                break
        if is_SQ_100:
            # num_rows, num_cols = 100, 1
            num_rows, num_cols = 40, 30
        else:
            num_rows, num_cols = 40, 30
        steps = num_bases // 32
        # num_rows = max(30, max_row_json, cadnano.app().prefs.squareRows)
        # num_cols = max(32, max_col_json, cadnano.app().prefs.squareCols)
        num_rows = max(30, max_row_json, prefs.SQUARE_PART_MAXROWS)
        num_cols = max(32, max_col_json, prefs.SQUARE_PART_MAXCOLS)
        part = document.addSquarePart(num_rows, num_cols, steps)
        # part = SquarePart(document=document, max_row=num_rows, max_col=num_cols, max_steps=steps)
    else:
        raise TypeError("Lattice type not recognized")
    # document._addPart(part, use_undostack=False)
    setBatch(True)
    # POPULATE VIRTUAL HELICES
    ordered_coord_list = []
    vh_num_to_coord = {}
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        coord = (row, col)
        vh_num_to_coord[vh_num] = coord
        ordered_coord_list.append(coord)
    # make sure we retain the original order
    for vh_num in sorted(vh_num_to_coord.keys()):
        row, col = vh_num_to_coord[vh_num]
        part.createVirtualHelix(row, col, use_undostack=False)
    if not getReopen():
        setBatch(False)
    part.setImportedVHelixOrder(ordered_coord_list)
    setReopen(False)
    setBatch(False)

    num_helices = len(obj['vstrands']) - 1
    scaf_seg = defaultdict(list)
    scaf_xo = defaultdict(list)
    stap_seg = defaultdict(list)
    stap_xo = defaultdict(list)

    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']
        vh = part.virtualHelixAtCoord((row, col))
        scaf_strand_set = vh._scaf_strand_set
        stap_strand_set = vh._stap_strand_set

        # read scaf segments and xovers

        for i in range(len(scaf)):
            five_vh, five_idx, three_vh, three_idx = scaf[i]
            if five_vh == -1 and three_vh == -1:
                continue  # null base
            if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                scaf_seg[vh_num].append(i)
            if five_vh != vh_num and three_vh != vh_num:  # special case
                scaf_seg[vh_num].append(i)  # end segment on a double crossover
            if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx):
                scaf_xo[vh_num].append((i, three_vh, three_idx))
            # read staple segments and xovers; update stap .json
                #TODO: Reem, you need to look here because this is where Amy added in the 5th element
                #TODO: Reem, you might need to modify this to render existing toeholds in otracad .json files
        for i in range(len(stap)):
                five_vh, five_idx, three_vh, three_idx = stap[i]
                '''
                append a fifth element to coordinate list; if staple is hybridized to a scaffold
                domain at the location, get the index of the scaffold domain on scaffold
                strandset and append it; if not hybridized to any scaffold domain, append -1.
                '''
                if five_vh == -1 and three_vh == -1:
                    stap[i].append(-1) # null base, cannot hybridize to any domain
                    continue  # null base
                elif hybridized(scaf[i]): # true if hybridized to scaffold at the position
                    scafNum = getStrandIdx(i,scaf_seg,vh_num=vh_num) # get hybridized scaffold domain index
                    stap[i].append(scafNum)

                if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                    stap_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    stap_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx):
                    stap_xo[vh_num].append((i, three_vh, three_idx))

        assert (len(stap_seg[vh_num]) % 2 == 0)
        # update scaf .json, append hybridized strand index to coordinate
        for i in range(len(scaf)):
                five_vh, five_idx, three_vh, three_idx = scaf[i]
                if five_vh == -1 and three_vh == -1:
                    scaf[i].append(-1)
                    continue  # null base
                elif hybridized(stap[i]):
                    stapNum = getStrandIdx(i,stap_seg,vh_num=vh_num)
                    scaf[i].append(stapNum)

        new_scaf_seg = []
        # install scaffold segments
        #TODO: Reem, whenever you see install, this is adding/rendering objects
        for i in range(0, len(scaf_seg[vh_num]), 2):
            low_idx = scaf_seg[vh_num][i]
            high_idx = scaf_seg[vh_num][i + 1]
            # split scaffold domain based on staple domain hybridized to
            installStrandSet(low_idx,high_idx,scaf,scaf_strand_set,new_scaf_seg)

        new_stap_seg = []
        #install staple segments
        for i in range(0, len(stap_seg[vh_num]), 2):
            low_idx = stap_seg[vh_num][i]
            high_idx = stap_seg[vh_num][i + 1]
            # split staple domain based on scaffold domain hybridized to
            installStrandSet(low_idx,high_idx,stap,stap_strand_set,new_stap_seg)

        # rename staple based on complement domain on scaffold
        for domain in vh._stap_strand_set:
            stap_idx = domain._low_idx
            scaf_domain_idx = getStrandIdx(stap_idx,new_scaf_seg)
            if scaf_domain_idx is not None:
                domain.setName(scaf_domain_idx)
            else:
                domain.setName(-1) #Inner-staple single-stranded region


      # INSTALL XOVERS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        from_vh = part.virtualHelixAtCoord((row, col))
        scaf_strand_set = from_vh.scaffoldStrandSet()
        stap_strand_set = from_vh.stapleStrandSet()
        # install scaffold xovers
        for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]:
            strand5p = scaf_strand_set.getStrand(idx5p)
            to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num])
            strand3p = to_vh.scaffoldStrandSet().getStrand(idx3p)
            assert strand5p._vhNum == from_vh._number and strand3p._vhNum == to_vh_num
            part.createXover(strand5p, idx5p, strand3p, idx3p,
                 update_oligo=False, use_undostack=False)

        for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]:
            strand5p = stap_strand_set.getStrand(idx5p)
            to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num])
            strand3p = to_vh.stapleStrandSet().getStrand(idx3p)

            part.createXover(strand5p, idx5p, strand3p, idx3p,
                update_oligo=False, use_undostack=False)

    # connect domains into oligos; domains linked by xovers will be on the same oligo
    RefreshOligosCommand(part, include_scaffold=True,
        colors=(prefs.DEFAULT_SCAF_COLOR, prefs.DEFAULT_STAP_COLOR)).redo()

   #SET DEFAULT COLOR
    for oligo in part.oligos():
        if oligo.isStaple():
            default_color = prefs.DEFAULT_STAP_COLOR
        else:
            default_color = prefs.DEFAULT_SCAF_COLOR
        oligo.applyColor(default_color, use_undostack=False)

 #   COLORS, INSERTIONS, SKIPS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']
        vh = part.virtualHelixAtCoord((row, col))
        scaf_strand_set = vh.scaffoldStrandSet()
        stap_strand_set = vh.stapleStrandSet()
        # install insertions and skips
    #     for base_idx in range(len(stap)):
    #         sum_of_insert_skip = insertions[base_idx] + skips[base_idx]
    #         if sum_of_insert_skip != 0:
    #             strand = scaf_strand_set.getStrand(base_idx)
    #             strand.addInsertion(base_idx, sum_of_insert_skip, use_undostack=False)
    #     # end for
    #     # populate colors
        for base_idx, color_number in helix['stap_colors']:
            color = Color(  (color_number >> 16) & 0xFF,
                            (color_number >> 8) & 0xFF,
                            color_number & 0xFF).name()
            strand = stap_strand_set.getStrand(base_idx)
            strand.oligo().applyColor(color, use_undostack=False)
    #
    # if 'oligos' in obj:
    #     for oligo in obj['oligos']:
    #         vh_num = oligo['vh_num']
    #         idx = oligo['idx']
    #         seq = str(oligo['seq']) if oligo['seq'] is not None else ''
    #         if seq != '':
    #             coord = vh_num_to_coord[vh_num]
    #             vh = part.virtualHelixAtCoord(coord)
    #             scaf_ss = vh.scaffoldStrandSet()
    #             # stapStrandSet = vh.stapleStrandSet()
    #             strand = scaf_ss.getStrand(idx)
    #             # print "sequence", seq, vh, idx,  strand.oligo()._strand5p
    #             strand.oligo().applySequence(seq, use_undostack=False)
    # if 'modifications' in obj:
    #     # print("AD", cadnano.app().activeDocument)
    #     # win = cadnano.app().activeDocument.win
    #     # modstool = win.pathToolManager.modsTool
    #     # modstool.connectSignals(part)
    #     for mod_id, item in obj['modifications'].items():
    #         if mod_id != 'int_instances' and mod_id != 'ext_instances':
    #             part.createMod(item, mod_id)
    #     for key, mid in obj['modifications']['ext_instances'].items():
    #         strand, idx = part.getModStrandIdx(key)
    #         try:
    #             strand.addMods(mid, idx, use_undostack=False)
    #         except:
    #             print(strand, idx)
    #             raise
    #     for key in obj['modifications']['int_instances'].items():
    #         strand, idx = part.getModStrandIdx(key)
    #         try:
    #             strand.addMods(mid, idx, use_undostack=False)
    #         except:
    #             print(strand, idx)
    #             raise
        #modstool.disconnectSignals(part)
 #end def
 #
    # user not allowed to modify file after import
    document.undoStack().clear()
Esempio n. 10
0
def decode(document, obj, emit_signals=False):
    """Parses a dictionary (obj) created from reading a json file and uses it
    to populate the given document with model data.
    """
    num_bases = len(obj['vstrands'][0]['scaf'])
    if num_bases % 32 == 0:
        lattice_type = LatticeType.SQUARE
    elif num_bases % 21 == 0:
        lattice_type = LatticeType.HONEYCOMB
    else:
        raise IOError("error decoding number of bases")

    part = None
    # DETERMINE MAX ROW,COL
    max_row_json = max_col_json = 0
    for helix in obj['vstrands']:
        max_row_json = max(max_row_json, int(helix['row'])+1)
        max_col_json = max(max_col_json, int(helix['col'])+1)

    # CREATE PART ACCORDING TO LATTICE TYPE
    if lattice_type == LatticeType.HONEYCOMB:
        doLattice = HoneycombDnaPart.legacyLatticeCoordToPositionXY
        isEven = HoneycombDnaPart.isEvenParity
    elif lattice_type == LatticeType.SQUARE:
        doLattice = SquareDnaPart.legacyLatticeCoordToPositionXY
        isEven = SquareDnaPart.isEvenParity
    else:
        raise TypeError("Lattice type not recognized")
    part = document.createNucleicAcidPart(use_undostack=False)
    part.setActive(True)
    setBatch(True)
    delta = num_bases - 42
    # POPULATE VIRTUAL HELICES
    ordered_id_list = []
    vh_num_to_coord = {}
    min_row, max_row = 10000, -10000
    min_col, max_col = 10000, -10000

    # find row, column limits
    for helix in obj['vstrands']:
        row = helix['row']
        if row < min_row:
            min_row = row
        if row > max_row:
            max_row = row
        col = helix['col']
        if col < min_col:
            min_col = col
        if col > max_col:
            max_col = col
    # end for

    delta_row = (max_row + min_row) // 2
    # 2 LINES COMMENTED OUT BY NC, doesn't appear to be necessary for honeycomb
    # if delta_row & 1:
    #     delta_row += 1
    delta_column = (max_col + min_col) // 2
    if delta_column & 1:
        delta_column += 1

    # print("Found cadnano version 2 file")
    # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row))
    # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column))

    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf= helix['scaf']
        # align row and columns to the center 0, 0
        coord = (row -  delta_row, col - delta_column)
        vh_num_to_coord[vh_num] = coord
        ordered_id_list.append(vh_num)
    # end for

    # make sure we retain the original order
    radius = DEFAULT_RADIUS
    for vh_num in sorted(vh_num_to_coord.keys()):
        row, col = vh_num_to_coord[vh_num]
        x, y = doLattice(radius, row, col)
        # print("%d:" % vh_num, x, y)
        part.createVirtualHelix(x, y, 0., num_bases,
                                id_num=vh_num, use_undostack=False)
    # zoom to fit
    if emit_signals:
        part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True)
    if not getReopen():
        setBatch(False)
    part.setImportedVHelixOrder(ordered_id_list)
    setReopen(False)
    setBatch(False)

    # INSTALL STRANDS AND COLLECT XOVER LOCATIONS
    scaf_seg = defaultdict(list)
    scaf_xo = defaultdict(list)
    stap_seg = defaultdict(list)
    stap_xo = defaultdict(list)
    try:
        for helix in obj['vstrands']:
            vh_num = helix['num']
            row, col = vh_num_to_coord[vh_num]
            scaf = helix['scaf']
            stap = helix['stap']
            insertions = helix['loop']
            skips = helix['skip']

            if isEven(row, col):
                scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num)
            else:
                stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num)

            # validate file serialization of lists
            assert( len(scaf) == len(stap) and
                    len(scaf) == len(insertions) and
                    len(insertions) == len(skips) )

            # read scaffold segments and xovers
            for i in range(len(scaf)):
                five_vh, five_idx, three_vh, three_idx = scaf[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                    scaf_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    scaf_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx):
                    scaf_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(scaf_seg[vh_num]) % 2 == 0)

            # install scaffold segments
            for i in range(0, len(scaf_seg[vh_num]), 2):
                low_idx = scaf_seg[vh_num][i]
                high_idx = scaf_seg[vh_num][i + 1]
                scaf_strand_set.createStrand(low_idx, high_idx, use_undostack=False)

            # read staple segments and xovers
            for i in range(len(stap)):
                five_vh, five_idx, three_vh, three_idx = stap[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                    stap_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    stap_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx):
                    stap_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(stap_seg[vh_num]) % 2 == 0)

            # install staple segments
            for i in range(0, len(stap_seg[vh_num]), 2):
                low_idx = stap_seg[vh_num][i]
                high_idx = stap_seg[vh_num][i + 1]
                stap_strand_set.createStrand(low_idx, high_idx, use_undostack=False)
            part.refreshSegments(vh_num)
        # end for
    except AssertionError:
        print("Unrecognized file format.")
        raise

    # INSTALL XOVERS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]

        if isEven(row, col):
            scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num)
        else:
            stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num)

        # install scaffold xovers
        for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            try:
                strand5p = scaf_strand_set.getStrand(idx5p)
            except:
                print(vh_num, idx5p)
                print(scaf_strand_set.strand_heap)
                print(stap_strand_set.strand_heap)
                raise
            coord = vh_num_to_coord[to_vh_num]
            if isEven(*coord):
                to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num)
            else:
                to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num)
            strand3p = to_scaf_strand_set.getStrand(idx3p)
            part.createXover(   strand5p, idx5p,
                                strand3p, idx3p,
                                allow_reordering=True,
                                update_oligo=False,
                                use_undostack=False)

        # install staple xovers
        for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = stap_strand_set.getStrand(idx5p)
            coord = vh_num_to_coord[to_vh_num]
            if isEven(*coord):
                to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num)
            else:
                to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num)
            strand3p = to_stap_strand_set.getStrand(idx3p)
            part.createXover(   strand5p, idx5p,
                                strand3p, idx3p,
                                allow_reordering=True,
                                update_oligo=False,
                                use_undostack=False)

    # need to heal all oligo connections into a continuous
    # oligo for the next steps
    RefreshOligosCommand(part).redo()

    # COLORS, INSERTIONS, SKIPS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row, col = vh_num_to_coord[vh_num]
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']

        if isEven(row, col):
            scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num)
        else:
            stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num)

        # install insertions and skips
        for base_idx in range(len(stap)):
            sum_of_insert_skip = insertions[base_idx] + skips[base_idx]
            if sum_of_insert_skip != 0:
                strand = scaf_strand_set.getStrand(base_idx)
                strand.addInsertion(base_idx,
                                    sum_of_insert_skip,
                                    use_undostack=False)
        # end for
        # populate colors
        for base_idx, color_number in helix['stap_colors']:
            color = intToColorHex(color_number)
            strand = stap_strand_set.getStrand(base_idx)
            strand.oligo().applyColor(color, use_undostack=False)

    if 'oligos' in obj:
        for oligo in obj['oligos']:
            vh_num = oligo['vh_num']
            idx = oligo['idx']
            seq = str(oligo['seq']) if oligo['seq'] is not None else ''
            if seq != '':
                coord = vh_num_to_coord[vh_num]
                if isEven(*coord):
                    scaf_ss, stap_ss = part.getStrandSets(vh_num)
                else:
                    stap_ss, scaf_ss = part.getStrandSets(vh_num)
                strand = scaf_ss.getStrand(idx)
                # print "sequence", seq, vh, idx,  strand.oligo()._strand5p
                strand.oligo().applySequence(seq, use_undostack=False)
    if 'modifications' in obj:
        for mod_id, item in obj['modifications'].items():
            if mod_id != 'int_instances' and mod_id != 'ext_instances':
                part.createMod(item, mod_id)
        for key, mid in obj['modifications']['ext_instances'].items():
            # strand, idx, coord, isstaple = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except:
                print(strand, idx)
                raise
        for key, mid in obj['modifications']['int_instances'].items():
            # strand, idx, coord, isstaple  = part.getModStrandIdx(key)
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(document, mid, idx, use_undostack=False)
            except:
                print(strand, idx)
                raise
Esempio n. 11
0
def decode(document, obj):
    """
    Parses a dictionary (obj) created from reading a json file and uses it
    to populate the given document with model data.
    """
    num_bases = len(obj['vstrands'][0]['scaf'])
    if num_bases % 32 == 0:
        lattice_type = LatticeType.SQUARE
    elif num_bases % 21 == 0:
        lattice_type = LatticeType.HONEYCOMB
    else:
        raise IOError("error decoding number of bases")


    # DETERMINE MAX ROW,COL
    max_row_json = max_col_json = 0
    for helix in obj['vstrands']:
        max_row_json = max(max_row_json, int(helix['row'])+1)
        max_col_json = max(max_col_json, int(helix['col'])+1)

    # CREATE PART ACCORDING TO LATTICE TYPE
    if lattice_type == LatticeType.HONEYCOMB:
        steps = num_bases // 21
        # num_rows = max(30, max_row_json, cadnano.app().prefs.honeycombRows)
        # num_cols = max(32, max_col_json, cadnano.app().prefs.honeycombCols)
        num_rows = max(30, max_row_json, prefs.HONEYCOMB_PART_MAXROWS)
        num_cols = max(32, max_col_json, prefs.HONEYCOMB_PART_MAXCOLS)
        part = document.addHoneycombPart(num_rows, num_cols, steps)
    elif lattice_type == LatticeType.SQUARE:
        is_SQ_100 = True  # check for custom SQ100 format
        for helix in obj['vstrands']:
            if helix['col'] != 0:
                is_SQ_100 = False
                break
        if is_SQ_100:
            # num_rows, num_cols = 100, 1
            num_rows, num_cols = 40, 30
        else:
            num_rows, num_cols = 40, 30
        steps = num_bases // 32
        # num_rows = max(30, max_row_json, cadnano.app().prefs.squareRows)
        # num_cols = max(32, max_col_json, cadnano.app().prefs.squareCols)
        num_rows = max(30, max_row_json, prefs.SQUARE_PART_MAXROWS)
        num_cols = max(32, max_col_json, prefs.SQUARE_PART_MAXCOLS)
        part = document.addSquarePart(num_rows, num_cols, steps)
        # part = SquarePart(document=document, max_row=num_rows, max_col=num_cols, max_steps=steps)
    else:
        raise TypeError("Lattice type not recognized")
    # document._addPart(part, use_undostack=False)
    setBatch(True)
    # POPULATE VIRTUAL HELICES
    ordered_coord_list = []
    vh_num_to_coord = {}
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf= helix['scaf']
        coord = (row, col)
        vh_num_to_coord[vh_num] = coord
        ordered_coord_list.append(coord)
    # make sure we retain the original order
    for vh_num in sorted(vh_num_to_coord.keys()):
        row, col = vh_num_to_coord[vh_num]
        part.createVirtualHelix(row, col, use_undostack=False)
    if not getReopen():
        setBatch(False)
    part.setImportedVHelixOrder(ordered_coord_list)
    setReopen(False)
    setBatch(False)
    
    # INSTALL STRANDS AND COLLECT XOVER LOCATIONS
    num_helices = len(obj['vstrands']) - 1
    scaf_seg = defaultdict(list)
    scaf_xo = defaultdict(list)
    stap_seg = defaultdict(list)
    stap_xo = defaultdict(list)
    try:
        for helix in obj['vstrands']:
            vh_num = helix['num']
            row = helix['row']
            col = helix['col']
            scaf = helix['scaf']
            stap = helix['stap']
            insertions = helix['loop']
            skips = helix['skip']
            vh = part.virtualHelixAtCoord((row, col))
            scaf_strand_set = vh.scaffoldStrandSet()
            stap_strand_set = vh.stapleStrandSet()
            assert(len(scaf) == len(stap) and len(stap) == part.maxBaseIdx() + 1 and\
                   len(scaf) == len(insertions) and len(insertions) == len(skips))
            # read scaffold segments and xovers
            for i in range(len(scaf)):
                five_vh, five_idx, three_vh, three_idx = scaf[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                    scaf_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    scaf_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx):
                    scaf_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(scaf_seg[vh_num]) % 2 == 0)
            # install scaffold segments
            for i in range(0, len(scaf_seg[vh_num]), 2):
                low_idx = scaf_seg[vh_num][i]
                high_idx = scaf_seg[vh_num][i + 1]
                scaf_strand_set.createStrand(low_idx, high_idx, use_undostack=False)
            # read staple segments and xovers
            for i in range(len(stap)):
                five_vh, five_idx, three_vh, three_idx = stap[i]
                if five_vh == -1 and three_vh == -1:
                    continue  # null base
                if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\
                                       five_idx, three_vh, three_idx):
                    stap_seg[vh_num].append(i)
                if five_vh != vh_num and three_vh != vh_num:  # special case
                    stap_seg[vh_num].append(i)  # end segment on a double crossover
                if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx):
                    stap_xo[vh_num].append((i, three_vh, three_idx))
            assert (len(stap_seg[vh_num]) % 2 == 0)
            # install staple segments
            for i in range(0, len(stap_seg[vh_num]), 2):
                low_idx = stap_seg[vh_num][i]
                high_idx = stap_seg[vh_num][i + 1]
                stap_strand_set.createStrand(low_idx, high_idx, use_undostack=False)
    except AssertionError:
        print("Unrecognized file format.")
        raise

    # INSTALL XOVERS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']
        from_vh = part.virtualHelixAtCoord((row, col))
        scaf_strand_set = from_vh.scaffoldStrandSet()
        stap_strand_set = from_vh.stapleStrandSet()
        # install scaffold xovers
        for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = scaf_strand_set.getStrand(idx5p)
            to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num])
            strand3p = to_vh.scaffoldStrandSet().getStrand(idx3p)
            part.createXover(strand5p, idx5p, strand3p, idx3p, 
                update_oligo=False, use_undostack=False)
        # install staple xovers
        for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]:
            # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p
            strand5p = stap_strand_set.getStrand(idx5p)
            to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num])
            strand3p = to_vh.stapleStrandSet().getStrand(idx3p)
            part.createXover(strand5p, idx5p, strand3p, idx3p, 
                update_oligo=False, use_undostack=False)

    # need to heal all oligo connections into a continuous
    # oligo for the next steps
    RefreshOligosCommand(part, include_scaffold=True, 
        colors=(prefs.DEFAULT_SCAF_COLOR, prefs.DEFAULT_STAP_COLOR)).redo()

    # SET DEFAULT COLOR
    # for oligo in part.oligos():
    #     if oligo.isStaple():
    #         default_color = prefs.DEFAULT_STAP_COLOR
    #     else:
    #         default_color = prefs.DEFAULT_SCAF_COLOR
    #     oligo.applyColor(default_color, use_undostack=False)

    # COLORS, INSERTIONS, SKIPS
    for helix in obj['vstrands']:
        vh_num = helix['num']
        row = helix['row']
        col = helix['col']
        scaf = helix['scaf']
        stap = helix['stap']
        insertions = helix['loop']
        skips = helix['skip']
        vh = part.virtualHelixAtCoord((row, col))
        scaf_strand_set = vh.scaffoldStrandSet()
        stap_strand_set = vh.stapleStrandSet()
        # install insertions and skips
        for base_idx in range(len(stap)):
            sum_of_insert_skip = insertions[base_idx] + skips[base_idx]
            if sum_of_insert_skip != 0:
                strand = scaf_strand_set.getStrand(base_idx)
                strand.addInsertion(base_idx, sum_of_insert_skip, use_undostack=False)
        # end for
        # populate colors
        for base_idx, color_number in helix['stap_colors']:
            color = Color(  (color_number >> 16) & 0xFF, 
                            (color_number >> 8) & 0xFF, 
                            color_number & 0xFF).name()
            strand = stap_strand_set.getStrand(base_idx)
            strand.oligo().applyColor(color, use_undostack=False) 

    if 'oligos' in obj:
        for oligo in obj['oligos']:
            vh_num = oligo['vh_num']
            idx = oligo['idx']
            seq = str(oligo['seq']) if oligo['seq'] is not None else ''
            if seq != '':
                coord = vh_num_to_coord[vhNum]
                vh = part.virtualHelixAtCoord(coord)
                scaf_ss = vh.scaffoldStrandSet()
                # stapStrandSet = vh.stapleStrandSet()
                strand = scaf_ss.getStrand(idx)
                # print "sequence", seq, vh, idx,  strand.oligo()._strand5p
                strand.oligo().applySequence(seq, use_undostack=False)
    if 'modifications' in obj:
        # print("AD", cadnano.app().activeDocument)
        # win = cadnano.app().activeDocument.win
        # modstool = win.pathToolManager.modsTool
        # modstool.connectSignals(part)
        for mod_id, item in obj['modifications'].items():
            if mod_id != 'int_instances' and mod_id != 'ext_instances':
                part.createMod(item, mod_id)
        for key, mid in obj['modifications']['ext_instances'].items():
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(mid, idx, use_undostack=False)
            except:
                print(strand, idx)
                raise
        for key in obj['modifications']['int_instances'].items():
            strand, idx = part.getModStrandIdx(key)
            try:
                strand.addMods(mid, idx, use_undostack=False)
            except:
                print(strand, idx)
                raise