import streamlit as st import requests import json import web3 abi = [ { "anonymous": False, "inputs": [ { "indexed": False, "internalType": "string", "name": "ipfsHash", "type": "string" } ], "name": "Store", "type": "event" }, { "inputs": [], "name": "getHashes", "outputs": [ { "internalType": "string[]", "name": "", "type": "string[]" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "string", "name": "ipfsHash", "type": "string" } ], "name": "storeHash", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] # function that uploads the results to ipfs def upload_json_to_ipfs(data): try: url = "https://api.pinata.cloud/pinning/pinJSONToIPFS" print("starting upload to ipfs") # check for JWT secret if "PinataJWT" not in st.secrets: st.write("No JWT secret found, please add your JWT in a secret titled \"PinataJWT\"") return jwt_token = st.secrets["PinataJWT"].strip() headers = { "Authorization": f"Bearer {jwt_token}", "Content-Type": "application/json" } # Convert Python dictionary to JSON string response = requests.post(url, headers=headers, json=data) if response.status_code == 200: # Print the IPFS hash from the successful response ipfs_hash = response.json().get("IpfsHash") return ipfs_hash else: st.write(f"Failed to upload JSON. Status code: {response.status_code}") st.write(response.text) return None except Exception as e: st.write(f"Error uploading to Pinata: {e}") # function that uploads to blockchain def upload_to_blockchain(ipfs_hash): print("starting blockchain upload") w3 = web3.Web3(web3.HTTPProvider(st.secrets["infura"])) if not w3.is_connected(): raise RuntimeError("Failed to connect to Ethereum network") try: contract_address = st.secrets["ContractAddress"] checksum_address = w3.to_checksum_address(contract_address) except Exception as e: raise ValueError(f"Invalid contract address: {contract_address} Error: {e}") try: assert isinstance(abi, list), "ABI must be a list" except Exception as e: raise ValueError(f"ABI format error: {e}") contract = w3.eth.contract(address=checksum_address, abi=abi) # Build transaction call_function = contract.functions.storeHash(ipfs_hash).build_transaction({ "chainId": 11155111, "from": st.secrets["EthWallet"], "nonce": w3.eth.get_transaction_count(st.secrets["EthWallet"]), "gas": 300000, "gasPrice": w3.to_wei("10", "gwei") }) # Sign transaction signed_tx = w3.eth.account.sign_transaction(call_function, private_key=st.secrets["pk"]) # Send transaction tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) # Wait for transaction receipt tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print("Transaction successful!") print("ETH Tx Hash:", tx_receipt.transactionHash.hex()) st.success('Data successfully stored. Thank you for taking the survey!', icon="✅") st.info(f'The Ethereum hash is: {tx_receipt.logs[0].transactionHash.hex()}', icon="ℹ️") return tx_receipt.transactionHash.hex() def get_ipfs_hashes(): w3 = web3.Web3(web3.HTTPProvider(st.secrets["infura"])) # Create an instance of the contract contract = w3.eth.contract(address=st.secrets["ContractAddress"], abi=abi) # Call the getHashes function try: ipfs_hashes = contract.functions.getHashes().call() return ipfs_hashes except Exception as e: st.write(f"Error retrieving hashes: {e}") return [] def retreive_ipfs_hash_data(hashes): results = [] for ipfs_hash in hashes: url = f"https://gateway.pinata.cloud/ipfs/{ipfs_hash}" try: response = requests.get(url) if response.status_code == 200: data = response.json() results.append({"hash": ipfs_hash, "data": data}) else: results.append({"hash": ipfs_hash, "error": f"Failed to retrieve data (status {response.status_code})"}) except Exception as e: results.append({"hash": ipfs_hash, "error": str(e)}) return results # function that handles survey submission # sets up ipfs and blockchain def submission(survey_data): ipfs_hash = upload_json_to_ipfs(survey_data) if ipfs_hash: upload_to_blockchain(ipfs_hash) st.info(f'The IPFS hash is: {ipfs_hash}', icon="ℹ️") total_number_pages = 2 placeholder_buttons = None Q1_radio_options = ["Yippee","No"] Q2_radio_options = ["1","gsdjjij"] Q3_radio_options = ["N/A", "Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"] Q4_radio_options = ["N/A", "Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"] Q5_radio_options = ["N/A", "Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"] Q6_radio_options = ["N/A", "Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"] # Function that records radio element changes def radio_change(element, state, key): st.session_state[state] = element.index(st.session_state[key]) # Setting previously selected option def multi_change(element, state, key): st.session_state[state] = [] for selected_option in st.session_state[key]: st.session_state[state].append(selected_option) # Function that disables the last button while data is uploaded to IPFS def button_disable(): st.session_state['disabled'] = True def answer_change(state, key): st.session_state[state] = st.session_state[key] st.set_page_config(page_title='IPFS-Based Survey',) st.title('') st.markdown("", unsafe_allow_html=True) st.markdown("", unsafe_allow_html=True) if "current_page" not in st.session_state: st.session_state["current_page"] = 1 st.session_state["Q1"] = None st.session_state["Q2"] = None st.session_state["Q3"] = None st.session_state["Q4"] = None st.session_state["Q5"] = None st.session_state["Q6"] = None st.session_state["disabled"] = False # Page 1; Video if st.session_state["current_page"] == 1: st.markdown("""

""", unsafe_allow_html=True) st.radio(label = "Hello", options = Q1_radio_options, index = None if st.session_state["Q1"] == None else st.session_state["Q1"], key = 'Q1_radio', on_change = radio_change, args = (Q1_radio_options, "Q1", "Q1_radio",)) st.markdown("""

""", unsafe_allow_html=True) st.multiselect(label = "WHATS", default = None if st.session_state["Q2"] == None else st.session_state["Q2"], options = Q2_radio_options, key = 'Q2_multi', on_change = multi_change, args = (Q2_radio_options, "Q2", "Q2_multi",)) st.markdown("""

""", unsafe_allow_html=True) st.radio(label = "Miami?", # Likert options = Q3_radio_options, index = None if st.session_state["Q3"] == None else st.session_state["Q3"], key = 'Q3_radio', on_change = radio_change, args = (Q3_radio_options, "Q3", "Q3_radio",)) st.markdown("""

""", unsafe_allow_html=True) st.radio(label = "People", # Likert options = Q4_radio_options, index = None if st.session_state["Q4"] == None else st.session_state["Q4"], key = 'Q4_radio', on_change = radio_change, args = (Q4_radio_options, "Q4", "Q4_radio",)) st.markdown("""

""", unsafe_allow_html=True) st.radio(label = "Life", # Likert options = Q5_radio_options, index = None if st.session_state["Q5"] == None else st.session_state["Q5"], key = 'Q5_radio', on_change = radio_change, args = (Q5_radio_options, "Q5", "Q5_radio",)) st.markdown("""

""", unsafe_allow_html=True) st.radio(label = "Wods", # Likert options = Q6_radio_options, index = None if st.session_state["Q6"] == None else st.session_state["Q6"], key = 'Q6_radio', on_change = radio_change, args = (Q6_radio_options, "Q6", "Q6_radio",)) st.markdown("""

""", unsafe_allow_html=True) placeholder = st.empty() if st.button('Next'): all_answered = True if st.session_state["Q1"] == None or st.session_state["Q1"] == []: all_answered = False if st.session_state["Q2"] == None or st.session_state["Q2"] == []: all_answered = False if st.session_state["Q3"] == None or st.session_state["Q3"] == []: all_answered = False if st.session_state["Q4"] == None or st.session_state["Q4"] == []: all_answered = False if st.session_state["Q5"] == None or st.session_state["Q5"] == []: all_answered = False if st.session_state["Q6"] == None or st.session_state["Q6"] == []: all_answered = False if all_answered: st.session_state["current_page"] += 1 st.rerun() else: with placeholder.container(): st.warning("Please answer all the questions on this page.", icon="⚠️") st.progress(st.session_state["current_page"]/total_number_pages, text="Progress") elif st.session_state["current_page"] == 2: # Last Page st.markdown('

Thank you for participating!
Click on the button below to submit your answers.

', unsafe_allow_html=True) st.button('Submit Responses', disabled = st.session_state["disabled"], on_click = button_disable) if st.session_state["disabled"]: with st.spinner(r"$\textsf{\normalsize Storing data on IPFS and Ethereum. This operation might take a few minutes. Please wait to receive your confirmation code!}$"): try: response = { "Q1": Q1_radio_options[st.session_state["Q1"]], "Q2": st.session_state["Q2"], "Q3": Q3_radio_options[st.session_state["Q3"]], "Q4": Q4_radio_options[st.session_state["Q4"]], "Q5": Q5_radio_options[st.session_state["Q5"]], "Q6": Q6_radio_options[st.session_state["Q6"]], } submission(response) except Exception as e: print(e) st.error(f'An error ocurred. Here is the error message: {e}', icon="🚨") print("Success!") if st.button('Back'): st.session_state["current_page"] -= 1 st.rerun() st.progress(st.session_state["current_page"]/total_number_pages, text="Progress")