From 5c0d8d1eaf57d70287f0f39e82d9eb5886addb65 Mon Sep 17 00:00:00 2001 From: SunOfLife1 Date: Thu, 12 Feb 2026 17:49:00 -0500 Subject: [PATCH 1/3] Create api.py file for interacting with the APIs --- store/api.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 store/api.py diff --git a/store/api.py b/store/api.py new file mode 100644 index 0000000..689fb13 --- /dev/null +++ b/store/api.py @@ -0,0 +1,7 @@ +# This file will contain functions to interact with the PubChem PUG REST and PUG +# View APIs. Interacting with the two APIs should be split into different +# functions +# For example, one function should take in a string (name of compound) to get a +# CID list from PUG REST +# Another function should take in a CID and return some info about the compound +# from PUG View From bdb64ae79fefe80920e5f0d6f5eea4d2e296d366 Mon Sep 17 00:00:00 2001 From: icyang1 Date: Thu, 12 Feb 2026 17:52:06 -0500 Subject: [PATCH 2/3] Update api.py --- store/api.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/store/api.py b/store/api.py index 689fb13..99b5421 100644 --- a/store/api.py +++ b/store/api.py @@ -5,3 +5,37 @@ # CID list from PUG REST # Another function should take in a CID and return some info about the compound # from PUG View +import requests + +BASE = "https://pubchem.ncbi.nlm.nih.gov/rest/pug" +PROPS = "MolecularWeight,MolecularFormula,IsomericSMILES,IUPACName" + +name = input("Enter Element or compound: ") + + +def get_pubchem_info(name): + cid_url = f"{BASE}/compound/name/{name}/cids/JSON" + cid_response = requests.get(cid_url) + + if cid_response.status_code != 200: + + return f"Failed to find information on {name}" + + cid_data = cid_response.json() + cids = cid_data.get("IdentifierList", {}).get("CID", []) + + if not cids: + return f"Failed to find information on {name}" + + cid = cids[0] + + prop_url = f"{BASE}/compound/cid/{cid}/property/{PROPS}/JSON" + prop_response = requests.get(prop_url) + + if prop_response.status_code != 200: + return f"Failed to find information on {name}" + + data = prop_response.json() + return data["PropertyTable"]["Properties"][0] + +print(get_pubchem_info(name)) \ No newline at end of file From 6494e39f17093109b7e15a496dcf106fdfc936b0 Mon Sep 17 00:00:00 2001 From: icyang1 Date: Wed, 25 Mar 2026 17:04:21 -0400 Subject: [PATCH 3/3] Update api.py --- store/api.py | 208 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 188 insertions(+), 20 deletions(-) diff --git a/store/api.py b/store/api.py index 99b5421..54e67c4 100644 --- a/store/api.py +++ b/store/api.py @@ -7,35 +7,203 @@ # from PUG View import requests -BASE = "https://pubchem.ncbi.nlm.nih.gov/rest/pug" -PROPS = "MolecularWeight,MolecularFormula,IsomericSMILES,IUPACName" +class Rest: + base = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/" + + def get_cid_list(search_param): + src = f"compound/name/{search_param}/cids/txt" + response = requests.get(Rest.base + src) -name = input("Enter Element or compound: ") + if response.status_code != 200: + return [-1] + + lines = response.text.strip().split('\n') + + if lines[0].startswith("Status"): + status_split = lines[0].split(" ") + return [-1, int(status_split[1])] + + return [int(line) for line in lines] +class View: + base = "https://pubchem.ncbi.nlm.nih.gov/rest/pug_view/" + class Chemical: + def __init__(self): + self.cid = None + self.CASNumber = None + self.commonName = None + self.synonyms = [] + self.molecularFormulas = [] + self.molecularWeight = None + self.storageConditions = None + self.HCodes = [] + self.PCodes = None + self.hazardIconURLs = [] + + def get_chemical(cid): + src = f"data/compound/{cid}/JSON" + response = requests.get(View.base + src) + + node = response.json() + chemical_data = View.Chemical() -def get_pubchem_info(name): - cid_url = f"{BASE}/compound/name/{name}/cids/JSON" - cid_response = requests.get(cid_url) + chemical_data.cid = View.getCID(node) + chemical_data.CASNumber = View.getCASNumber(node) + chemical_data.commonName = View.getCommonName(node) + chemical_data.synonyms = View.getSynonyms(node) + chemical_data.molecularFormulas = View.getMolecularFormulas(node) + chemical_data.molecularWeight = View.getMolecularWeight(node) + chemical_data.storageConditions = View.getStorageCondition(node) + chemical_data.HCodes = View.getHCodes(node) + chemical_data.PCodes = View.getPCodes(node) + chemical_data.hazardIconURLs = View.getHazardIconURLs(node) - if cid_response.status_code != 200: + return chemical_data + + def getSection(sections, section_heading): + for section in sections: + if section.get("TOCHeading") == section_heading: + return section + return None + + def getCID(obj): + return obj["Record"]["RecordNumber"] + + def getHazardIconURLs(obj): + try: + primaryHazardsNode = View.getSection(obj["Record"]["Section"], "Primary Hazards") + + urls = [] + # Loop through all Information entries since there may be multiple + for info in primaryHazardsNode["Information"]: + for markup in info["Value"]["StringWithMarkup"]: + # Each markup entry may have a "Markup" list containing icon URLs + if "Markup" in markup: + for icon in markup["Markup"]: + if "URL" in icon: + urls.append(icon["URL"]) + + return urls + except (KeyError, TypeError, IndexError): + return [] + + def getMolecularFormulas(obj): + try: + namesNode = View.getSection(obj["Record"]["Section"],"Names and Identifiers") + allFormulasNode = View.getSection(namesNode["Section"], "Molecular Formula")["Information"] + + formulas = [] + for entry in allFormulasNode: + i = entry["Value"]["StringWithMarkup"][0]["String"] + if i not in formulas: + formulas.append(i) + return formulas + + except (KeyError, TypeError, IndexError): + return [] - return f"Failed to find information on {name}" + + def getCASNumber(obj): + try: + namesNode = View.getSection(obj["Record"]["Section"],"Names and Identifiers") + otherIdentifiersNode = View.getSection(namesNode["Section"],"Other Identifiers") + allCASNumbersNode = View.getSection(otherIdentifiersNode["Section"],"CAS")["Information"] - cid_data = cid_response.json() - cids = cid_data.get("IdentifierList", {}).get("CID", []) + cas_count = {} + for entry in allCASNumbersNode: + cas = entry["Value"]["StringWithMarkup"][0]["String"] + cas_count[cas] = cas_count.get(cas,0) + 1 - if not cids: - return f"Failed to find information on {name}" + return max(cas_count, key = cas_count.get) - cid = cids[0] + except (KeyError, TypeError, IndexError): + return None + + def getCommonName(obj): + return obj["Record"]["RecordTitle"] + + def getSectionID(obj, TOCHeading): + recordSectionID = -1 - prop_url = f"{BASE}/compound/cid/{cid}/property/{PROPS}/JSON" - prop_response = requests.get(prop_url) + for i in range(len(obj["Record"]["Section"])): + if obj["Record"]["Section"][i]["TOCHeading"] == TOCHeading: + recordSectionID = i + break + return recordSectionID + + def getSynonyms(obj): + nameSectionID = View.getSectionID(obj, "Names and Identifiers") + topFiveSynonyms = [] - if prop_response.status_code != 200: - return f"Failed to find information on {name}" + size = len(obj["Record"]["Section"][nameSectionID]["Section"][4]["Section"][1]["Information"][0]["Value"]["StringWithMarkup"]) + + allSynonyms = [] + + for i in range(size): + allSynonyms.append(obj["Record"]["Section"][nameSectionID]["Section"][4]["Section"][1]["Information"][0]["Value"]["StringWithMarkup"][i]["String"]) + + for i in range(5): + topFiveSynonyms.append(allSynonyms[i]) + + return topFiveSynonyms + + def getMolecularWeightValues(obj): + chemPropertiesSectionID = View.getSectionID(obj,"Chemical and Physical Properties") + return round(float(obj["Record"]["Section"][chemPropertiesSectionID]["Section"][0]["Section"][0]["Information"][0]["Value"]["StringWithMarkup"][0]["String"]), 2) + + def getMolecularWeightUnit(obj): + chemPropertiesSectionID = View.getSectionID(obj, "Chemical and Physical Properties") + return obj["Record"]["Section"][chemPropertiesSectionID]["Section"][0]["Section"][0]["Information"][0]["Value"]["Unit"] + + def getMolecularWeight(obj): + value = View.getMolecularWeightValues(obj) + unit = View.getMolecularWeightUnit(obj) + + combo = str(value) + " " + unit + return combo + + def getStorageCondition(obj): + try: + safetyAndHazardsNode = View.getSection(obj["Record"]["Section"],"Safety and Hazards") + handlingAndStorageNode = View.getSection(safetyAndHazardsNode["Section"],"Handling and Storage") + storageConditionsNode = View.getSection(handlingAndStorageNode["Section"],"Storage Conditions") + return storageConditionsNode["Information"][0]["Value"]["StringWithMarkup"][0]["String"] + except (KeyError, TypeError, IndexError): + + return None + + def getHCodes(obj): + try: + safetyNode = View.getSection(obj["Record"]["Section"], "Safety and Hazards") + hazardsIdNode = View.getSection(safetyNode["Section"], "Hazards Identification") + ghsNode = View.getSection(hazardsIdNode["Section"], "GHS Classification") + + # Find the first entry specifically named "GHS Hazard Statements" + # This handles chemicals where index [2] is not always the H codes + for entry in ghsNode["Information"]: + if entry.get("Name") == "GHS Hazard Statements": + hCodesArrayNode = entry["Value"]["StringWithMarkup"] + size = len(hCodesArrayNode) + hCodes = [] + for i in range(size): + hCodes.append(hCodesArrayNode[i]["String"]) + return hCodes # Return after the FIRST matching entry only + + return [] # No GHS Hazard Statements found + except (KeyError, TypeError, IndexError): + return [] + + def getPCodes(obj): + try: + safetyNode = View.getSection(obj["Record"]["Section"], "Safety and Hazards") + hazardsIdNode = View.getSection(safetyNode["Section"], "Hazards Identification") + ghsNode = View.getSection(hazardsIdNode["Section"], "GHS Classification") - data = prop_response.json() - return data["PropertyTable"]["Properties"][0] + # Find the first entry specifically named "Precautionary Statement Codes" + for entry in ghsNode["Information"]: + if entry.get("Name") == "Precautionary Statement Codes": + return entry["Value"]["StringWithMarkup"][0]["String"] -print(get_pubchem_info(name)) \ No newline at end of file + return None # No Precautionary Statement Codes found + except (KeyError, TypeError, IndexError): + return None \ No newline at end of file