@@ -5,7 +5,9 @@ const handler = require('../../../utils/generic-handler');
55const getDatabase = require ( '../../../database' ) ;
66// Standard HTTP response status codes
77const { NOT_FOUND , INTERNAL_SERVER_ERROR } = require ( '../../../utils/status-codes' ) ;
8+ const { PROTEIN_RESIDUE_NAME_LETTERS } = require ( '../../../utils/constants' ) ;
89const { getHost } = require ( '../../../utils/auxiliar-functions' ) ;
10+ const { buildKnowledgeResponse, formatKnowledgeDate } = require ( '../shared' ) ;
911
1012// Set the name of the current analysis
1113const ANALYSIS_NAME = 'lipid-inter' ;
@@ -66,25 +68,7 @@ router.route('/').get( handler({ async retriever(request) {
6668 poreFacingFractions = channelsData . data ?. pore_residues ?. pore_facing || [ ] ;
6769 }
6870 }
69- // Set the sites list according to MemProtMD schema
70- const sites = [
71- {
72- site_id : 1 ,
73- label : "membrane lipid acyl-tail interacting residue" ,
74- } ,
75- {
76- site_id : 2 ,
77- label : "membrane lipid head-group interacting residue" ,
78- } ,
79- {
80- site_id : 3 ,
81- label : "solvent interacting residue" ,
82- } ,
83- {
84- site_id : 4 ,
85- label : "pore-facing residue" ,
86- } ,
87- ] ;
71+
8872
8973 // Helper function to classify confidence based on interaction fraction
9074 const classifyConfidence = ( value ) => {
@@ -124,8 +108,14 @@ router.route('/').get( handler({ async retriever(request) {
124108 // To do so we must anotate data in PDB reference PDB chains and residues
125109 // Get chain data according to the FunPDBe schema
126110 const pdbChains = [ ] ;
127- // Iterate over the different PDB chains
128- for ( const [ chainLetter , uniprotId ] of Object . entries ( pdbReference . chain_uniprots ) ) {
111+ // Iterate over the different UniProt ids
112+ // Note that we may have more tha one chain in the PDB belonging to the same UniProt
113+ // Note that we may have more tha one chain in the simulation to the same UniProt
114+ // There are many possible scenarios and there is no simple rule to match chains always
115+ // For this reason, for every different UniProt id, we will use only the first chain
116+ // We will path values from the first system chain with numeration of the first PDB chain
117+ const uniprotIds = new Set ( Object . values ( pdbReference . chain_uniprots ) ) ;
118+ for ( const uniprotId of uniprotIds ) {
129119 // Find the reference index in the topology
130120 const referenceIndex = topologyData . references . indexOf ( uniprotId ) ;
131121 // It may happen that the reference is not found
@@ -134,18 +124,22 @@ router.route('/').get( handler({ async retriever(request) {
134124 // Get residue indices for residues which belong to this reference/chain
135125 const residueIndices = [ ] ;
136126 Object . entries ( topologyData . residue_reference_indices ) . forEach ( ( [ residueIndex , residueReferenceIndex ] ) => {
137- if ( residueReferenceIndex === referenceIndex ) residueIndices . push ( parseInt ( residueIndex ) ) ;
127+ if ( residueReferenceIndex === referenceIndex ) residueIndices . push ( residueIndex ) ;
138128 } ) ;
139- // Get residue data according to the FunPDBe schema
140- const pdbResidues = [ ] ;
141-
142129 // Iterate residue indices
143- residueIndices . forEach ( residueIndex => {
144- // Get the residue numeration according to the reference (uniprot and thus the PDB)
145- const residueNumber = topologyData . residue_reference_numbers [ residueIndex ] ;
146- // Get residue name
147- const residueName = topologyData . residue_names [ residueIndex ] ;
148-
130+ for ( const residueIndex of residueIndices ) {
131+ // Get the residue numeration according to the reference (uniprot)
132+ const residueUniprotNumber = topologyData . residue_reference_numbers [ residueIndex ] ;
133+ // Now use the uniprot 2 pdb map to get the equivalent PDB values
134+ const uniprotKey = `${ uniprotId } _${ residueUniprotNumber } ` ;
135+ const pdbEquivalent = pdbReference . uni2pdb [ uniprotKey ] ;
136+ if ( ! pdbEquivalent ) continue ;
137+ // If there is no PDB equivalent then this residue is not present in the PDB
138+ // We will return no data for this specific residue
139+ const [ chainLetter , residueLabel , residueType ] = pdbEquivalent ;
140+ // Get the amino acid single letter code
141+ const residueLetter = PROTEIN_RESIDUE_NAME_LETTERS [ residueType ] || 'X' ;
142+
149143 // Get max contact probability for tail and head across all lipid types
150144 const tailInteraction = tailByResidue [ residueIndex ] || 0 ;
151145 const headInteraction = headByResidue [ residueIndex ] || 0 ;
@@ -173,12 +167,16 @@ router.route('/').get( handler({ async retriever(request) {
173167 }
174168 ] ;
175169
170+ // Get the list of residues which belong to the current chain
171+ let pdbResidues = pdbChains [ chainLetter ] ;
172+ if ( pdbResidues === undefined )
173+ pdbChains [ chainLetter ] = pdbResidues = [ ] ;
176174 // Add current PDB residue to the list
177175 pdbResidues . push ( {
178- pdb_res_label : residueNumber . toString ( ) ,
179- aa_type : residueName ,
176+ pdb_res_label : residueLabel ,
177+ aa_type : residueType ,
180178 additional_residue_annotations : {
181- MDDB_lipid_data : {
179+ mddb_lipid_data : {
182180 'group=Tail' : tailInteraction ,
183181 'group=Head' : headInteraction ,
184182 'group=Solvent' : solventInteraction ,
@@ -187,15 +185,15 @@ router.route('/').get( handler({ async retriever(request) {
187185 } ,
188186 site_data : site_data
189187 } ) ;
190- } )
191- // Add the current PDB chain to the list
192- pdbChains . push ( {
193- chain_label : chainLetter ,
194- residues : pdbResidues
195- } )
188+ }
196189 }
190+ // Convert the pdbChains object to an array
191+ const finalPdbChains = Object . entries ( pdbChains ) . map ( ( [ chainLabel , residues ] ) => ( {
192+ chain_label : chainLabel ,
193+ residues : residues
194+ } ) ) ;
197195 // If no PDB chains were found then something is wrong
198- if ( pdbChains . length === 0 ) return {
196+ if ( finalPdbChains . length === 0 ) return {
199197 headerError : INTERNAL_SERVER_ERROR ,
200198 error : 'Something went wrong when searching for PDB chains'
201199 }
@@ -206,22 +204,38 @@ router.route('/').get( handler({ async retriever(request) {
206204 // Add the cliente equivalent project URL as it has been suggested
207205 // HARDCODE: El host de la query no tiene por que ser el del cliente
208206 // HARDCODE: De hecho una API podría no tener cliente asociado o tener varios
209- const url = `${ protocol } ://${ host } /#/id/${ project . accession } /` ;
207+ const url = `${ protocol } ://${ host } /#/id/${ request . params . project } /` ;
208+ // Set the date in the expected format
209+ const funschemaDate = formatKnowledgeDate ( pdbReference . date ) ;
210210 // Return the final response in the expected format
211- return {
212- data_resource : "MDDB" ,
213- resource_version : "0.0" ,
214- resource_entry_url : url ,
215- model_coordinates_url : `https://www.ebi.ac.uk/pdbe/entry/pdb/${ pdbReference . id } ` ,
216- release_date : pdbReference . date ,
217- pdb_id : pdbReference . id ,
218- chains : pdbChains ,
219- evidence_code_ontology : [ {
220- "eco_term" : "molecular dynamics evidence used in automatic assertion" ,
221- "eco_code" : "ECO_0006373"
222- } ] ,
223- sites : sites ,
224- } ;
211+ return buildKnowledgeResponse ( {
212+ resourceVersion : '0.0' ,
213+ resourceEntryUrl : url ,
214+ modelCoordinatesUrl : `https://www.ebi.ac.uk/pdbe/entry/pdb/${ pdbReference . id } ` ,
215+ releaseDate : funschemaDate ,
216+ pdbId : pdbReference . id ,
217+ sourceId : request . params . project ,
218+ chains : finalPdbChains ,
219+ // Set the sites list according to MemProtMD schema
220+ sites : [
221+ {
222+ site_id : 1 ,
223+ label : "membrane lipid acyl-tail interacting residue" ,
224+ } ,
225+ {
226+ site_id : 2 ,
227+ label : "membrane lipid head-group interacting residue" ,
228+ } ,
229+ {
230+ site_id : 3 ,
231+ label : "solvent interacting residue" ,
232+ } ,
233+ {
234+ site_id : 4 ,
235+ label : "pore-facing residue" ,
236+ } ,
237+ ] ,
238+ } ) ;
225239} } ) ) ;
226240
227241module . exports = router ;
0 commit comments