Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import os | |
| import time | |
| import pandas as pd | |
| import numpy as np | |
| from typing import List | |
| from supabase import create_client | |
| from dotenv import load_dotenv | |
| from openai import OpenAI | |
| load_dotenv() | |
| client = OpenAI(api_key=os.environ.get("API_TOKEN", "")) | |
| supabase = create_client(os.getenv("SUPABASE_URL", ""), os.getenv("SUPABASE_KEY", "")) | |
| def get_embedding(text: str) -> List[float]: | |
| r = client.embeddings.create(model="text-embedding-3-small", input=text) | |
| return [float(x) for x in r.data[0].embedding] | |
| def cosine_similarity(a: List[float], b: List[float]) -> float: | |
| return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) | |
| def sign_up_school( | |
| username, password, name, address_line1, city, state, zip_code, | |
| school_type, educational_level, enrollment, title_i_status, | |
| free_reduced_lunch_percentage, contact_email, additional_info | |
| ): | |
| try: | |
| auth_response = supabase.auth.sign_up({"email": username, "password": password}) | |
| user_id = auth_response.user.id | |
| profile_text = f""" | |
| School: {name} | |
| Type: {school_type} | |
| Level: {educational_level} | |
| Title I: {title_i_status} | |
| Free/Reduced Lunch: {free_reduced_lunch_percentage}% | |
| Additional Info: {additional_info} | |
| """ | |
| profile_embedding = get_embedding(profile_text) | |
| if len(profile_embedding) != 1536: | |
| raise ValueError(f"Expected embedding dimension 1536, got {len(profile_embedding)}") | |
| school_data = { | |
| "user_id": user_id, | |
| "name": name, | |
| "address_line1": address_line1, | |
| "city": city, | |
| "state": state, | |
| "zip_code": zip_code, | |
| "school_type": school_type, | |
| "educational_level": educational_level, | |
| "enrollment": enrollment, | |
| "title_i_status": title_i_status, | |
| "free_reduced_lunch_percentage": free_reduced_lunch_percentage, | |
| "contact_email": contact_email, | |
| "additional_info": additional_info, | |
| "embedding": profile_embedding | |
| } | |
| supabase.table('schools').insert(school_data).execute() | |
| return True, "School profile created successfully!", user_id | |
| except Exception as e: | |
| return False, f"Error: {str(e)}", None | |
| def login(username, password): | |
| try: | |
| r = supabase.auth.sign_in_with_password({"email": username, "password": password}) | |
| user_id = r.user.id | |
| school_response = supabase.table('schools').select("*").eq('user_id', user_id).execute() | |
| school_id = None | |
| if school_response.data and len(school_response.data) > 0: | |
| school_id = school_response.data[0]['user_id'] | |
| return True, user_id, school_id | |
| except Exception as e: | |
| return False, f"Error: {str(e)}", None | |
| def get_school_name(school_id): | |
| if not school_id: | |
| return "No school selected" | |
| try: | |
| r = supabase.table('schools').select("name").eq('user_id', school_id).execute() | |
| if r.data and len(r.data) > 0: | |
| return r.data[0]['name'] | |
| return "Unknown school" | |
| except: | |
| return "Error loading school" | |
| def search_grants(query, school_id=None): | |
| try: | |
| headers = ["Title", "Provider", "Due_date", "Description", "Funding_amount", "Requirements", "Eligibility", "Categories", "Score"] | |
| if not query or query.strip() == "": | |
| return pd.DataFrame(columns=headers) | |
| query_embedding = get_embedding(query) | |
| school_data = None | |
| if school_id and str(school_id).strip().lower() != 'none': | |
| try: | |
| sr = supabase.table('schools').select("*").eq('user_id', school_id).execute() | |
| if sr.data: | |
| school_data = sr.data[0] | |
| if school_data.get('embedding'): | |
| try: | |
| school_data['embedding'] = eval(school_data['embedding']) | |
| except: | |
| school_data['embedding'] = None | |
| except: | |
| pass | |
| grants_response = supabase.table('grants').select("*").execute() | |
| if not grants_response.data: | |
| return pd.DataFrame(columns=headers) | |
| grants = grants_response.data | |
| scored_grants = [] | |
| for g in grants: | |
| try: | |
| if g.get('embedding'): | |
| try: | |
| grant_embedding = eval(g['embedding']) | |
| except: | |
| continue | |
| q_sim = cosine_similarity(query_embedding, grant_embedding) | |
| s_sim = 0 | |
| if school_data and school_data.get('embedding'): | |
| s_sim = cosine_similarity(school_data['embedding'], grant_embedding) | |
| context_score = 0 | |
| scored = 0.4 * q_sim + 0.3 * s_sim + 0.3 * context_score | |
| if scored > 0.1: | |
| scored_grants.append({ | |
| 'Title': g.get('title', ''), | |
| 'Provider': g.get('provider', ''), | |
| 'Due_date': g.get('due_date', ''), | |
| 'Description': g.get('description', ''), | |
| 'Funding_amount': g.get('funding_amount', ''), | |
| 'Requirements': g.get('requirements', ''), | |
| 'Eligibility': g.get('eligibility', ''), | |
| 'Categories': g.get('categories', ''), | |
| 'Score': round(scored, 3) | |
| }) | |
| except: | |
| continue | |
| if not scored_grants: | |
| return pd.DataFrame(columns=headers) | |
| scored_grants.sort(key=lambda x: x['Score'], reverse=True) | |
| df = pd.DataFrame(scored_grants) | |
| return df | |
| except: | |
| return pd.DataFrame(columns=headers) | |
| def chat_with_ai(message, history, grant_id=None, school_id=None): | |
| print(f"Chat with AI called with message: {message}, grant_id: {grant_id}, school_id: {school_id}") | |
| try: | |
| grant_data = None | |
| school_data = None | |
| school_profile = None | |
| if school_id and str(school_id).strip().lower() != 'none': | |
| sr = supabase.table('schools').select("*").eq('user_id', school_id).execute() | |
| if sr.data: | |
| school_data = sr.data[0] | |
| school_profile = f""" | |
| School Profile: | |
| Name: {school_data.get('name', '')} | |
| Type: {school_data.get('school_type', '')} | |
| Level: {school_data.get('educational_level', '')} | |
| Title I Status: {school_data.get('title_i_status', '')} | |
| Free/Reduced Lunch: {school_data.get('free_reduced_lunch_percentage', '')}% | |
| Enrollment: {school_data.get('enrollment', '')} | |
| Location: {school_data.get('city', '')}, {school_data.get('state', '')} | |
| Additional Info: {school_data.get('additional_info', '')} | |
| """ | |
| if grant_id and str(grant_id).strip().lower() != 'none': | |
| grr = supabase.table('grants').select("*").eq('grant_id', grant_id).execute() | |
| if not grr.data: | |
| grr = supabase.table('grants').select("*").eq('grant_id', grant_id).execute() | |
| if grr.data: | |
| grant_data = grr.data[0] | |
| system_prompt = f""" | |
| School Profile: {school_profile if school_profile else 'No school profile available'} | |
| Grant: {grant_data['title'] if grant_data else 'No specific grant selected'} | |
| You are GrantGPT, a grant writing assistant. Begin by summarizing the selected grant: | |
| "You are applying for [Grant Name], which provides [Funding Amount] for [Purpose]. The deadline is [Date]." | |
| Ask if they have a project in mind or need suggestions. Guide users through sections: | |
| - Project Abstract/Summary | |
| - Statement of Need | |
| - Program Description | |
| - Budget & Funding | |
| - Evaluation Plan | |
| Keep responses focused on grant writing assistance. Redirect unrelated questions to appropriate resources. | |
| """ | |
| messages = [{"role": "system", "content": system_prompt}] | |
| if history: | |
| for user_msg, assistant_msg in history: | |
| messages.append({"role": "user", "content": user_msg}) | |
| messages.append({"role": "assistant", "content": assistant_msg}) | |
| messages.append({"role": "user", "content": message}) | |
| response = client.chat.completions.create(model="gpt-4", messages=messages, max_tokens=2000, temperature=0.7) | |
| full_message = response.choices[0].message.content | |
| if (grant_id and str(grant_id).strip().lower() != 'none' and school_id and str(school_id).strip().lower() != 'none'): | |
| try: | |
| app_response = supabase.table('applications').select("id").eq('grant_id', grant_id).eq('school_id', school_id).execute() | |
| if not app_response.data: | |
| app_response = supabase.table('applications').insert({ | |
| 'grant_id': grant_id, | |
| 'school_id': school_id, | |
| 'status': 'Draft' | |
| }).execute() | |
| if app_response.data: | |
| application_id = app_response.data[0]['id'] | |
| supabase.table('application_chat_history').insert([ | |
| {'application_id': application_id, 'message_type': 'user', 'content': message}, | |
| {'application_id': application_id, 'message_type': 'assistant', 'content': full_message} | |
| ]).execute() | |
| except: | |
| pass | |
| return full_message | |
| except Exception as e: | |
| return f"Error: {str(e)}" | |
| custom_css = """ | |
| body { | |
| margin: 0; | |
| background: linear-gradient(to bottom, #c2e9fb, #e2fcff); | |
| font-family: 'Helvetica Neue', sans-serif; | |
| min-height: 100vh; | |
| } | |
| .gradio-container { | |
| max-width: 100% !important; | |
| margin: auto; | |
| padding: 0; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| min-height: 100vh; | |
| } | |
| .modal-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.2); | |
| z-index: 999; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| } | |
| .auth-modal { | |
| background: #fff; | |
| padding: 40px; | |
| border-radius: 10px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| z-index: 1000; | |
| width: 400px !important; | |
| max-width: 90% !important; | |
| height: auto !important; | |
| min-height: 300px !important; | |
| max-height: 400px !important; | |
| overflow-y: auto; | |
| margin: 0 auto; | |
| display: flex !important; | |
| flex-direction: column !important; | |
| justify-content: flex-start !important; | |
| } | |
| .landing-wrapper { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| width: 100%; | |
| padding: 20px; | |
| box-sizing: border-box; | |
| } | |
| .landing-buttons { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| width: 100%; | |
| margin-top: 20px; | |
| } | |
| .landing-button { | |
| background-color: #8ecae6 !important; | |
| color: #2c3e50 !important; | |
| border: none !important; | |
| border-radius: 25px !important; | |
| padding: 12px 20px !important; | |
| font-size: 16px !important; | |
| font-weight: 500 !important; | |
| cursor: pointer !important; | |
| transition: background-color 0.3s ease !important; | |
| width: 100% !important; | |
| text-align: center !important; | |
| } | |
| .landing-button:hover { | |
| background-color: #76b5da !important; | |
| } | |
| @media (max-width: 768px) { | |
| .auth-modal { | |
| width: 90% !important; | |
| padding: 20px; | |
| } | |
| } | |
| .auth-modal h1 { | |
| color: #2c3e50 !important; | |
| margin: 0 0 20px 0 !important; | |
| text-align: center; | |
| flex-shrink: 0; | |
| } | |
| .auth-modal button { | |
| background-color: #ffffff; | |
| color: #2c3e50; | |
| border: 1px solid #ccc; | |
| border-radius: 25px; | |
| padding: 0.6rem 1.2rem; | |
| font-size: 1rem; | |
| margin: 10px; | |
| cursor: pointer; | |
| } | |
| .auth-modal button:hover { | |
| background-color: #f0f0f0; | |
| } | |
| .login-form, .signup-form { | |
| margin-top: 20px; | |
| } | |
| .gr-text-input input[type=text], | |
| .gr-text-input input[type=password], | |
| textarea { | |
| background-color: #fff !important; | |
| color: #333 !important; | |
| border: 1px solid #ccc !important; | |
| border-radius: 4px !important; | |
| } | |
| h1, h2, h3, h4, h5, h6 { | |
| color: #2c3e50; | |
| } | |
| .grant-card { | |
| background: #ffffff; | |
| border-radius: 10px; | |
| padding: 20px; | |
| margin-bottom: 15px; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); | |
| cursor: pointer; | |
| } | |
| .grant-card:hover { | |
| box-shadow: 0 4px 8px rgba(0,0,0,0.15); | |
| } | |
| .nav-bar { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| background: #fff; | |
| padding: 10px 20px; | |
| border-bottom: 1px solid #ccc; | |
| } | |
| .nav-bar button { | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| font-weight: bold; | |
| color: #2c3e50; | |
| } | |
| .nav-bar button:hover { | |
| text-decoration: underline; | |
| } | |
| .grants-container { | |
| max-height: 600px; | |
| overflow-y: auto; | |
| padding: 20px; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: 10px; | |
| margin: 20px 0; | |
| } | |
| .grants-container::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .grants-container::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| border-radius: 4px; | |
| } | |
| .grants-container::-webkit-scrollbar-thumb { | |
| background: #888; | |
| border-radius: 4px; | |
| } | |
| .grants-container::-webkit-scrollbar-thumb:hover { | |
| background: #555; | |
| } | |
| .help-button-container { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| margin-top: 2rem; | |
| margin-bottom: 2rem; | |
| } | |
| .centered-button { | |
| min-width: 200px; | |
| background-color: #2c3e50; | |
| color: white; | |
| border: none; | |
| border-radius: 25px; | |
| padding: 1rem 2rem; | |
| font-size: 1.1rem; | |
| cursor: pointer; | |
| transition: background-color 0.3s ease; | |
| } | |
| .centered-button:hover { | |
| background-color: #34495e; | |
| } | |
| .grant-details { | |
| background: white; | |
| padding: 2rem; | |
| border-radius: 10px; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); | |
| color: #333; | |
| margin: 1rem; | |
| max-height: 70vh; | |
| overflow-y: auto; | |
| } | |
| .grant-details::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .grant-details::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| border-radius: 4px; | |
| } | |
| .grant-details::-webkit-scrollbar-thumb { | |
| background: #888; | |
| border-radius: 4px; | |
| } | |
| .grant-details::-webkit-scrollbar-thumb:hover { | |
| background: #555; | |
| } | |
| .grant-details h1, | |
| .grant-details h2, | |
| .grant-details h3, | |
| .grant-details h4, | |
| .grant-details h5, | |
| .grant-details h6 { | |
| color: #2c3e50; | |
| } | |
| .grant-details p { | |
| color: #333; | |
| line-height: 1.6; | |
| } | |
| .grant-details strong { | |
| color: #2c3e50; | |
| } | |
| .grant-details ul, | |
| .grant-details ol { | |
| color: #333; | |
| } | |
| .grant-details li { | |
| color: #333; | |
| line-height: 1.6; | |
| } | |
| .grant-details .loading { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 2rem; | |
| } | |
| .grant-details .loading::after { | |
| content: ""; | |
| width: 40px; | |
| height: 40px; | |
| border: 4px solid #f3f3f3; | |
| border-top: 4px solid #2c3e50; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| """ | |
| def build_login_form(): | |
| with gr.Column(elem_classes="login-form"): | |
| email = gr.Textbox(label="Email") | |
| password = gr.Textbox(label="Password", type="password") | |
| login_button = gr.Button("Log In") | |
| error = gr.Markdown(visible=False) | |
| return email, password, login_button, error | |
| def build_signup_form(): | |
| with gr.Column(elem_classes="signup-form"): | |
| email = gr.Textbox(label="Email") | |
| password = gr.Textbox(label="Password", type="password") | |
| name = gr.Textbox(label="School Name") | |
| with gr.Row(): | |
| address_line1 = gr.Textbox(label="Address Line 1") | |
| city = gr.Textbox(label="City") | |
| with gr.Row(): | |
| state = gr.Dropdown(["CA", "NY", "TX"], label="State") | |
| zip_code = gr.Textbox(label="ZIP Code") | |
| with gr.Row(): | |
| school_type = gr.Dropdown(["Public", "Private", "Charter", "Nonprofit", "Other"], label="School Type") | |
| educational_level = gr.Dropdown(["K-12", "Higher Education", "Both"], label="Educational Level") | |
| with gr.Row(): | |
| enrollment = gr.Number(label="Enrollment") | |
| title_i_status = gr.Checkbox(label="Title I Status") | |
| free_reduced_lunch_percentage = gr.Number(label="Free/Reduced Lunch %") | |
| contact_email = gr.Textbox(label="Contact Email") | |
| additional_info = gr.Textbox(label="Additional Info", lines=3) | |
| signup_button = gr.Button("Create Profile") | |
| error = gr.Markdown(visible=False) | |
| return ( | |
| email, password, name, address_line1, city, state, zip_code, | |
| school_type, educational_level, enrollment, title_i_status, | |
| free_reduced_lunch_percentage, contact_email, additional_info, | |
| signup_button, error | |
| ) | |
| with gr.Blocks(css=custom_css) as demo: | |
| page_state = gr.State("landing") | |
| current_school_id = gr.State(None) | |
| selected_grant = gr.State(None) | |
| selected_grant_id = gr.State(None) | |
| is_authenticated = gr.State(False) | |
| def router(page): | |
| return { | |
| landing_page: gr.update(visible=(page == "landing")), | |
| login_page: gr.update(visible=(page == "login")), | |
| signup_page: gr.update(visible=(page == "signup")), | |
| home_page: gr.update(visible=(page == "home")), | |
| grant_details_page: gr.update(visible=(page == "grant_details")), | |
| chat_page: gr.update(visible=(page == "chat")) | |
| } | |
| with gr.Column(visible=True) as landing_page: | |
| with gr.Column(elem_classes="modal-overlay"): | |
| with gr.Column(elem_classes="auth-modal"): | |
| gr.Markdown("# Welcome to GrantRight!") | |
| with gr.Column(elem_classes="landing-buttons"): | |
| create_account_btn = gr.Button("CREATE ACCOUNT", elem_classes="landing-button") | |
| login_btn = gr.Button("LOG INTO EXISTING ACCOUNT", elem_classes="landing-button") | |
| def go_signup(): | |
| return "signup" | |
| def go_login(): | |
| return "login" | |
| create_account_btn.click(go_signup, None, page_state) | |
| login_btn.click(go_login, None, page_state) | |
| with gr.Column(visible=False) as login_page: | |
| with gr.Column(elem_classes="auth-modal"): | |
| gr.Markdown("## Log In") | |
| login_email, login_password, login_submit, login_error = build_login_form() | |
| def handle_login_click(email, pwd): | |
| success, result, school_id = login(email, pwd) | |
| if success: | |
| if school_id: | |
| return { | |
| is_authenticated: True, | |
| current_school_id: school_id, | |
| page_state: "home", | |
| login_error: gr.update(visible=False) | |
| } | |
| return { | |
| is_authenticated: True, | |
| current_school_id: None, | |
| page_state: "home", | |
| login_error: gr.update(visible=False) | |
| } | |
| else: | |
| return {login_error: gr.update(visible=True, value=result)} | |
| login_submit.click( | |
| handle_login_click, | |
| inputs=[login_email, login_password], | |
| outputs=[is_authenticated, current_school_id, page_state, login_error] | |
| ) | |
| with gr.Column(visible=False) as signup_page: | |
| with gr.Column(elem_classes="auth-modal"): | |
| gr.Markdown("## Create School Profile") | |
| ( | |
| su_email, su_password, su_name, su_address, su_city, | |
| su_state, su_zip, su_type, su_level, su_enroll, | |
| su_titlei, su_lunch, su_contact, su_info, | |
| su_submit, su_error | |
| ) = build_signup_form() | |
| def handle_signup_click( | |
| username, password, name, address_line1, city, state, zip_code, | |
| school_type, educational_level, enrollment, title_i_status, | |
| free_reduced_lunch_percentage, contact_email, additional_info | |
| ): | |
| success, msg, school_id = sign_up_school( | |
| username, password, name, address_line1, city, state, zip_code, | |
| school_type, educational_level, enrollment, title_i_status, | |
| free_reduced_lunch_percentage, contact_email, additional_info | |
| ) | |
| if success: | |
| return { | |
| is_authenticated: True, | |
| current_school_id: school_id, | |
| page_state: "home", | |
| su_error: gr.update(visible=False) | |
| } | |
| else: | |
| return {su_error: gr.update(visible=True, value=msg)} | |
| su_submit.click( | |
| handle_signup_click, | |
| inputs=[ | |
| su_email, su_password, su_name, su_address, su_city, su_state, | |
| su_zip, su_type, su_level, su_enroll, su_titlei, su_lunch, | |
| su_contact, su_info | |
| ], | |
| outputs=[is_authenticated, current_school_id, page_state, su_error] | |
| ) | |
| with gr.Column(visible=False) as home_page: | |
| with gr.Row(elem_classes="nav-bar"): | |
| nav_home_btn = gr.Button("Home") | |
| school_label = gr.Markdown("") | |
| search_bar = gr.Textbox(placeholder="Example: We're looking for STEM education grants to establish a robotics program...") | |
| search_button = gr.Button("Search") | |
| search_loading_indicator = gr.Markdown("", visible=False, elem_classes="loading") | |
| grants_container = gr.Column(elem_classes="grants-container") | |
| grant_dropdown = gr.Dropdown( | |
| choices=[], | |
| label="Select a Grant", | |
| visible=False | |
| ) | |
| selected_grant_id = gr.State(None) | |
| view_grant_btn = gr.Button( | |
| "View Grant Details", | |
| visible=False | |
| ) | |
| def update_school_label(sid): | |
| if sid: | |
| return get_school_name(sid) | |
| return "No School Selected" | |
| current_school_id.change(update_school_label, current_school_id, school_label) | |
| def do_search(q, sid): | |
| df = search_grants(q, sid) | |
| if df.empty: | |
| return { | |
| grants_container: gr.update(value="<p>No grants found matching your search.</p>"), | |
| grant_dropdown: gr.update(choices=[], visible=False), | |
| view_grant_btn: gr.update(visible=False), | |
| selected_grant_id: None | |
| } | |
| df = df.nlargest(30, 'Score') | |
| html_content = "" | |
| for _, row in df.iterrows(): | |
| html_content += f""" | |
| <div class="grant-card"> | |
| <h3>{row['Title']}</h3> | |
| <p>{row['Description'][:80]}...</p> | |
| <p>Due: {row['Due_date']}</p> | |
| <p>Amount: {row['Funding_amount']}</p> | |
| <p>Score: {row['Score']}</p> | |
| </div> | |
| """ | |
| grants_data = {} | |
| for _, row in df.iterrows(): | |
| grants_data[row['Title']] = row['id'] if 'id' in row else None | |
| choices = [(row['Title'], row['Title']) for _, row in df.iterrows()] | |
| return { | |
| grants_container: gr.update(value=html_content), | |
| grant_dropdown: gr.update(choices=choices, visible=True), | |
| view_grant_btn: gr.update(visible=True), | |
| selected_grant_id: None | |
| } | |
| def handle_grant_selection(title, sid): | |
| if not title: | |
| return "home", None, None | |
| r = supabase.table('grants').select("grant_id").eq('title', title).execute() | |
| grant_id = None | |
| if r.data and len(r.data) > 0: | |
| grant_id = r.data[0]['grant_id'] | |
| print(f"Selected grant ID: {grant_id} for title: {title}") | |
| else: | |
| print(f"No grant ID found for title: {title}") | |
| return "grant_details", title, grant_id | |
| search_button.click( | |
| lambda: gr.update(visible=True), | |
| None, | |
| search_loading_indicator, | |
| queue=False | |
| ).then( | |
| do_search, | |
| inputs=[search_bar, current_school_id], | |
| outputs=[grants_container, grant_dropdown, view_grant_btn, selected_grant_id] | |
| ).then( | |
| lambda: gr.update(visible=False), | |
| None, | |
| search_loading_indicator, | |
| queue=False | |
| ) | |
| view_grant_btn.click( | |
| handle_grant_selection, | |
| inputs=[grant_dropdown, current_school_id], | |
| outputs=[page_state, selected_grant, selected_grant_id] | |
| ) | |
| with gr.Column(visible=False) as grant_details_page: | |
| with gr.Row(elem_classes="nav-bar"): | |
| nav_home_btn2 = gr.Button("Home") | |
| school_label2 = gr.Markdown("") | |
| with gr.Column(elem_classes="grant-details"): | |
| grant_title = gr.Markdown("") | |
| grant_info = gr.Markdown("") | |
| loading_indicator = gr.Markdown("", visible=False) | |
| with gr.Row(elem_classes="help-button-container"): | |
| help_write_btn = gr.Button("Help Me Write", size="lg", elem_classes="centered-button") | |
| def load_grant_details(title): | |
| if not title: | |
| return "", "", gr.update(visible=True) | |
| return "", gr.update(visible=True), gr.update(visible=True) | |
| def process_grant_details(title): | |
| if not title: | |
| return "", "", gr.update(visible=False) | |
| r = supabase.table('grants').select("*").eq('title', title).execute() | |
| if not r.data: | |
| return "", "", gr.update(visible=False) | |
| g = r.data[0] | |
| t = f"# {g.get('title', '')}" | |
| description = g.get('description', '') | |
| try: | |
| response = client.chat.completions.create( | |
| model="gpt-4", | |
| messages=[ | |
| {"role": "system", "content": "You are a grant writing assistant. Convert the following grant description into clear, concise sentences that highlight the most important information for grant applicants. Focus on key details about the grant's purpose, requirements, and benefits. Keep the language simple and direct."}, | |
| {"role": "user", "content": description} | |
| ], | |
| max_tokens=500, | |
| temperature=0.3 | |
| ) | |
| processed_description = response.choices[0].message.content | |
| except: | |
| processed_description = description | |
| d = f""" | |
| ### Grant Details | |
| **Description** | |
| {processed_description} | |
| **Important Information** | |
| - Due Date: {g.get('due_date', '')} | |
| - Amount: {g.get('funding_amount', '')} | |
| - Provider: {g.get('provider', '')} | |
| """ | |
| return t, d, gr.update(visible=False) | |
| selected_grant.change( | |
| load_grant_details, | |
| selected_grant, | |
| [grant_title, grant_info, loading_indicator] | |
| ).then( | |
| process_grant_details, | |
| selected_grant, | |
| [grant_title, grant_info, loading_indicator] | |
| ) | |
| def go_chat(): | |
| return "chat" | |
| help_write_btn.click(go_chat, None, page_state) | |
| with gr.Column(visible=False) as chat_page: | |
| with gr.Row(elem_classes="nav-bar"): | |
| nav_home_btn3 = gr.Button("Home") | |
| school_label3 = gr.Markdown("") | |
| chat_grant_id = gr.Textbox(visible=False) | |
| chat_school_id = gr.Textbox(visible=False) | |
| def set_chat_vars(grant_id, school_id): | |
| print(f"Setting chat variables - Grant ID: {grant_id}, School ID: {school_id}") | |
| return grant_id, school_id | |
| def chat_wrapper(message, history): | |
| grant_id = chat_grant_id.value | |
| school_id = chat_school_id.value | |
| print(f"Chat wrapper called with grant_id: {grant_id}, school_id: {school_id}") | |
| return chat_with_ai(message, history, grant_id, school_id) | |
| help_write_btn.click( | |
| set_chat_vars, | |
| inputs=[selected_grant_id, current_school_id], | |
| outputs=[chat_grant_id, chat_school_id], | |
| queue=False | |
| ).then( | |
| lambda: "chat", | |
| None, | |
| page_state | |
| ) | |
| chat = gr.ChatInterface(fn=chat_wrapper) | |
| def go_home(): | |
| return "home" | |
| nav_home_btn.click(go_home, None, page_state) | |
| nav_home_btn2.click(go_home, None, page_state) | |
| nav_home_btn3.click(go_home, None, page_state) | |
| page_state.change(router, page_state, [landing_page, login_page, signup_page, home_page, grant_details_page, chat_page]) | |
| demo.load(fn=lambda: "landing", inputs=None, outputs=page_state) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860, debug=True, share=True) | |