Spaces:
Running
Running
| 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 | |