rairo commited on
Commit
dff737d
Β·
verified Β·
1 Parent(s): 7cc555e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -35
app.py CHANGED
@@ -7,8 +7,12 @@ import nest_asyncio
7
  import os
8
  import subprocess
9
  import io
 
 
 
 
 
10
  import urllib.parse
11
- from langchain_google_genai import ChatGoogleGenerativeAI
12
 
13
  # Ensure Playwright installs required browsers and dependencies
14
  subprocess.run(["playwright", "install"])
@@ -58,6 +62,8 @@ def process_multiple_urls(urls):
58
  f"""
59
  **Processing Grant Opportunities** πŸš€
60
  Scanning URL {index+1} of {total_urls}: `{url}`
 
 
61
  """,
62
  unsafe_allow_html=True,
63
  )
@@ -88,31 +94,22 @@ def convert_to_excel(data):
88
 
89
 
90
  def create_knowledge_base(data):
91
- # Store JSON representation of data in session state
92
- st.session_state.knowledge_base_json = json.dumps(data, indent=2)
 
 
93
 
 
 
94
 
95
- def chat_with_knowledge_base(query):
96
- if "knowledge_base_json" not in st.session_state:
97
- return "Knowledge base not initialized. Please load grant data first."
98
-
99
- context = st.session_state.knowledge_base_json
100
- prompt = f"""
101
- You are an AI assistant that helps users analyze grant opportunities.
102
- Here is the extracted grant data in JSON format:
103
-
104
- {context}
105
-
106
- User's question: {query}
107
- Answer the question based on the provided grant data.
108
- """
109
 
110
  llm = ChatGoogleGenerativeAI(
111
  model="gemini-2.0-flash-thinking-exp", google_api_key=GOOGLE_API_KEY, temperature=0
112
  )
113
-
114
- response = llm.invoke(prompt)
115
- return response
116
 
117
 
118
  def get_shareable_link(file_data, file_name, file_type):
@@ -124,9 +121,20 @@ def main():
124
  st.set_page_config(page_title="Quantilytix Grant Finder", page_icon="πŸ’°", layout="wide")
125
  st.title("πŸ’° Quantilytix Grant Finder")
126
 
 
 
 
 
 
 
 
 
 
 
127
  st.sidebar.image("logoqb.jpeg", use_container_width=True)
128
  st.sidebar.header("Scrape & Configure")
129
 
 
130
  if "scraped_data" not in st.session_state:
131
  st.session_state.scraped_data = None
132
  if "chat_history" not in st.session_state:
@@ -134,6 +142,7 @@ def main():
134
  if "chat_interface_active" not in st.session_state:
135
  st.session_state.chat_interface_active = False
136
 
 
137
  url_input = st.sidebar.text_area(
138
  "Enter Grant URLs (one per line)",
139
  height=150,
@@ -141,50 +150,104 @@ def main():
141
  placeholder="e.g.,\nhttps://www.example-grants.org/opportunities\nhttps://another-funding-source.com/grants-list"
142
  )
143
 
 
144
  if st.sidebar.button("πŸ” Get Grant Opportunities"):
145
  if url_input:
146
- urls = [url.strip() for url in url_input.split("\n") if url.strip()] # FIXED TYPO
147
  if urls:
148
  try:
149
  with st.spinner("Scraping in progress... Please wait patiently."):
150
  result = process_multiple_urls(urls)
151
  st.session_state.scraped_data = result
152
- st.success(f"βœ… Successfully scraped {len(result['grants'])} grant opportunities!")
153
  except Exception as e:
154
- st.error(f"🚨 Scraping error: {e}")
155
  else:
156
  st.warning("⚠️ Please enter valid URLs.")
157
  else:
158
- st.warning("⚠️ Please enter at least one URL.")
159
 
 
160
  st.markdown("---")
161
 
162
- if st.session_state.scraped_data and st.session_state.scraped_data["grants"]:
163
  st.header("πŸ“Š Scraped Grant Data")
164
 
165
- with st.expander(f"πŸ“Š Preview Grant Data ({len(st.session_state.scraped_data['grants'])} grants)"):
 
166
  st.dataframe(st.session_state.scraped_data["grants"])
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  if st.button("🧠 Load as Knowledge Base & Chat"):
169
  with st.spinner("Loading data into knowledge base..."):
170
- create_knowledge_base(st.session_state.scraped_data)
171
  st.session_state.chat_interface_active = True
172
- st.session_state.chat_history = []
173
- st.success("Knowledge base loaded! Start chatting.")
174
 
175
  if st.session_state.get("chat_interface_active"):
 
176
  st.header("πŸ’¬ Chat with Grants Bot")
