|
from typing import List, Dict, Tuple, Union, Optional, Any |
|
import os |
|
|
|
import gradio as gr |
|
|
|
import requests |
|
import backoff |
|
|
|
|
|
class OrganizationNotFound(Exception): |
|
"No organization(s) found" |
|
|
|
|
|
@backoff.on_exception( |
|
backoff.expo, |
|
(requests.exceptions.Timeout, requests.exceptions.ConnectionError), |
|
max_tries=5 |
|
) |
|
def _get_json(route: str, **request_kwargs) -> Dict[str, Any] | str: |
|
r = requests.get( |
|
url=f"{os.getenv('LOI_API_URL')}/{route}", |
|
params=request_kwargs, |
|
headers={"x-api-key": os.getenv("LOI_API_KEY")}, |
|
timeout=30 |
|
) |
|
|
|
r.raise_for_status() |
|
return r.json().get("response") |
|
|
|
|
|
@backoff.on_exception( |
|
backoff.expo, |
|
(requests.exceptions.Timeout, requests.exceptions.ConnectionError), |
|
max_tries=5 |
|
) |
|
def _post_json(route: str, *, payload: Dict[str, Any]) -> Dict[str, Any] | str | int: |
|
r = requests.post( |
|
url=f"{os.getenv('LOI_API_URL')}/{route}", |
|
json=payload, |
|
headers={"x-api-key": os.getenv("LOI_API_KEY")}, |
|
timeout=30 |
|
) |
|
|
|
r.raise_for_status() |
|
return r.json().get("response") |
|
|
|
|
|
def organization_pair_autofill( |
|
recipient_name: str, |
|
recipient_ein: str, |
|
funder_name: str, |
|
funder_ein: str |
|
): |
|
recip_match = _get_json("/organization/search", name=recipient_name, ein=recipient_ein) |
|
if len(recip_match or []) == 0: |
|
|
|
raise gr.Error("No matching recipient could be found") |
|
gr.Info(f"{recipient_name} found, auto-filling fields...") |
|
|
|
funder_match = _get_json("/organization/search", name=funder_name, ein=funder_ein) |
|
if len(funder_match or []) == 0: |
|
|
|
raise gr.Error("No matching funder could be found") |
|
gr.Info(f"{funder_name} found, auto-filling fields...") |
|
|
|
data = _get_json( |
|
"/organization/autofill", |
|
recipient_candid_entity_id=recip_match[0]["candid_entity_id"], |
|
funder_candid_entity_id=funder_match[0]["candid_entity_id"], |
|
) |
|
|
|
return ( |
|
data.get("recipient_data", {}).get("projects_text"), |
|
data.get("recipient_data", {}).get("capacity_text"), |
|
data.get("recipient_data", {}).get("contact_text"), |
|
data.get("recipient_data", {}).get("data_text"), |
|
data.get("recipient_data", {}).get("mission_statement_text"), |
|
data.get("funder_data", {}).get("mission_statement_text"), |
|
data.get("funding_history_text"), |
|
data.get("recipient_data", {}).get("org_data"), |
|
recip_match[0]["candid_entity_id"], |
|
data.get("funder_data", {}).get("org_data"), |
|
funder_match[0]["candid_entity_id"], |
|
) |
|
|
|
|
|
def cost_estimator( |
|
recipient_candid_entity_id: Union[int, str], |
|
recipient_data: Dict[str, Any], |
|
funder_data: Dict[str, Any], |
|
program_desc: Optional[str] = None |
|
) -> str: |
|
estimate: str = _post_json( |
|
"budget", |
|
payload={ |
|
"recipient_candid_entity_id": recipient_candid_entity_id, |
|
"program_description": program_desc, |
|
"recipient_data": recipient_data, |
|
"funder_data": funder_data, |
|
} |
|
) |
|
return estimate |
|
|
|
|
|
def identify_vague_statements(text: str) -> Tuple[List[str], List[str], List[str]]: |
|
data = _get_json("/editorialai/vaguestatement", input_section=text) |
|
|
|
statements, alternatives, reasons = [], [], [] |
|
for record in data: |
|
statements.append(record["vague_statement"]) |
|
alternatives.append(record["alternative_text"]) |
|
reasons.append(record["reason"]) |
|
|
|
return statements, alternatives, reasons |
|
|
|
|
|
def help_mission_statement(recipient_name: str, recipient_mission_info: str): |
|
return _get_json("/writerhelper/missionstatement", info=recipient_mission_info, org_name=recipient_name) |
|
|
|
|
|
def draft_letter( |
|
recipient_name: str, funder_name: str, projects: str, amount: str, |
|
recipient_mission_statement: str = "", funder_mission_statement: str = "", |
|
project_name: str = "", project_purpose: str = "", |
|
prior_contact: str = "", connection: str = "", |
|
capacity: str = "", path_to_solution: str = "", recent_accomplishments: str = "", |
|
recipient_history: str = "", mutual_history: str = "", |
|
geo_pop_targets: str = "", project_data_stats: str = "", |
|
desired_objectives: str = "", major_activities: str = "", key_staff: str = "", standout_features: str = "", |
|
success_metric: str = "", |
|
other_funding_sources: str = "", |
|
contact_information: str = "" |
|
): |
|
gr.Info("Writing the letter, please scroll to the top of the page.") |
|
opening: str = _post_json( |
|
"/writer/opening", |
|
payload=dict( |
|
funder_name=funder_name, |
|
recipient_name=recipient_name, |
|
project_name=project_name, |
|
project_purpose=project_purpose, |
|
amount=amount, |
|
prior_contact=prior_contact, |
|
connection=connection |
|
) |
|
) |
|
|
|
recipient_description: str = _post_json( |
|
"/writer/org", |
|
payload=dict( |
|
opening=opening, |
|
recipient_mission_statement=recipient_mission_statement, |
|
capacity=capacity, |
|
history=recipient_history, |
|
path=path_to_solution, |
|
accomplishment=recent_accomplishments |
|
) |
|
) |
|
|
|
need_statement: str = _post_json( |
|
"/writer/need", |
|
payload=dict( |
|
recipient_desc=recipient_description, |
|
target=geo_pop_targets, |
|
data=project_data_stats, |
|
funder_mission_statement=funder_mission_statement |
|
) |
|
) |
|
|
|
project_description: str = _post_json( |
|
"/writer/project", |
|
payload=dict( |
|
need=need_statement, |
|
projects=projects, |
|
desired_objectives=desired_objectives, |
|
major_activities=major_activities, |
|
key_staff=key_staff, |
|
stand_out=standout_features, |
|
success=success_metric |
|
) |
|
) |
|
|
|
funding_request: str = _post_json( |
|
"/writer/fund", |
|
payload=dict( |
|
project_desc=project_description, |
|
amount=amount, |
|
funding_history=mutual_history, |
|
other_funding=other_funding_sources |
|
) |
|
) |
|
|
|
conclusion: str = _post_json( |
|
"/writer/conclusion", |
|
payload=dict( |
|
funding_request=funding_request, |
|
project_desc=project_description, |
|
follow_up=contact_information |
|
) |
|
) |
|
|
|
return ( |
|
opening, |
|
recipient_description, |
|
need_statement, |
|
project_description, |
|
funding_request, |
|
conclusion |
|
) |
|
|
|
|
|
def send_feedback( |
|
context: Dict[str, Any], |
|
letter: Dict[str, Any], |
|
relevance: int, |
|
coherence: int, |
|
fluency: int, |
|
overall_quality: int, |
|
comments: Optional[str] = None, |
|
email: Optional[str] = None |
|
) -> int: |
|
count = _post_json( |
|
"feedback", |
|
payload={ |
|
"context": context, |
|
"letter": letter, |
|
"relevance": relevance, |
|
"coherence": coherence, |
|
"fluency": fluency, |
|
"overall_quality": overall_quality, |
|
"comments": comments, |
|
"email": email |
|
} |
|
) |
|
return count |
|
|