diff --git a/package.json b/package.json index 0987ee3..3f62ef8 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@nightingale-elements/nightingale-navigation": "5.6.0", "@nightingale-elements/nightingale-sequence": "5.6.0", "@nightingale-elements/nightingale-sequence-heatmap": "5.6.2", - "@nightingale-elements/nightingale-structure": "5.7.0", + "@nightingale-elements/nightingale-structure": "5.8.0", "@nightingale-elements/nightingale-track-canvas": "5.6.0", "@nightingale-elements/nightingale-variation": "5.6.0", "color-hash": "2.0.2", diff --git a/src/adapters/alphafold-confidence-adapter.ts b/src/adapters/alphafold-confidence-adapter.ts index ed90ab4..ccbd51e 100644 --- a/src/adapters/alphafold-confidence-adapter.ts +++ b/src/adapters/alphafold-confidence-adapter.ts @@ -1,4 +1,4 @@ -import { AlphafoldPayload } from './types/alphafold'; +import { AlphaFoldPayload } from '@nightingale-elements/nightingale-structure'; type AlphafoldConfidencePayload = { residueNumber: Array; @@ -6,7 +6,7 @@ type AlphafoldConfidencePayload = { confidenceCategory: Array; }; -const getConfidenceURLFromPayload = (data: AlphafoldPayload) => { +const getConfidenceURLFromPayload = (data: AlphaFoldPayload) => { const cifURL = data?.[0]?.cifUrl; return cifURL?.length ? cifURL.replace('-model', '-confidence').replace('.cif', '.json') @@ -31,12 +31,17 @@ type PartialProtein = { }; const transformData = async ( - data: AlphafoldPayload, + data: AlphaFoldPayload, protein: PartialProtein ) => { const confidenceUrl = getConfidenceURLFromPayload(data); - const { uniprotSequence } = data?.[0] || {}; - if (confidenceUrl && uniprotSequence === protein.sequence.sequence) { + if (!confidenceUrl) { + return; + } + const alphaFoldSequenceMatch = data?.filter( + ({ sequence }) => protein.sequence.sequence === sequence + ); + if (alphaFoldSequenceMatch.length) { const confidenceData = await loadConfidence(confidenceUrl); return confidenceData?.confidenceCategory.join(''); } diff --git a/src/adapters/alphamissense-heatmap-adapter.ts b/src/adapters/alphamissense-heatmap-adapter.ts index 073c4a3..204184b 100644 --- a/src/adapters/alphamissense-heatmap-adapter.ts +++ b/src/adapters/alphamissense-heatmap-adapter.ts @@ -1,8 +1,9 @@ +import { AlphaFoldPayload } from '@nightingale-elements/nightingale-structure'; + import { cellSplitter, rowSplitter, } from './alphamissense-pathogenicity-adapter'; -import { AlphafoldPayload } from './types/alphafold'; const parseCSV = (rawText: string): Array> => { const data = []; @@ -43,13 +44,22 @@ type PartialProtein = { }; const transformData = async ( - data: AlphafoldPayload, + data: AlphaFoldPayload, protein: PartialProtein ) => { - const { amAnnotationsUrl, uniprotSequence } = data?.[0] || {}; - if (amAnnotationsUrl && uniprotSequence === protein.sequence.sequence) { - const heatmapData = await loadAndParseAnnotations(amAnnotationsUrl); + const alphaFoldSequenceMatch = data?.filter( + ({ sequence, amAnnotationsUrl }) => + protein.sequence.sequence === sequence && amAnnotationsUrl + ); + if (alphaFoldSequenceMatch.length === 1) { + const heatmapData = await loadAndParseAnnotations( + alphaFoldSequenceMatch[0].amAnnotationsUrl + ); return heatmapData; + } else if (alphaFoldSequenceMatch.length > 1) { + console.warn( + `Found more than one matches (${alphaFoldSequenceMatch.length}) for AlphaMissense pathogenicity against protein sequence: ${protein.sequence}` + ); } }; diff --git a/src/adapters/alphamissense-pathogenicity-adapter.ts b/src/adapters/alphamissense-pathogenicity-adapter.ts index 0071fdb..165b0b0 100644 --- a/src/adapters/alphamissense-pathogenicity-adapter.ts +++ b/src/adapters/alphamissense-pathogenicity-adapter.ts @@ -1,4 +1,4 @@ -import { AlphafoldPayload } from './types/alphafold'; +import { AlphaFoldPayload } from '@nightingale-elements/nightingale-structure'; // from color scale B:0,H:0.1132,V:0.2264,L:0.3395,A:0.4527,l:0.5895,h:0.7264,p:0.8632,P:1 const certainlyBenign = 0; @@ -95,7 +95,7 @@ const loadAndParseAnnotations = async (url: string): Promise => { const rawCSV = await payload.text(); return parseCSV(rawCSV); } catch (e) { - console.error('Could not load AlphaMissense pathogenicity', e); + console.error('Could not load AlphaMissense pathogenicity score', e); } }; @@ -106,14 +106,22 @@ type PartialProtein = { }; const transformData = async ( - data: AlphafoldPayload, + data: AlphaFoldPayload, protein: PartialProtein ) => { - const { amAnnotationsUrl, uniprotSequence } = data?.[0] || {}; - if (amAnnotationsUrl && uniprotSequence === protein.sequence.sequence) { - const variationData = await loadAndParseAnnotations(amAnnotationsUrl); - // return confidenceData?.confidenceCategory.join(''); - return variationData; + const alphaFoldSequenceMatch = data?.filter( + ({ sequence, amAnnotationsUrl }) => + protein.sequence.sequence === sequence && amAnnotationsUrl + ); + if (alphaFoldSequenceMatch.length === 1) { + const heatmapData = await loadAndParseAnnotations( + alphaFoldSequenceMatch[0].amAnnotationsUrl + ); + return heatmapData; + } else if (alphaFoldSequenceMatch.length > 1) { + console.warn( + `Found more than one matches (${alphaFoldSequenceMatch.length}) for AlphaMissense pathogenicity score against protein sequence: ${protein.sequence}` + ); } }; diff --git a/src/adapters/types/alphafold.ts b/src/adapters/types/alphafold.ts deleted file mode 100644 index f4f65c0..0000000 --- a/src/adapters/types/alphafold.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type AlphafoldPayload = Array<{ - entryId: string; - gene: string; - uniprotAccession: string; - uniprotId: string; - uniprotDescription: string; - taxId: number; - organismScientificName: string; - uniprotStart: number; - uniprotEnd: number; - uniprotSequence: string; - modelCreatedDate: string; - latestVersion: number; - allVersions: number[]; - isReviewed: boolean; - isReferenceProteome: boolean; - cifUrl?: string; - bcifUrl?: string; - amAnnotationsUrl?: string; - amAnnotationsHg19Url?: string; - amAnnotationsHg38Url?: string; - pdbUrl: string; - paeImageUrl: string; - paeDocUrl: string; -}>; diff --git a/src/config.ts b/src/config.ts index cb3c72a..36c0515 100644 --- a/src/config.ts +++ b/src/config.ts @@ -815,8 +815,10 @@ const config: ProtvistaConfig = { name: 'ALPHAMISSENSE_PATHOGENICITY', label: 'AlphaMissense', trackType: 'nightingale-colored-sequence', - scale: 'B:0,H:0.1132,V:0.2264,L:0.3395,A:0.4527,l:0.5895,h:0.7264,p:0.8632,P:1', - 'color-range': '#2166ac:0,#4290bf:0.1132,#8cbcd4:0.2264,#c3d6e0:0.3395,#e2e2e2:0.4527,#edcdba:0.5895,#e99e7c:0.7264,#d15e4b:0.8632,#b2182b:1', + scale: + 'B:0,H:0.1132,V:0.2264,L:0.3395,A:0.4527,l:0.5895,h:0.7264,p:0.8632,P:1', + 'color-range': + '#2166ac:0,#4290bf:0.1132,#8cbcd4:0.2264,#c3d6e0:0.3395,#e2e2e2:0.4527,#edcdba:0.5895,#e99e7c:0.7264,#d15e4b:0.8632,#b2182b:1', tracks: [ { name: 'alphamissense_pathogenicity', @@ -836,13 +838,13 @@ const config: ProtvistaConfig = { { name: 'alphamissense_pathogenicity_heatmap', label: 'AlphaMissense Pathogenicity', - labelUrl: 'https://alphafold.ebi.ac.uk/entry/{accession}', + labelUrl: `${alphafoldEntry}{accession}`, trackType: 'nightingale-sequence-heatmap', data: [ { adapter: 'alphamissense-heatmap-adapter', url: [ - 'https://alphafold.ebi.ac.uk/api/prediction/{accession}', + `${alphafoldApi}prediction/{accession}`, `${proteinsApiServices.proteins}{accession}`, ], }, diff --git a/src/protvista-uniprot-structure.ts b/src/protvista-uniprot-structure.ts index 5bd4c8b..7f7a2eb 100644 --- a/src/protvista-uniprot-structure.ts +++ b/src/protvista-uniprot-structure.ts @@ -2,7 +2,7 @@ import { LitElement, html, svg, TemplateResult, css, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import NightingaleStructure, { - PredictionData, + AlphaFoldPayload, } from '@nightingale-elements/nightingale-structure'; import ProtvistaDatatable from 'protvista-datatable'; import { fetchAll, loadComponent } from './utils'; @@ -164,13 +164,13 @@ const processPDBData = (data: UniProtKBData): ProcessedStructureData[] => transformedItem !== undefined ); -const processAFData = (data: PredictionData[]): ProcessedStructureData[] => +const processAFData = (data: AlphaFoldPayload): ProcessedStructureData[] => data.map((d) => ({ - id: d.entryId, + id: d.modelEntityId, source: 'AlphaFold', method: 'Predicted', - positions: `${d.uniprotStart}-${d.uniprotEnd}`, - protvistaFeatureId: d.entryId, + positions: `${d.sequenceStart}-${d.sequenceEnd}`, + protvistaFeatureId: d.modelEntityId, downloadLink: d.pdbUrl, })); @@ -308,14 +308,16 @@ class ProtvistaUniprotStructure extends LitElement { const pdbData = processPDBData(rawData[pdbUrl] || []); let afData = []; // Check if AF sequence matches UniProt sequence - if ( - rawData[pdbUrl].sequence?.value === rawData[alphaFoldUrl]?.[0]?.sequence - ) { - afData = processAFData(rawData[alphaFoldUrl] || []); - this.alphamissenseAvailable = rawData[alphaFoldUrl].some( + const alphaFoldSequenceMatch = rawData[alphaFoldUrl]?.filter( + ({ sequence }) => rawData[pdbUrl].sequence?.value === sequence + ); + if (alphaFoldSequenceMatch.length) { + afData = processAFData(alphaFoldSequenceMatch); + this.alphamissenseAvailable = alphaFoldSequenceMatch.some( (data) => data.amAnnotationsUrl ); } + const beaconsData = process3DBeaconsData(rawData[beaconsUrl] || []); // TODO: return if no data at all diff --git a/yarn.lock b/yarn.lock index 0fd5de0..222dcfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1841,10 +1841,10 @@ "@nightingale-elements/nightingale-new-core" "^5.6.0" d3 "7.9.0" -"@nightingale-elements/nightingale-structure@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@nightingale-elements/nightingale-structure/-/nightingale-structure-5.7.0.tgz#dcfb75b3d73332c01abc0e34090bec173f211bf6" - integrity sha512-sP554EZqt1gJcZTok5qVa4kdBC7pM1OSTbqnx15LYqThqHiUJ0RwiJoCVyPD6RcjlKUZEM4l60e7kISnaKGNbA== +"@nightingale-elements/nightingale-structure@5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@nightingale-elements/nightingale-structure/-/nightingale-structure-5.8.0.tgz#55e69196a05206fa6cb8507a7e97836f2c3765b9" + integrity sha512-HdInDB0hh7d7LSyAOUJwEYgoBNffR/kvOU1p8I9ezMccpQ4YYf6E/Fj3xUd9NxWfULNfGk9AF/Fsg43qC6HzHw== dependencies: "@nightingale-elements/nightingale-new-core" "^5.6.0" d3 "7.9.0"