Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NCBI sequence_report.tsv alias adapter, with ability to recode NCBI fasta files to use UCSC style names #4516

Merged
merged 7 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"electron-mock-ipc": "^0.3.8",
"eslint": "^9.0.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "next",
"eslint-plugin-react-hooks": "^5.1.0-rc-d48603a5-20240813",
"eslint-plugin-react-refresh": "^0.4.3",
"eslint-plugin-tsdoc": "^0.3.0",
"eslint-plugin-unicorn": "^55.0.0",
Expand Down
189 changes: 116 additions & 73 deletions packages/core/assemblyManager/assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,14 @@ async function loadRefNameMap(
}),
)

// make the reversed map too
const reversed = Object.fromEntries(
Object.entries(refNameMap).map(([canonicalName, adapterName]) => [
adapterName,
canonicalName,
]),
)

return {
forwardMap: refNameMap,
reverseMap: reversed,
reverseMap: Object.fromEntries(
Object.entries(refNameMap).map(([canonicalName, adapterName]) => [
adapterName,
canonicalName,
]),
),
}
}

Expand Down Expand Up @@ -131,19 +128,12 @@ export interface BasicRegion {
assemblyName: string
}

export interface Loading {
adapterRegionsWithAssembly: Region[]
refNameAliases: RefNameAliases
lowerCaseRefNameAliases: RefNameAliases
cytobands: Feature[]
}

