Skip to content

Commit

Permalink
Fix "alias not iterable" bug (fixes #1747)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrobinso committed Jan 15, 2024
1 parent 0e952c3 commit 95836b7
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 16 deletions.
4 changes: 2 additions & 2 deletions js/bam/bamReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class BamReader {

async readAlignments(chr, bpStart, bpEnd) {

const chrId = await this.#getChrIdx(chr)
const chrId = await this.#getRefId(chr)
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config)

if (chrId === undefined) {
Expand All @@ -54,7 +54,7 @@ class BamReader {
}
}

async #getChrIdx(chr) {
async #getRefId(chr) {

await this.getHeader()

Expand Down
50 changes: 36 additions & 14 deletions js/cram/cramReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ class CramReader {
this.genome = genome

this.cramFile = new gmodCRAM.CramFile({
filehandle: new FileHandler(config.url, config),
filehandle: config.fileHandle ? config.fileHandle : new FileHandler(config.url, config),
seqFetch: config.seqFetch || seqFetch.bind(this),
checkSequenceMD5: config.checkSequenceMD5 !== undefined ? config.checkSequenceMD5 : true
})

const indexFileHandle = new FileHandler(config.indexURL, config)
const indexFileHandle = config.indexFileHandle ? config.indexFileHandle : new FileHandler(config.indexURL, config)
this.indexedCramFile = new gmodCRAM.IndexedCramFile({
cram: this.cramFile,
index: new gmodCRAM.CraiIndex({
Expand Down Expand Up @@ -85,7 +85,7 @@ class CramReader {
this.header = {
indexToChr: indexToChr,
chrToIndex: chrToIndex,
chrNames: new Set(header.chrToIndex),
chrNames: new Set(Object.keys(chrToIndex)),
readGroups: readGroups

}
Expand All @@ -94,23 +94,45 @@ class CramReader {
return this.header
}

async readAlignments(chr, bpStart, bpEnd) {
async #getRefId(chr) {

if (!this.chrAliasTable.has(chr)) {
const chromosome = this.genome.getChromosome(chr)
if (chromosome) {
const aliases = chromosome.altNames
for (let a of aliases) {
if (this.chrNames.has(a)) {
this.chrAliasTable.set(chr, a)
}
await this.getHeader()

if (this.chrAliasTable.has(chr)) {
chr = this.chrAliasTable.get(chr)
if (chr === undefined) {
return undefined
}
}

let refId = this.header.chrToIndex[chr]

// Try alias
if (refId === undefined) {
const aliasRecord = await this.genome.getAliasRecord(chr)
let alias
if (aliasRecord) {
const aliases = Object.keys(aliasRecord)
.filter(k => k !== "start" && k !== "end")
.map(k => aliasRecord[k])
.filter(a => undefined !== this.header.chrToIndex[a])
if (aliases.length > 0) {
alias = aliases[0]
refId = this.header.chrToIndex[aliases[0]]
}
}
this.chrAliasTable.set(chr, alias) // alias may be undefined => no alias exists. Setting prevents repeated attempts
}
return refId
}


async readAlignments(chr, bpStart, bpEnd) {

const header = await this.getHeader()
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr
const chrIdx = header.chrToIndex[queryChr]

const chrIdx = await this.#getRefId(chr)

const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config)

if (chrIdx === undefined) {
Expand Down
81 changes: 81 additions & 0 deletions test/testCRAM.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import "./utils/mockObjects.js"
import BamReader from "../js/bam/bamReader.js"
import {assert} from 'chai'
import BamReaderNonIndexed from "../js/bam/bamReaderNonIndexed.js"
import {createGenome} from "./utils/MockGenome.js"


const genome = createGenome()

suite("testBAM", function () {

test("BAM alignments - CSI index", async function () {

const start = 155140000
const end = 155160000

const bamReader = new BamReader({
type: 'bam',
url: 'test/data/bam/na12889.cram',
indexURL: 'test/data/bam/na12889.cram.crai'
},
genome)


alignmentContainer = await bamReader.readAlignments("1", start, end)
validate(assert, alignmentContainer)

})


function validate(assert, alignmentContainer) {
assert.ok(alignmentContainer)

// 2 alignments, 1 paired and 1 single
assert.equal(alignmentContainer.alignments.length, 2)

const firstAlignment = alignmentContainer.alignments[0].firstAlignment

assert.equal(firstAlignment.seq, 'TTCATCTAAAAATCACATTGCAAATTATTCAATATATTTGGGCCTCCATCTCGTTTACATCAATATGTGTTTGTTGAAGTATCTGCCCTGCAATGTCCATA')

assert.deepEqual(firstAlignment.qual, [34, 9, 12, 10, 24, 17, 10, 5, 19, 7, 28, 17, 23, 29, 10,
26, 10, 8, 14, 7, 15, 17, 32, 33, 31, 23, 34, 16, 33, 28, 34, 27, 10, 29, 10, 17, 11, 26,
8, 27, 4, 4, 35, 32, 12, 32, 40, 39, 38, 41, 40, 36, 3, 34, 17, 30, 37, 10, 29, 36, 41,
35, 24, 34, 11, 19, 11, 16, 24, 16, 26, 10, 11, 19, 13, 18, 11, 28, 30, 37, 30, 38, 43,
43, 40, 43, 43, 41, 32, 34, 39, 41, 31, 37, 36, 36, 36, 33, 37, 34, 30])

assert.equal(firstAlignment.start, 155148856)
assert.equal(firstAlignment.scStart, 155148856 - 20)
assert.equal(firstAlignment.lengthOnRef, 81)
assert.equal(firstAlignment.scLengthOnRef, 101)
assert.equal(firstAlignment.pairOrientation, 'F2R1')
assert.equal(firstAlignment.fragmentLength, 307)
assert.equal(firstAlignment.mq, 29)

const blocks = firstAlignment.blocks
assert.equal(blocks.length, 4)
const expectedLength = [20, 46, 8, 24]
const expectedOffset = [0, 20, 66, 77]
const expectedTypes = ['S', 'M', 'M', 'M']
for (let i = 0; i < 4; i++) {
const b = blocks[i]
assert.equal(b.len, expectedLength[i])
assert.equal(b.type, expectedTypes[i])
assert.equal(b.seqOffset, expectedOffset[i])
}

const insertions = firstAlignment.insertions
assert.equal(insertions.length, 1)
assert.equal(insertions[0].len, 3)
assert.equal(insertions[0].type, 'I')

const tags = firstAlignment.tags()
assert.equal(tags["BQ"], "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TPEO@@KPXPZJKS@@@@@@@@@@@@@@@@@@@@@@@@@@@")
assert.equal(tags["AM"], 29)
assert.equal(tags["MQ"], 29)
assert.equal(tags["AM"], 29)
assert.equal(tags["XT"], "M")
}

})

29 changes: 29 additions & 0 deletions test/utils/JBrowseFileHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import fs from 'fs';

export default class LocalFile {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
constructor(source, opts = {}) {
this.filename = source;
}
getFd() {
if (!this.fd) {
this.fd = fs.openSync(this.filename, 'r');
}
return this.fd;
}
async read(buffer, offset = 0, length, position = 0) {
const fetchLength = Math.min(buffer.length - offset, length);
const ret = await fs.readSync(await this.getFd(), buffer, offset, fetchLength, position);
return { bytesRead: ret, buffer };
}
async readFile(options) {
return fs.readFileSync(this.filename, options);
}
// todo memoize
async stat() {
return fs.statSync(this.filename);
}
async close() {
return fs.closeSync(this.filename);
}
}

0 comments on commit 95836b7

Please sign in to comment.