Spaces:
Sleeping
Sleeping
from typing import List, Tuple, Optional, Any | |
import os | |
import gradio as gr | |
try: | |
from base.api_base import BaseAPI | |
except ImportError: | |
from .base.api_base import BaseAPI | |
class OrgSearch(BaseAPI): | |
def __init__(self): | |
super().__init__( | |
url=f"{os.getenv('CDS_API_URL')}/v1/organization/search", | |
headers={"x-api-key": os.getenv('CDS_API_KEY')} | |
) | |
def __call__(self, name: str, **kwargs): | |
is_valid = False | |
payload = { | |
"names": [{ | |
"value": name, | |
"type": "main" | |
}], | |
"status": "authorized" | |
} | |
if kwargs.get("ein"): | |
ein = kwargs.get("ein") | |
if "-" not in ein: | |
ein = f"{ein[:2]}-{ein[2:]}" | |
payload["ids"] = [{ | |
"value": ein, | |
"type": "ein" | |
}] | |
is_valid = True | |
if kwargs.get("street") or kwargs.get("city") or kwargs.get("state") or kwargs.get("postal_code"): | |
payload["addresses"] = [{ | |
"street1": kwargs.get("street") or "", | |
"city": kwargs.get("city") or "", | |
"state": kwargs.get("state") or "", | |
"postal_code": kwargs.get("postal_code") or "" | |
}] | |
is_valid = True | |
if not is_valid: | |
return None | |
result = self.post(payload=payload) | |
return result.get("payload", []) | |
search_org = OrgSearch() | |
def callback_to_state(event: gr.SelectData, state: gr.State) -> Tuple[List[Any], int]: | |
"""Handles DataFrame `select` events. Updates the internal state for either the recipient or funder based on | |
selection. Also sends along the selected Candid entity ID to the proposal generation tab. | |
Parameters | |
---------- | |
event : gr.SelectData | |
state : gr.State | |
Returns | |
------- | |
Tuple[List[Any], int] | |
(Updated state, Candid entity ID) | |
""" | |
row, _ = event.index | |
if len(state) == 0: | |
return [], None | |
# the state should be a nested list of lists | |
# if the state is a single list with non-list elements then we just want a pass-through | |
if all(isinstance(s, list) for s in state): | |
return state[row], state[row][0] | |
return state, state[0] | |
def lookup_organization( | |
name: str, | |
ein: Optional[str] = None, | |
# street: Optional[str] = None, | |
city: Optional[str] = None, | |
state: Optional[str] = None, | |
postal: Optional[str] = None, | |
) -> Tuple[List[List[str]], List[List[str]]]: | |
"""Performs a simple search using the CDS organization search API. Results are sent to the DataFrame table and also | |
populate the state for the recipient information. | |
Parameters | |
---------- | |
name : str | |
Org name | |
ein : Optional[str], optional | |
Org EIN, by default None | |
street : Optional[str], optional | |
Street address, by default None | |
city : Optional[str], optional | |
Address city, by default None | |
state : Optional[str], optional | |
Address state, by default None | |
postal : Optional[str], optional | |
Address postal code, by default None | |
Returns | |
------- | |
Tuple[List[List[str]], List[List[str]]] | |
(recip data, recip data) | |
Raises | |
------ | |
gr.Error | |
Raised if not enough information was entered to run a search | |
gr.Error | |
Raised if no search results were returned | |
""" | |
results = search_org(name=name, ein=ein, city=city, state=state, postal=postal) | |
if results is None: | |
raise gr.Error("You must provide a name, and either an EIN or an address.") | |
if not results: | |
raise gr.Error("No organizations could be found. Please refine the search criteria.") | |
data = [] | |
for applicant_data in results: | |
address = applicant_data.get("addresses", [{}])[0].get("normalized") | |
seal = (applicant_data.get("current_seal", {}) or {}).get("image") | |
record = [ | |
applicant_data.get('candid_entity_id'), | |
applicant_data.get('main_sort_name'), | |
address | |
] | |
if seal: | |
record.append(f"") | |
else: | |
record.append("") | |
data.append(record) | |
return data, data | |
def render(org_id_element: Optional[gr.Blocks] = None) -> Tuple[gr.Blocks, gr.State]: | |
"""Main blocks build and render function. | |
Parameters | |
---------- | |
org_id_element : Optional[gr.Blocks], optional | |
Callback Gradio element, by default None | |
Returns | |
------- | |
Tuple[gr.Blocks, gr.State] | |
(component, selected org state) | |
""" | |
with gr.Blocks() as component: | |
org_data = gr.State([]) | |
selected_org_data = gr.State([]) | |
with gr.Row(): | |
with gr.Column(scale=2): | |
name = gr.Textbox(label="Name of organization", lines=1) | |
ein = gr.Textbox(label="EIN of organization", lines=1) | |
with gr.Column(scale=3): | |
with gr.Group(): | |
with gr.Row(): | |
with gr.Column(): | |
# street = gr.Textbox(label="Street address", lines=1) | |
city = gr.Textbox(label="City", lines=1) | |
with gr.Column(): | |
state = gr.Textbox(label="State/province", lines=1) | |
postal = gr.Textbox(label="Postal code", lines=1) | |
search_button = gr.Button("Find organization", variant="primary") | |
org_info = gr.DataFrame( | |
label="Organizations", | |
type="array", | |
headers=["Candid ID", "Name", "Address", "Seal"], | |
col_count=(4, "fixed"), | |
datatype=["number", "str", "str", "markdown"], | |
wrap=True, | |
column_widths=["20%", "30%", "30%", "20%"] | |
) | |
if org_id_element is None: | |
org_id_element = gr.Textbox(label="Selected Candid entity ID", lines=1) | |
# pylint: disable=no-member | |
search_button.click( | |
fn=lambda name, ein, city, state, postal: lookup_organization( | |
name=name, | |
ein=ein, | |
# street=street, | |
city=city, | |
state=state, | |
postal=postal | |
), | |
# inputs=[name, ein, street, city, state, postal], | |
inputs=[name, ein, city, state, postal], | |
outputs=[org_info, org_data], | |
api_name=False, | |
show_api=False | |
) | |
# pylint: disable=no-member | |
org_info.select( | |
fn=callback_to_state, | |
inputs=org_data, | |
outputs=[selected_org_data, org_id_element], | |
api_name=False, | |
show_api=False | |
) | |
return component, selected_org_data | |