Skip to content

Commit

Permalink
Upgrade to @gmod/bam v2.0.0 (#3751)
Browse files Browse the repository at this point in the history
* Bump @gmod/bam to 2.0.0

* Slightly easier to reason about statuscallback updates
  • Loading branch information
cmdcolin committed Jun 8, 2023
1 parent 27484b9 commit 8261893
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 104 deletions.
2 changes: 1 addition & 1 deletion plugins/alignments/package.json
Expand Up @@ -37,7 +37,7 @@
"clean": "rimraf dist esm *.tsbuildinfo"
},
"dependencies": {
"@gmod/bam": "^1.1.15",
"@gmod/bam": "^2.0.0",
"@gmod/cram": "^1.7.1",
"@mui/icons-material": "^5.0.1",
"canvas2svg": "^1.0.16",
Expand Down
79 changes: 40 additions & 39 deletions plugins/alignments/src/BamAdapter/BamAdapter.ts
Expand Up @@ -43,8 +43,6 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
// chunkSizeLimit and fetchSizeLimit are more troublesome than
// helpful, and have given overly large values on the ultra long
// nanopore reads even with 500MB limits, so disabled with infinity
chunkSizeLimit: Infinity,
fetchSizeLimit: Infinity,
yieldThreadTime: Infinity,
})

