def __init__(self, record_to_request: str, input_type='iupac_name'): if search_validate(input_type): #in pubchem_search_types: if TESTING == True: greenprint("searching for a Description : " + record_to_request) if input_type == "iupac_name": self.thing_type = "name" else: self.thing_type = input_type self.record_to_request = record_to_request self.request_url = requote_uri("{}/compound/{}/{}/description/XML".format(\ API_BASE_URL,self.thing_type,self.record_to_request)) blueprint("[+] Requesting: " + makered(self.request_url) + "\n") self.request_return = requests.get(self.request_url) self.soupyresults = BeautifulSoup(self.request_return.text, features='lxml').contents[1] self.parsed_result = self.soupyresults.find_all( lambda tag: tag.name == 'description') if self.parsed_result != []: self.parsed_result = str(self.parsed_result[0].contents[0]) greenprint("[+] Description Found!") print(self.parsed_result) elif self.parsed_result == [] or NoneType: blueprint("[-] No Description Available in XML REST response") self.parsed_result = "No Description Available in XML REST response"
def decode_and_save(self, base64_image, name, image_format): ''' Decodes a base64 string to an image and saves that This helps display images in discord because they suck and dont allow base64 as a uri anymore ''' greenprint("[+] Decoding and Saving image as {}".format(name)) decoded_image = self.decode_image_from_base64(base64_image) decoded_image.save(name, format=image_format)
def encode_image_to_base64(self, image, image_format="png"): ''' stack overflow post https://stackoverflow.com/questions/52411503/convert-image-to-base64-using-python-pil ''' greenprint("[+] Encoding Image as Base64") buff = BytesIO() image.save(buff, format=image_format) img_str = base64.b64encode(buff.getvalue()) return img_str
def __init__(self, record_to_request: str, image_as_base64=True, input_type="name", temp_file="image"): ############################# if search_validate(input_type): #in pubchem_search_types: greenprint("searching for an image : " + record_to_request) # fixes local code/context to work with url/remote context if input_type == "iupac_name": self.input_type = "name" else: self.input_type = input_type self.request_url = requote_uri("{}/compound/{}/{}/PNG".format(\ API_BASE_URL,self.input_type,record_to_request)) blueprint("[+] Requesting: " + makered(self.request_url)) self.rest_request = requests.get(self.request_url) if self.was_there_was_an_error() == False: # request good # Store image # we want an image file if image_as_base64 == False: try: self.filename = temp_file + ".png" greenprint("[+] Saving image as {}".format( self.filename)) self.image_storage = Image.open( BytesIO(self.rest_request.content)) self.image_storage.save(self.filename, format="png") self.image_storage.close() except Exception as derp: redprint( "[-] Exception when opening or writing image file") print(derp) # we want a base64 string elif image_as_base64 == True: self.image_storage = self.encode_image_to_base64( self.image_storage) else: redprint("[-] Error with Class Variable self.base64_save") else: redprint("[-] Input type was wrong for Image Search") return None
def internal_local_database_lookup(entity: str, id_of_record: str): """ feed it a formula or CID followed buy "formula" or "cid" or "iupac_name searches by record and entry Returns False and raises and exception/prints exception on error Returns an SQLAlchemy database object if record exists Don't forget this is for compounds only! """ try: greenprint("[+] performing internal lookup") if search_validate(id_of_record): # in pubchem_search_types: kwargs = {id_of_record: entity} lookup_result = Compound.query.filter_by(**kwargs).first() #lookup_result = database.Compound.query.filter_by(id_of_record = entity).first() return lookup_result except Exception as derp: print(derp) redprint("[-] Not in local database") # None if empty return None
def validate_user_input(self, user_input: str, type_of_input: str): """ User Input is expected to be the proper identifier. type of input is one of the following: cid , iupac_name , cas Ater validation, the user input is used in : Pubchem_lookup.do_lookup() Pubchem_lookup.pubchem_lookup_by_name_or_CID() """ import re cas_regex = re.compile('[1-9]{1}[0-9]{1,5}-\d{2}-\d') if search_validate(type_of_input): #in pubchem_search_types: greenprint("user supplied a : " + type_of_input) try: if type_of_input == "cas": greenprint( "[+} trying to match regular expression for CAS") if re.match(cas_regex, user_input): greenprint("[+] Good CAS Number") self.do_lookup(user_input, type_of_input) else: redprint("[-] Bad CAS Number") self.user_input_was_wrong("bad_CAS", user_input) elif type_of_input == "cid" or "iupac_name": self.do_lookup(user_input, type_of_input) else: redprint( "[-] Something really wierd happened inside the validation flow" ) except Exception as derp: redprint("[-] reached the exception ") print(derp) else: self.user_input_was_wrong("input_type", type_of_input)
def __repr__(self): list_to_string = lambda list_to_convert: ''.join(list_to_convert) formula_list = str.split(self.compounds, sep=",") greenprint(formula_list) formula = "" # what the hell was I doing here? # seriously, I forgot def format_asdf(): for each in formula_list: #catches the amount if list_to_string(each).isnumeric(): amount = list_to_string(str(each)) #catches the element/compound else: compound = str(each) formula + '{} : {} {}'.format(compound, amount, "\n\t") return 'Composition: {} \n\ Units: {} \n\ Formula: {} \n\ Notes: {}'.format(self.name, self.units, formula, self.notes)
def __init__(self, record_to_request: str, image_as_base64: bool, input_type="name", temp_file="image"): ############################# # greenprint("[+] Running as Discord Attachment") # greenprint("[+] Not running as Discord Attachment") #print(str(os.environ['DISCORDAPP'])) if image_as_base64 == False: self.filename = temp_file + ".png" if search_validate(input_type): #in pubchem_search_types: greenprint("searching for an image : " + record_to_request) # fixes local code/context to work with url/remote context if input_type == "iupac_name": self.input_type = "name" else: self.input_type = input_type self.request_url = requote_uri("{}/compound/{}/{}/PNG".format(\ API_BASE_URL,self.input_type,record_to_request)) blueprint("[+] Requesting: " + makered(self.request_url)) self.rest_request = requests.get(self.request_url) # redprint("[-] Request failure at local level") # True means no error if self.was_there_was_an_error() == True: # request good # Store image self.image_storage = Image.open( BytesIO(self.rest_request.content)) if image_as_base64 == False: try: greenprint("[+] Saving image as {}".format( self.filename)) self.image_storage.save(self.filename, format="png") except Exception as blorp: redprint( "[-] Exception when opening or writing image file") print(blorp) elif image_as_base64 == True: print(self.rest_request.raw) greenprint("[+] Encoding Image as Base64") self.image_storage = base64.b64encode(self.image_storage) else: redprint("[-] Error with Class Variable self.base64_save") else: redprint("[-] Input type was wrong for Image Search") return None
############################################################################### def composition_to_database(comp_name: str, units_used :str, \ formula_list : list , info : str): """ The composition is a relation between multiple Compounds Each Composition entry will have required a pubchem_lookup on each Compound in the Formula field. the formula_list is a CSV STRING WHERE: ...str_compound,int_amount,.. REPEATING (floats allowed) EXAMPLE : Al,27.7,NH4ClO4,72.3 BIG TODO: be able to input list of cas/cid/whatever for formula_list """ # query local database for records before performing pubchem # lookups # expected to return FALSE if no record found # if something is there, it will evaluate to true # for each in formula_list: # input = Pubchem_lookup.formula_input_validation(each) # extend this but dont forget to add more fields in the database model! Database_functions.add_to_db(Composition(\ name = comp_name, \ units = units_used, \ compounds = formula_list, \ notes = info )) greenprint("[+] Loaded database")
if DISPLAY_FROM_BASE64 == True: from discord import Attachment #image_string = 'data:image/png;base64,{}'.format(str(new_lookup.lookup_object.image)) #image_string = 'data:image/png;base64,'+ str(new_lookup.lookup_object.image) #pubchem_embed.set_image(url='data:image/png;base64,{}'.format(str(new_lookup.lookup_object.image))) asdf = Attachment(data='image/png;base64,{}'.format(str(new_lookup.lookup_object.image))) #await ctx.send(content=new_lookup.image, embed=pubchem_embed) await ctx.send(content=asdf, embed=pubchem_embed) @lookup_bot.command() async def balance_equation(ctx, arg1): EquationBalancer.validate_formula_input(arg1) await ctx.send(lookup_output_container) greenprint("[+] Loaded Discord commands") ################################################################################ # AND NOW WE RUN THE BOT!!! YAY!!! I HAVE MORE DEBUGGING TO DO!!######## from variables_for_reality import TESTING from variables_for_reality import SAVE_BASE64 if TESTING == True: lookup_bot.run(discord_key.discord_bot_token, bot=True) else: try: if __name__ == '__main__': SAVE_BASE64 = True DISPLAY_FROM_BASE64 = False lookup_bot.run(discord_key.discord_bot_token, bot=True) else:
def pubchem_lookup_by_name_or_CID(self, compound_id, type_of_data: str): ''' Provide a search term and record type requests can be CAS,CID,IUPAC NAME/SYNONYM outputs in the following order: CID, CAS, SMILES, Formula, Name Stores lookup in database if lookup is valid ''' return_relationships = [] # you get multiple records returned from a pubchem search VERY often # so you have to choose the best one to store, This needs to be # presented as an option to the user,and not programmatically # return_index is the result to return, 0 is the first one return_index = 0 data = ["iupac_name", "cid", "cas"] if type_of_data in data: # different methods are used depending on the type of input # one way if type_of_data == ("iupac_name" or "cas"): try: greenprint("[+] Performing Pubchem Query") lookup_results = pubchem.get_compounds(compound_id, 'name') except Exception: # pubchem.PubChemPyError: redprint( "[-] Error in pubchem_lookup_by_name_or_CID : NAME exception" ) self.user_input_was_wrong("pubchem_lookup_by_name_or_CID") # CID requires another way elif type_of_data == "cid": try: greenprint("[+] Performing Pubchem Query") lookup_results = pubchem.Compound.from_cid(compound_id) except Exception: # pubchem.PubChemPyError: redprint("lookup by NAME/CAS exception - name") self.user_input_was_wrong("pubchem_lookup_by_name_or_CID") # once we have the lookup results, do something if isinstance(lookup_results, list): # and len(lookup_results) > 1 : greenprint("[+] Multiple results returned ") for each in lookup_results: query_appendix = [{'cid' : each.cid ,\ #'cas' : each.cas ,\ 'smiles' : each.isomeric_smiles ,\ 'formula' : each.molecular_formula ,\ 'molweight' : each.molecular_weight ,\ 'charge' : each.charge ,\ 'bond_stereo_count' : each.bond_stereo_count ,\ 'bonds' : each.bonds ,\ 'rotatable_bond_count' : each.rotatable_bond_count ,\ 'multipoles_3d' : each.multipoles_3d ,\ 'mmff94_energy_3d' : each.mmff94_energy_3d ,\ 'mmff94_partial_charges_3d': each.mmff94_partial_charges_3d ,\ 'atom_stereo_count' : each.atom_stereo_count ,\ 'h_bond_acceptor_count' : each.h_bond_acceptor_count ,\ 'feature_selfoverlap_3d' : each.feature_selfoverlap_3d ,\ 'cactvs_fingerprint' : each.cactvs_fingerprint ,\ 'iupac_name' : each.iupac_name ,\ 'description' : self.lookup_description ,\ 'image' : self.image }] return_relationships.append(query_appendix) # Right here we need to find a way to store multiple records # and determine the best record to store as the main entry #compound_to_database() TAKES A LIST!!! First element of first element #[ [this thing here] , [not this one] ] #print(return_relationships[return_index]) Database_functions.compound_to_database( return_relationships[return_index]) # if there was only one result or the user supplied a CID for a single chemical elif isinstance(lookup_results, pubchem.Compound): #\ #or (len(lookup_results) == 1 and isinstance(lookup_results, list)) : greenprint("[+] One Result Returned!") query_appendix = [{'cid' : lookup_results.cid ,\ #'cas' : lookup_results.cas ,\ 'smiles' : lookup_results.isomeric_smiles ,\ 'formula' : lookup_results.molecular_formula ,\ 'molweight' : lookup_results.molecular_weight ,\ 'charge' : lookup_results.charge ,\ 'bond_stereo_count' : each.bond_stereo_count ,\ 'bonds' : each.bonds ,\ 'rotatable_bond_count' : each.rotatable_bond_count ,\ 'multipoles_3d' : each.multipoles_3d ,\ 'mmff94_energy_3d' : each.mmff94_energy_3d ,\ 'mmff94_partial_charges_3d': each.mmff94_partial_charges_3d ,\ 'atom_stereo_count' : each.atom_stereo_count ,\ 'h_bond_acceptor_count' : each.h_bond_acceptor_count ,\ 'feature_selfoverlap_3d' : each.feature_selfoverlap_3d ,\ 'cactvs_fingerprint' : each.cactvs_fingerprint ,\ 'iupac_name' : each.iupac_name ,\ 'description' : self.lookup_description ,\ 'iupac_name' : lookup_results.iupac_name ,\ # Local stuff 'description' : self.lookup_description ,\ 'image' : self.image }] return_relationships.append(query_appendix) #print(query_appendix) Database_functions.compound_to_database( return_relationships[return_index]) else: redprint("PUBCHEM LOOKUP BY CID : ELSE AT THE END") #after storing the lookup to the local database, retrive the local entry #This returns an SQLALchemy object return_query = return_relationships[return_index] query_cid = return_query[0].get('cid') local_query = Compound.query.filter_by(cid=query_cid).first() return local_query
def do_lookup(self, user_input, type_of_input): ''' after validation, the user input is used in Pubchem_lookup.pubchem_lookup_by_name_or_CID() pubchemREST_Description_Request(user_input, type_of_input) Image_lookup() ''' try: internal_lookup = Database_functions.internal_local_database_lookup( user_input, type_of_input) # if internal lookup is false, we do a remote lookup and then store the result if internal_lookup == None or False: redprint("[-] Internal Lookup returned false") self.internal_lookup_bool = False # we grab things in the proper order # description first try: description_lookup = pubchemREST_Description_Request( user_input, type_of_input) except Exception as derp: redprint("[-] Description Lookup Failed") print(derp) self.lookup_description = "Description Lookup Failed" #then image try: #if (SAVE_BASE64 == True) : image_lookup = Image_lookup(user_input ,\ image_as_base64 = True ,\ input_type = type_of_input ,\ temp_file = user_input ) except Exception as derp: redprint("[-] Image Lookup Failed") print(derp) image_lookup = None # no image if image_lookup == None: self.image = "No Image Available" # image as base64 elif (image_lookup != None) and (DISPLAY_FROM_BASE64 == True): self.image = str(image_lookup.image_storage) # image as file elif (image_lookup != None) and (DISPLAY_FROM_BASE64 == False): self.image = image_lookup.image_storage self.image_filename = image_lookup.filename else: redprint("[-] Something wierd happened in do_lookup") # Now pubchem self.lookup_description = description_lookup.parsed_result self.lookup_object = self.pubchem_lookup_by_name_or_CID( user_input, type_of_input) # we return the internal lookup if the entry is already in the DB # for some reason, asking if it's true doesn't work here so we use a NOT instead of an Equals. elif internal_lookup != None or False: greenprint("[+] Internal Lookup returned TRUE") self.internal_lookup_bool = True self.lookup_description = internal_lookup.description self.lookup_object = internal_lookup self.image = internal_lookup.image #redprint("==BEGINNING==return query for DB lookup===========") #greenprint(str(internal_lookup)) #redprint("=====END=====return query for DB lookup===========") # its in dire need of proper exception handling except Exception as derp: redprint( '[-] Something happened in the try/except block for the function do_lookup' ) print(derp)
def save_image(self, PIL_image, name, image_format): ''' Saves image ''' greenprint("[+] Saving image as {}".format(name)) PIL_image.save(self.filename, format=image_format)
#after storing the lookup to the local database, retrive the local entry #This returns an SQLALchemy object return_query = return_relationships[return_index] query_cid = return_query[0].get('cid') local_query = Compound.query.filter_by(cid=query_cid).first() return local_query # OR return the remote lookup entry, either way, the information was stored # and you get a common "api" to draw data from. ############################################################################### # TODO: Testing procedure requires bad data craft a list of shitty things a # user can attempt. Be malicious and stupid with it # break shit greenprint("[+] Loaded Pubchem Lookup") try: if __name__ == '__main__': if TESTING == True: import time #craft a list of queries to test with test_query_list = [["420","cid"],\ ["methanol","iupac_name"],\ ["phenol","iupac_name"],\ ["methylene chloride","iupac_name"] ,\ ["6623","cid"],\ ["5462309","cid"],\ ["24823","cid"],\ ["water","iupac_name"]] ###################################################################