/**
* #stateModel Assembly
*/
export default function assemblyFactory(
assemblyConfigType: IAnyType,
pm: PluginManager,
pluginManager: PluginManager,
) {
const adapterLoads = new AbortablePromiseCache<CacheData, RefNameMap>({
cache: new QuickLRU({ maxSize: 1000 }),
Expand Down Expand Up @@ -172,14 +162,30 @@ export default function assemblyFactory(
})
.volatile(() => ({
error: undefined as unknown,
loaded: false,
loadingP: undefined as Promise<void> | undefined,
volatileRegions: undefined as BasicRegion[] | undefined,
refNameAliases: undefined as RefNameAliases | undefined,
lowerCaseRefNameAliases: undefined as RefNameAliases | undefined,
cytobands: undefined as Feature[] | undefined,
}))
.views(self => ({
/**
* #getter
*/
get lowerCaseRefNameAliases() {
return self.refNameAliases
? Object.fromEntries(
Object.entries(self.refNameAliases).map(([key, val]) => [
key.toLowerCase(),
val,
]),
)
: undefined
},
}))
.views(self => ({
/**
* #method
*/
getConf(arg: string) {
return self.configuration ? getConf(self, arg) : undefined
},
Expand All @@ -193,12 +199,14 @@ export default function assemblyFactory(
self.load()
return !!self.refNameAliases
},

/**
* #getter
*/
get name(): string {
return self.getConf('name') || ''
},

/**
* #getter
*/
Expand All @@ -207,12 +215,14 @@ export default function assemblyFactory(
self.load()
return self.volatileRegions
},

/**
* #getter
*/
get aliases(): string[] {
return self.getConf('aliases') || []
},

/**
* #getter
*/
Expand All @@ -225,12 +235,14 @@ export default function assemblyFactory(
hasName(name: string) {
return this.allAliases.includes(name)
},

/**
* #getter
*/
get allAliases() {
return [this.name, ...this.aliases]
},

/**
* #getter
* note: lowerCaseRefNameAliases not included here: this allows the list
Expand Down Expand Up @@ -291,6 +303,7 @@ export default function assemblyFactory(
'aliases not loaded, we expect them to be loaded before getCanonicalRefName can be called',
)
}

return (
self.refNameAliases[refName] || self.lowerCaseRefNameAliases[refName]
)
Expand All @@ -303,10 +316,9 @@ export default function assemblyFactory(
return undefined
}
const idx = self.refNames.indexOf(refName)
if (idx === -1) {
return undefined
}
return self.refNameColors[idx % self.refNameColors.length]
return idx === -1
? undefined
: self.refNameColors[idx % self.refNameColors.length]
},
/**
* #method
Expand All @@ -325,21 +337,22 @@ export default function assemblyFactory(
* #action
*/
setLoaded({
adapterRegionsWithAssembly,
regions,
refNameAliases,
lowerCaseRefNameAliases,
cytobands,
}: Loading) {
self.loaded = true
this.setRegions(adapterRegionsWithAssembly)
this.setRefNameAliases(refNameAliases, lowerCaseRefNameAliases)
}: {
regions: Region[]
refNameAliases: RefNameAliases
cytobands: Feature[]
}) {
this.setRegions(regions)
this.setRefNameAliases(refNameAliases)
this.setCytobands(cytobands)
},
/**
* #action
*/
setError(e: unknown) {
console.error(e)
self.error = e
},
/**
Expand All @@ -351,9 +364,8 @@ export default function assemblyFactory(
/**
* #action
*/
setRefNameAliases(aliases: RefNameAliases, lcAliases: RefNameAliases) {
setRefNameAliases(aliases: RefNameAliases) {
self.refNameAliases = aliases
self.lowerCaseRefNameAliases = lcAliases
},
/**
* #action
Expand Down Expand Up @@ -389,38 +401,51 @@ export default function assemblyFactory(
const sequenceAdapterConf = conf?.sequence.adapter
const assemblyName = self.name

const regions = await getAssemblyRegions(sequenceAdapterConf, pm)
const regions = await getAssemblyRegions({
config: sequenceAdapterConf,
pluginManager,
})

const adapterRegionsWithAssembly = regions.map(r => {
checkRefName(r.refName)
return { ...r, assemblyName }
})
const refNameAliases: RefNameAliases = {}

const ret = await getRefNameAliases(refNameAliasesAdapterConf, pm)
const cytobands = await getCytobands(cytobandAdapterConf, pm)
ret.forEach(({ refName, aliases }) => {
aliases.forEach(a => {
checkRefName(a)
refNameAliases[a] = refName
})
})
// add identity to the refNameAliases list
adapterRegionsWithAssembly.forEach(region => {
refNameAliases[region.refName] = region.refName
const refNameAliases = {} as Record<string, string>

const refNameAliasCollection = await getRefNameAliases({
config: refNameAliasesAdapterConf,
pluginManager,
})

const lowerCaseRefNameAliases = Object.fromEntries(
Object.entries(refNameAliases).map(([key, val]) => [
key.toLowerCase(),
val,
]),
)
for (const { refName, aliases, override } of refNameAliasCollection) {
for (const alias of aliases) {
checkRefName(alias)
refNameAliases[alias] = refName
}
// the override field is supplied by a RefNameAliasAdapter to make
// the refName field returned by the adapter to be used as the
// primary names for this assembly
if (override) {
refNameAliases[refName] = refName
}
}
// add identity to the refNameAliases list
for (const region of adapterRegionsWithAssembly) {
// this ||= means that if the refNameAliasAdapter already set a
// mapping for the primary region to be an alias
refNameAliases[region.refName] ||= region.refName
}

this.setLoaded({
adapterRegionsWithAssembly,
refNameAliases,
lowerCaseRefNameAliases,
cytobands,
regions: adapterRegionsWithAssembly.map(r => ({
...r,
refName: refNameAliases[r.refName] || r.refName,
})),
cytobands: await getCytobands({
config: cytobandAdapterConf,
pluginManager,
}),
})
},
}))
Expand All @@ -437,7 +462,7 @@ export default function assemblyFactory(
adapterConfigCacheKey(adapterConf),
{
adapterConf,
self: self as Assembly,
self,
options: rest,
} as CacheData,

Expand Down Expand Up @@ -477,34 +502,52 @@ export default function assemblyFactory(
}))
}

async function getRefNameAliases(
config: AnyConfigurationModel,
pm: PluginManager,
signal?: AbortSignal,
) {
const type = pm.getAdapterType(config.type)!
async function getRefNameAliases({
config,
pluginManager,
signal,
}: {
config: AnyConfigurationModel
pluginManager: PluginManager
signal?: AbortSignal
}) {
const type = pluginManager.getAdapterType(config.type)!
const CLASS = await type.getAdapterClass()
const adapter = new CLASS(config, undefined, pm) as BaseRefNameAliasAdapter
const adapter = new CLASS(
config,
undefined,
pluginManager,
) as BaseRefNameAliasAdapter
return adapter.getRefNameAliases({ signal })
}

async function getCytobands(config: AnyConfigurationModel, pm: PluginManager) {
const type = pm.getAdapterType(config.type)!
async function getCytobands({
config,
pluginManager,
}: {
config: AnyConfigurationModel
pluginManager: PluginManager
}) {
const type = pluginManager.getAdapterType(config.type)!
const CLASS = await type.getAdapterClass()
const adapter = new CLASS(config, undefined, pm)
const adapter = new CLASS(config, undefined, pluginManager)

// @ts-expect-error
return adapter.getData()
}

async function getAssemblyRegions(
config: AnyConfigurationModel,
pm: PluginManager,
signal?: AbortSignal,
) {
const type = pm.getAdapterType(config.type)!
async function getAssemblyRegions({
config,
pluginManager,
signal,
}: {
config: AnyConfigurationModel
pluginManager: PluginManager
signal?: AbortSignal
}) {
const type = pluginManager.getAdapterType(config.type)!
const CLASS = await type.getAdapterClass()
const adapter = new CLASS(config, undefined, pm) as RegionsAdapter
const adapter = new CLASS(config, undefined, pluginManager) as RegionsAdapter
return adapter.getRegions({ signal })
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BaseOptions } from './types'
export interface Alias {
refName: string
aliases: string[]
override?: boolean
}
export interface BaseRefNameAliasAdapter extends BaseAdapter {
getRefNameAliases(opts: BaseOptions): Promise<Alias[]>
Expand Down
Loading
Loading