177
- query = st.text_input("Your question:")
 
 
178
  if query:
179
- with st.spinner("Generating response..."):
180
- response = chat_with_knowledge_base(query)
181
- st.session_state.chat_history.append({"query": query, "response": response})
 
 
 
182
 
183
  if st.session_state.chat_history:
184
  st.subheader("Chat History")
185
  for chat in st.session_state.chat_history:
186
- st.markdown(f"**You:** {chat['query']}")
187
- st.markdown(f"**Grants Bot:** {chat['response']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  if __name__ == "__main__":
190
  main()
 
7
  import os
8
  import subprocess
9
  import io
10
+ from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
11
+ from langchain.vectorstores import FAISS
12
+ from langchain.text_splitter import CharacterTextSplitter
13
+ from langchain.chains import ConversationalRetrievalChain
14
+ from langchain.memory import ConversationBufferMemory
15
  import urllib.parse
 
16
 
17
  # Ensure Playwright installs required browsers and dependencies
18
  subprocess.run(["playwright", "install"])
 
62
  f"""
63
  **Processing Grant Opportunities** πŸš€
64
  Scanning URL {index+1} of {total_urls}: `{url}`
65
+ <br>
66
+ <p style='font-size: 0.9em; color: #6699CC;'>Completed: {index}/{total_urls} | Remaining: {total_urls - index - 1}</p>
67
  """,
68
  unsafe_allow_html=True,
69
  )
 
94
 
95
 
96
  def create_knowledge_base(data):
97
+ documents = []
98
+ for grant in data["grants"]:
99
+ doc_parts = [f"{key.replace('_', ' ').title()}: {value}" for key, value in grant.items()]
100
+ documents.append("\n".join(doc_parts))
101
 
102
+ text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
103
+ texts = text_splitter.create_documents(documents)
104
 
105
+ embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001", google_api_key=GOOGLE_API_KEY)
106
+ vectorstore = FAISS.from_documents(texts, embeddings)
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  llm = ChatGoogleGenerativeAI(
109
  model="gemini-2.0-flash-thinking-exp", google_api_key=GOOGLE_API_KEY, temperature=0
110
  )
111
+ memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
112
+ return ConversationalRetrievalChain.from_llm(llm, vectorstore.as_retriever(), memory=memory)
 
113
 
114
 
115
  def get_shareable_link(file_data, file_name, file_type):
 
121
  st.set_page_config(page_title="Quantilytix Grant Finder", page_icon="πŸ’°", layout="wide")
122
  st.title("πŸ’° Quantilytix Grant Finder")
123
 
124
+ # --- Introduction and Motivation ---
125
+ st.markdown("""
126
+ <div style="text-align: justify;">
127
+ <p>
128
+ Welcome to <b>Quantilytix Grant Finder</b>, an AI-powered platform designed to streamline the grant discovery process, especially for academics and researchers in Zimbabwe.
129
+ </p>
130
+
131
+ </div>
132
+ """, unsafe_allow_html=True)
133
+
134
  st.sidebar.image("logoqb.jpeg", use_container_width=True)
135
  st.sidebar.header("Scrape & Configure")
136
 
137
+ # Initialize session state
138
  if "scraped_data" not in st.session_state:
139
  st.session_state.scraped_data = None
140
  if "chat_history" not in st.session_state:
 
142
  if "chat_interface_active" not in st.session_state:
143
  st.session_state.chat_interface_active = False
144
 
145
+ # URL Input in Sidebar
146
  url_input = st.sidebar.text_area(
147
  "Enter Grant URLs (one per line)",
148
  height=150,
 
150
  placeholder="e.g.,\nhttps://www.example-grants.org/opportunities\nhttps://another-funding-source.com/grants-list"
151
  )
152
 
153
+ # Get Grants Button with Icon
154
  if st.sidebar.button("πŸ” Get Grant Opportunities"):
155
  if url_input:
156
+ urls = [url.strip() for url in url_input.split("\n") if url.strip()]
157
  if urls:
158
  try:
159
  with st.spinner("Scraping in progress... Please wait patiently."):
160
  result = process_multiple_urls(urls)
161
  st.session_state.scraped_data = result
162
+ st.success(f"βœ… Successfully scraped {len(result['grants'])} grant opportunities from {len(urls)} URLs!")
163
  except Exception as e:
164
+ st.error(f"🚨 Scraping process encountered an error: {e}")
165
  else:
166
  st.warning("⚠️ Please enter valid URLs.")
167
  else:
168
+ st.warning("⚠️ Please enter at least one URL to begin scraping.")
169
 
170
+ # --- Main Panel for Data Display and Chat ---
171
  st.markdown("---")
172
 
