1515
1616import json
1717import os
18+ import re
1819import sys
1920import urllib .request
21+ from html import unescape
2022
2123import yaml
2224
@@ -142,6 +144,46 @@ def get_chembl_id_from_unichem(sources):
142144 return None
143145
144146
147+ def clean_text (value ):
148+ if not isinstance (value , str ):
149+ return value
150+ return re .sub (r"<[^>]+>" , "" , unescape (value )).strip ()
151+
152+
153+ def safe_float (value ):
154+ try :
155+ return float (value )
156+ except (TypeError , ValueError ):
157+ return None
158+
159+
160+ def sanitize_sameas (sameas ):
161+ patterns = {
162+ "ChEBI" : r"^CHEBI:\d+$" ,
163+ "ChEMBL" : r"^CHEMBL\d+$" ,
164+ "lipidmaps" : r"^LM(FA|GL|GP|SP|ST|PR|SL|PK)[0-9]{4}([0-9a-zA-Z]{4,6})?$" ,
165+ "metabolights" : r"^MTBL[CS]\d+$" ,
166+ "slm" : r"^SLM:\d+$" ,
167+ "pdb.ligand" : r"^[A-Za-z0-9]+$" ,
168+ "unii" : r"^[A-Z0-9]+$" ,
169+ "cas" : r"^\d{1,7}-\d{2}-\d$" ,
170+ }
171+ sanitized = {}
172+ for key , value in sameas .items ():
173+ if key == "pubchem.compound" :
174+ if isinstance (value , int ):
175+ sanitized [key ] = value
176+ else :
177+ try :
178+ sanitized [key ] = int (value )
179+ except (TypeError , ValueError ):
180+ pass
181+ continue
182+ if isinstance (value , str ) and re .match (patterns .get (key , r".+" ), value ):
183+ sanitized [key ] = value
184+ return sanitized
185+
186+
145187def load_existing_metadata (path ):
146188 if os .path .exists (path ):
147189 with open (path , "r" , encoding = "utf-8" ) as f :
@@ -188,7 +230,7 @@ def main():
188230 chembl = get_chembl (inchikey )
189231 pubchem = get_pubchem (inchikey )
190232 sources = get_unichem (inchikey )
191- sameas = extract_sameas (sources )
233+ sameas = sanitize_sameas ( extract_sameas (sources ) )
192234
193235 cid = pubchem .get ("CID" , sameas .get ("pubchem.compound" ))
194236 synonyms = get_pubchem_synonyms (cid ) if cid else []
@@ -216,6 +258,7 @@ def main():
216258 # 3. If still no synonyms, try PubChem synonyms
217259 if not alternate_names and synonyms :
218260 alternate_names = synonyms
261+ alternate_names = [clean_text (name ) for name in alternate_names if clean_text (name )]
219262
220263 molecule_props = chembl .get ("molecule_properties" , {})
221264 molecule_structures = chembl .get ("molecule_structures" , {})
@@ -229,14 +272,23 @@ def main():
229272 else :
230273 image_url = ""
231274
275+ nmr_name = (
276+ existing .get ("NMRlipids" , {}).get ("name" )
277+ or clean_text (chembl .get ("pref_name" , "" ))
278+ or clean_text (molecule_props .get ("iupac_name" , "" ))
279+ or clean_text (pubchem .get ("IUPACName" , "" ))
280+ or nmr_id
281+ )
282+
232283 bioschema = {
233- "name" : molecule_props .get ("iupac_name" ) or pubchem .get ("IUPACName" , "" ),
234- "iupacName" : molecule_props .get ("iupac_name" ) or pubchem .get ("IUPACName" , "" ),
284+ "name" : clean_text ( molecule_props .get ("iupac_name" )) or clean_text ( pubchem .get ("IUPACName" , "" ) ),
285+ "iupacName" : clean_text ( molecule_props .get ("iupac_name" )) or clean_text ( pubchem .get ("IUPACName" , "" ) ),
235286 "molecularFormula" : molecule_props .get ("full_molformula" ) or pubchem .get ("MolecularFormula" , "" ),
236- "molecularWeight" : float (molecule_props .get ("full_mwt" ) or pubchem .get ("MolecularWeight" , 0 )),
237- "inChI" : molecule_structures .get ("standard_inchi" ) or pubchem .get ("InChI" , "" ),
238- "inChIKey" : molecule_structures .get ("standard_inchi_key" ) or pubchem .get ("InChIKey" , "" ),
239- "smiles" : molecule_structures .get ("canonical_smiles" ) or pubchem .get ("SMILES" , "" ),
287+ "molecularWeight" : safe_float (molecule_props .get ("full_mwt" ) or pubchem .get ("MolecularWeight" )),
288+ "inChI" : clean_text (molecule_structures .get ("standard_inchi" )) or clean_text (pubchem .get ("InChI" , "" )),
289+ "inChIKey" : clean_text (molecule_structures .get ("standard_inchi_key" ))
290+ or clean_text (pubchem .get ("InChIKey" , "" )),
291+ "smiles" : clean_text (molecule_structures .get ("canonical_smiles" )) or clean_text (pubchem .get ("SMILES" , "" )),
240292 "image" : image_url ,
241293 "description" : "" ,
242294 }
@@ -245,7 +297,7 @@ def main():
245297 bioschema ["alternateName" ] = alternate_names
246298
247299 new_data = {
248- "NMRlipids" : {"id" : nmr_id , "name" : "" , "charge" : "" },
300+ "NMRlipids" : {"id" : nmr_id , "name" : nmr_name },
249301 "sameAs" : sameas ,
250302 "bioschema_properties" : bioschema ,
251303 }
0 commit comments