Expand Down Expand Up @@ -89,7 +87,7 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
const idToName: string[] = []
const nameToId: Record<string, number> = {}
samHeader
.filter(l => l.tag === 'SQ')
?.filter(l => l.tag === 'SQ')
.forEach((sqLine, refId) => {
sqLine.data.forEach(item => {
if (item.tag === 'SN') {
Expand Down Expand Up @@ -182,46 +180,50 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
return ObservableCreate<Feature>(async observer => {
const { bam } = await this.configure()
await this.setup(opts)
statusCallback('Downloading alignments')
const records = await bam.getRecordsForRange(refName, start, end, opts)

const {
flagInclude = 0,
flagExclude = 0,
tagFilter,
readName,
} = filterBy || {}

for (const record of records) {
let ref: string | undefined
if (!record.get('MD')) {
ref = await this.seqFetch(
originalRefName || refName,
record.get('start'),
record.get('end'),
)
}
const records = await updateStatus(
'Downloading alignments',
statusCallback,
() => bam.getRecordsForRange(refName, start, end, opts),
)

const flags = record.flags
if ((flags & flagInclude) !== flagInclude && !(flags & flagExclude)) {
continue
}
await updateStatus('Processing alignments', statusCallback, async () => {
const {
flagInclude = 0,
flagExclude = 0,
tagFilter,
readName,
} = filterBy || {}

for (const record of records) {
let ref: string | undefined
if (!record.get('MD')) {
ref = await this.seqFetch(
originalRefName || refName,
record.get('start'),
record.get('end'),
)
}

if (tagFilter) {
const v = record.get(tagFilter.tag)
if (!(v === '*' ? v !== undefined : `${v}` === tagFilter.value)) {
const flags = record.flags
if ((flags & flagInclude) !== flagInclude && !(flags & flagExclude)) {
continue
}
}

if (readName && record.get('name') !== readName) {
continue
}
if (tagFilter) {
const v = record.get(tagFilter.tag)
if (!(v === '*' ? v !== undefined : `${v}` === tagFilter.value)) {
continue
}
}

observer.next(new BamSlightlyLazyFeature(record, this, ref))
}
statusCallback('')
observer.complete()
if (readName && record.get('name') !== readName) {
continue
}

observer.next(new BamSlightlyLazyFeature(record, this, ref))
}
observer.complete()
})
}, signal)
}

Expand All @@ -231,8 +233,7 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
) {
const { bam } = await this.configure()
// this is a method to avoid calling on htsget adapters
// @ts-expect-error
if (bam.index.filehandle !== '?') {
if (bam.index) {
const bytes = await bytesForRegions(regions, bam)
const fetchSizeLimit = this.getConf('fetchSizeLimit')
return { bytes, fetchSizeLimit }
Expand Down
121 changes: 65 additions & 56 deletions plugins/alignments/src/CramAdapter/CramAdapter.ts
Expand Up @@ -4,7 +4,12 @@ import {
BaseOptions,
BaseSequenceAdapter,
} from '@jbrowse/core/data_adapters/BaseAdapter'
import { checkAbortSignal, Region, Feature } from '@jbrowse/core/util'
import {
checkAbortSignal,
Region,
Feature,
updateStatus,
} from '@jbrowse/core/util'
import { openLocation } from '@jbrowse/core/util/io'
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
import { toArray } from 'rxjs/operators'
Expand Down Expand Up @@ -142,36 +147,36 @@ export default class CramAdapter extends BaseFeatureDataAdapter {

private async setupPre(opts?: BaseOptions) {
const { statusCallback = () => {} } = opts || {}
const conf = await this.configure()
statusCallback('Downloading index')
const { cram } = conf
const samHeader = await cram.cram.getSamHeader()

// use the @SQ lines in the header to figure out the
// mapping between ref ID numbers and names
const idToName: string[] = []
const nameToId: Record<string, number> = {}
samHeader
.filter(l => l.tag === 'SQ')
.forEach((sqLine, refId) => {
sqLine.data.forEach(item => {
if (item.tag === 'SN') {
// this is the ref name
const refName = item.value
nameToId[refName] = refId
idToName[refId] = refName
}
return updateStatus('Downloading index', statusCallback, async () => {
const conf = await this.configure()
const { cram } = conf
const samHeader = await cram.cram.getSamHeader()

// use the @SQ lines in the header to figure out the
// mapping between ref ID numbers and names
const idToName: string[] = []
const nameToId: Record<string, number> = {}
samHeader
.filter(l => l.tag === 'SQ')
.forEach((sqLine, refId) => {
sqLine.data.forEach(item => {
if (item.tag === 'SN') {
// this is the ref name
const refName = item.value
nameToId[refName] = refId
idToName[refId] = refName
}
})
})
})

const readGroups = samHeader
.filter(l => l.tag === 'RG')
.map(rgLine => rgLine.data.find(item => item.tag === 'ID')?.value)
const readGroups = samHeader
.filter(l => l.tag === 'RG')
.map(rgLine => rgLine.data.find(item => item.tag === 'ID')?.value)

const data = { idToName, nameToId, readGroups }
statusCallback('')
this.samHeader = data
return { samHeader: data, ...conf }
const data = { idToName, nameToId, readGroups }
this.samHeader = data
return { samHeader: data, ...conf }
})
}

private async setup(opts?: BaseOptions) {
Expand Down Expand Up @@ -236,40 +241,44 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
if (originalRefName) {
this.seqIdToOriginalRefName[refId] = originalRefName
}
statusCallback('Downloading alignments')
const records = await cram.getRecordsForRange(refId, start, end)
const records = await updateStatus(
'Downloading alignments',
statusCallback,
() => cram.getRecordsForRange(refId, start, end),
)
checkAbortSignal(signal)
const {
flagInclude = 0,
flagExclude = 0,
tagFilter,
readName,
} = filterBy || {}

for (const record of records) {
const flags = record.flags
if ((flags & flagInclude) !== flagInclude && !(flags & flagExclude)) {
continue
}

if (tagFilter) {
const v =
tagFilter.tag === 'RG'
? this.samHeader.readGroups?.[record.readGroupId]
: record.tags[tagFilter.tag]
if (!(v === '*' ? v !== undefined : `${v}` === tagFilter.value)) {
await updateStatus('Processing alignments', statusCallback, () => {
const {
flagInclude = 0,
flagExclude = 0,
tagFilter,
readName,
} = filterBy || {}

for (const record of records) {
const flags = record.flags
if ((flags & flagInclude) !== flagInclude && !(flags & flagExclude)) {
continue
}
}

if (readName && record.readName !== readName) {
continue
if (tagFilter) {
const v =
tagFilter.tag === 'RG'
? this.samHeader.readGroups?.[record.readGroupId]
: record.tags[tagFilter.tag]
if (!(v === '*' ? v !== undefined : `${v}` === tagFilter.value)) {
continue
}
}

if (readName && record.readName !== readName) {
continue
}
observer.next(this.cramRecordToFeature(record))
}
observer.next(this.cramRecordToFeature(record))
}

statusCallback('')
observer.complete()
observer.complete()
})
}, signal)
}

Expand Down
14 changes: 6 additions & 8 deletions yarn.lock
Expand Up @@ -1917,19 +1917,17 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==

"@gmod/bam@^1.1.15":
version "1.1.18"
resolved "https://registry.yarnpkg.com/@gmod/bam/-/bam-1.1.18.tgz#de28533289f0f659f962a59226d9c342590a0d50"
integrity sha512-2Sn4zLV7DKYyrmYbNJRMchKGfhfIVm6LZEl2h7MSuGmAEtGfa1RBVOfG8Cu6VDC+lIFrgS4ys4vUFFxaWaxgSQ==
"@gmod/bam@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@gmod/bam/-/bam-2.0.0.tgz#a3be53304bd723b90e245823db9cf7ac26421603"
integrity sha512-jzm19zQcDGtKApr9vs1WAMfjfjdRVQTvYfyTcdbLwbCILG0bYqQLCnptrKRqV1w5powOBmcXwUu1Pm2NORnjkQ==
dependencies:
"@gmod/bgzf-filehandle" "^1.4.4"
abortable-promise-cache "^1.5.0"
buffer-crc32 "^0.2.13"
cross-fetch "^3.0.2"
generic-filehandle "^3.0.0"
long "^4.0.0"
object.entries-ponyfill "^1.0.1"
quick-lru "^2.0.0"
quick-lru "^4.0.0"

"@gmod/bbi@^4.0.0":
version "4.0.0"
Expand Down Expand Up @@ -7975,7 +7973,7 @@ cross-env@^7.0.2:
dependencies:
cross-spawn "^7.0.1"

cross-fetch@^3.0.0, cross-fetch@^3.0.2, cross-fetch@^3.0.4:
cross-fetch@^3.0.0, cross-fetch@^3.0.4:
version "3.1.6"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.6.tgz#bae05aa31a4da760969756318feeee6e70f15d6c"
integrity sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==
Expand Down

0 comments on commit 8261893

Please sign in to comment.