From 84bbce73491746cd8afcec99982ad82cb8158078 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Mon, 13 Oct 2025 15:23:50 +0100 Subject: [PATCH 1/7] Filter alphafold on sequence match. --- src/protvista-uniprot-structure.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/protvista-uniprot-structure.ts b/src/protvista-uniprot-structure.ts index 5bd4c8b..90ba196 100644 --- a/src/protvista-uniprot-structure.ts +++ b/src/protvista-uniprot-structure.ts @@ -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 From 3d64da56ee46b3eb0a05064d9949c5b3ede32aa6 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Mon, 13 Oct 2025 18:51:56 +0100 Subject: [PATCH 2/7] Just use AF payload type from nightingale. --- src/adapters/types/alphafold.ts | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/adapters/types/alphafold.ts 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; -}>; From 4e6556b2b64684084d73a809f5921d75d89b15f1 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Mon, 13 Oct 2025 18:52:29 +0100 Subject: [PATCH 3/7] Import AF payload from nightingale; update for latest AF changes. --- src/adapters/alphafold-confidence-adapter.ts | 15 ++++++++---- src/adapters/alphamissense-heatmap-adapter.ts | 20 ++++++++++++---- .../alphamissense-pathogenicity-adapter.ts | 24 ++++++++++++------- src/config.ts | 10 ++++---- src/protvista-uniprot-structure.ts | 4 ++-- 5 files changed, 49 insertions(+), 24 deletions(-) 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..02ee6c8 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 ${alphaFoldSequenceMatch.length} matches 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/config.ts b/src/config.ts index cb3c72a..3af671e 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 90ba196..164bb7e 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,7 +164,7 @@ const processPDBData = (data: UniProtKBData): ProcessedStructureData[] => transformedItem !== undefined ); -const processAFData = (data: PredictionData[]): ProcessedStructureData[] => +const processAFData = (data: AlphaFoldPayload): ProcessedStructureData[] => data.map((d) => ({ id: d.entryId, source: 'AlphaFold', From 56c60de94604c932f82176fb966ae9242bd9ded2 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Mon, 13 Oct 2025 18:56:55 +0100 Subject: [PATCH 4/7] Update console message. --- src/adapters/alphamissense-heatmap-adapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapters/alphamissense-heatmap-adapter.ts b/src/adapters/alphamissense-heatmap-adapter.ts index 02ee6c8..204184b 100644 --- a/src/adapters/alphamissense-heatmap-adapter.ts +++ b/src/adapters/alphamissense-heatmap-adapter.ts @@ -58,7 +58,7 @@ const transformData = async ( return heatmapData; } else if (alphaFoldSequenceMatch.length > 1) { console.warn( - `Found ${alphaFoldSequenceMatch.length} matches for AlphaMissense pathogenicity against protein sequence: ${protein.sequence}` + `Found more than one matches (${alphaFoldSequenceMatch.length}) for AlphaMissense pathogenicity against protein sequence: ${protein.sequence}` ); } }; From edfe11e3b4bc9f721b805c1048c560c57cb598a7 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Tue, 14 Oct 2025 08:38:36 +0100 Subject: [PATCH 5/7] Remove extra . --- src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 3af671e..36c0515 100644 --- a/src/config.ts +++ b/src/config.ts @@ -844,7 +844,7 @@ const config: ProtvistaConfig = { { adapter: 'alphamissense-heatmap-adapter', url: [ - `${alphafoldApi}/prediction/{accession}`, + `${alphafoldApi}prediction/{accession}`, `${proteinsApiServices.proteins}{accession}`, ], }, From fd74a4d30c19c4bf3ca40c3bf4600af861c072f9 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Tue, 14 Oct 2025 13:08:39 +0100 Subject: [PATCH 6/7] Update @nightingale-elements/nightingale-structure --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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/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" From 074574bdf32b802e165b1626b7842b89899d84f8 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Tue, 14 Oct 2025 13:08:46 +0100 Subject: [PATCH 7/7] Fix more types. --- src/protvista-uniprot-structure.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protvista-uniprot-structure.ts b/src/protvista-uniprot-structure.ts index 164bb7e..7f7a2eb 100644 --- a/src/protvista-uniprot-structure.ts +++ b/src/protvista-uniprot-structure.ts @@ -166,11 +166,11 @@ const processPDBData = (data: UniProtKBData): 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, }));