173
+ if st.session_state.scraped_data and st.session_state.scraped_data['grants']:
174
  st.header("πŸ“Š Scraped Grant Data")
175
 
176
+ # Data Preview and Download Options in Main Panel
177
+ with st.expander(f"πŸ“Š Preview Grant Data {len(st.session_state.scraped_data['grants'])} grants"):
178
  st.dataframe(st.session_state.scraped_data["grants"])
179
 
180
+ col1, col2, col3 = st.columns([1, 1, 2]) # Adjust column widths for better layout
181
+
182
+ with col1:
183
+ selected_format = st.selectbox("Download As:", ("CSV", "Excel"), key="download_format_selector")
184
+
185
+ with col2:
186
+ if selected_format == "CSV":
187
+ file_data = convert_to_csv(st.session_state.scraped_data)
188
+ file_name = "grants_data.csv"
189
+ file_type = "text/csv"
190
+ else:
191
+ file_data = convert_to_excel(st.session_state.scraped_data)
192
+ file_name = "grants_data.xlsx"
193
+ file_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
194
+
195
+ download_link_html = f"<a href='data:{file_type};base64,{base64.b64encode(file_data).decode()}' download='{file_name}'><button style='background-color:#4CAF50;color:white;padding:10px 15px;border:none;border-radius:4px;'>⬇️ Download {selected_format}</button></a>"
196
+ st.markdown(download_link_html, unsafe_allow_html=True)
197
+
198
+ with col3:
199
+ shareable_link = get_shareable_link(file_data, file_name, file_type)
200
+ whatsapp_url = f"https://api.whatsapp.com/send?text={urllib.parse.quote(f'Check out these grant opportunities: {shareable_link}')}"
201
+ email_subject = urllib.parse.quote("Grant Opportunities File")
202
+ email_body = urllib.parse.quote(f"Download the grant opportunities file here: {shareable_link}")
203
+ email_url = f"mailto:?subject={email_subject}&body={email_body}"
204
+
205
+ st.markdown("<div style='margin-top:10px;'>Share via:</div>", unsafe_allow_html=True) # Add some margin for better spacing
206
+ st.markdown(f"πŸ“± [WhatsApp]({whatsapp_url}) | πŸ“§ [Email]({email_url})", unsafe_allow_html=True)
207
+
208
+
209
+ # Knowledge Base and Chat Interface
210
  if st.button("🧠 Load as Knowledge Base & Chat"):
211
  with st.spinner("Loading data into knowledge base..."):
212
+ st.session_state.qa_chain = create_knowledge_base(st.session_state.scraped_data)
213
  st.session_state.chat_interface_active = True
214
+ st.session_state.chat_history = [] # Clear chat history on reload
215
+ st.success("Knowledge base loaded! You can now chat with the Grants Bot.")
216
 
217
  if st.session_state.get("chat_interface_active"):
218
+ st.markdown("---")
219
  st.header("πŸ’¬ Chat with Grants Bot")
220
+ st.markdown("Ask questions about the scraped grants to get quick insights!")
221
+
222
+ query = st.text_input("Your question:", key="chat_input")
223
  if query:
224
+ if st.session_state.qa_chain:
225
+ with st.spinner("Generating response..."):
226
+ response = st.session_state.qa_chain({"question": query})
227
+ st.session_state.chat_history.append({"query": query, "response": response["answer"]})
228
+ else:
229
+ st.error("Knowledge base not initialized. Please load data as knowledge base.")
230
 
231
  if st.session_state.chat_history:
232
  st.subheader("Chat History")
233
  for chat in st.session_state.chat_history:
234
+ st.markdown(f"<div style='padding: 10px; border-radius: 5px; margin-bottom: 5px; background-color: #f0f2f6;'><strong>You:</strong> {chat['query']}</div>", unsafe_allow_html=True)
235
+ st.markdown(f"<div style='padding: 10px; border-radius: 5px; margin-bottom: 10px; background-color: #e0e2e6;'><strong>Grants Bot:</strong> {chat['response']}</div>", unsafe_allow_html=True)
236
+
237
+
238
+ else:
239
+ st.info("⬅️ Enter URLs in the sidebar and click 'Get Grant Opportunities' to start scraping.")
240
+
241
+ st.sidebar.markdown("---")
242
+ st.sidebar.markdown(
243
+ """
244
+ <div style='text-align: center; font-size: 0.8em; color: grey;'>
245
+ Powered by <a href="https://quantilytix.com" style='color: grey;'>Quantilytix</a> | &copy; 2025
246
+ </div>
247
+ """,
248
+ unsafe_allow_html=True,
249
+ )
250
+
251
 
252
  if __name__ == "__main__":
253
  main()