Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import requests
|
| 3 |
-
import json
|
| 4 |
import pandas as pd
|
| 5 |
-
from difflib import get_close_matches
|
| 6 |
|
| 7 |
KYS_SAMPLE = "https://kys.udiseplus.gov.in/webapp/api/search-schools?searchType=3&searchParam={udise}"
|
| 8 |
|
|
@@ -23,12 +24,12 @@ def call_tavily(tavily_url, api_key, payload_text):
|
|
| 23 |
if api_key:
|
| 24 |
headers["Authorization"] = api_key
|
| 25 |
body = {"query": payload_text}
|
|
|
|
| 26 |
try:
|
| 27 |
resp = requests.post(tavily_url, json=body, headers=headers, timeout=12)
|
| 28 |
resp.raise_for_status()
|
| 29 |
return {"ok": True, "url": tavily_url, "status_code": resp.status_code, "data": resp.json()}
|
| 30 |
except Exception as e:
|
| 31 |
-
# fallback to GET
|
| 32 |
try:
|
| 33 |
params = {"q": payload_text}
|
| 34 |
resp = requests.get(tavily_url, params=params, headers=headers, timeout=12)
|
|
@@ -39,7 +40,6 @@ def call_tavily(tavily_url, api_key, payload_text):
|
|
| 39 |
|
| 40 |
|
| 41 |
def extract_udise_candidates_from_tavily(tavily_json):
|
| 42 |
-
# Heuristic: walk the JSON and collect strings that look like UDISE (digits, length 6-14)
|
| 43 |
found = set()
|
| 44 |
|
| 45 |
def walk(obj):
|
|
@@ -61,20 +61,13 @@ def extract_udise_candidates_from_tavily(tavily_json):
|
|
| 61 |
|
| 62 |
|
| 63 |
def json_to_table(obj):
|
| 64 |
-
# Try to convert a JSON object/array to pandas DataFrame for display
|
| 65 |
try:
|
| 66 |
if isinstance(obj, list):
|
| 67 |
-
|
| 68 |
-
return df
|
| 69 |
if isinstance(obj, dict):
|
| 70 |
-
|
| 71 |
-
if "results" in obj and isinstance(obj["results"], list):
|
| 72 |
-
return pd.json_normalize(obj["results"])
|
| 73 |
-
# common patterns: top-level list wrapped in data or hits
|
| 74 |
-
for k in ("data", "hits", "items", "results"):
|
| 75 |
if k in obj and isinstance(obj[k], list):
|
| 76 |
return pd.json_normalize(obj[k])
|
| 77 |
-
# fallback: flatten dict to single-row table
|
| 78 |
return pd.json_normalize([obj])
|
| 79 |
except Exception:
|
| 80 |
pass
|
|
@@ -88,8 +81,7 @@ def to_table_from_kys(kys_json):
|
|
| 88 |
items = items["data"]
|
| 89 |
if not items:
|
| 90 |
return pd.DataFrame()
|
| 91 |
-
|
| 92 |
-
return df
|
| 93 |
except Exception:
|
| 94 |
return pd.DataFrame()
|
| 95 |
|
|
@@ -106,7 +98,6 @@ def search_workflow(school_name_or_udise, tavily_url, tavily_key, use_tavily, us
|
|
| 106 |
else:
|
| 107 |
out["tavily"] = {"ok": False, "error": "Tavily disabled or no URL provided"}
|
| 108 |
|
| 109 |
-
# If user provided a numeric UDISE directly and KYS requested, do KYS
|
| 110 |
if use_kys and school_name_or_udise and school_name_or_udise.strip().isdigit() and 6 <= len(school_name_or_udise.strip()) <= 14:
|
| 111 |
kys_res = call_kys_by_udise(school_name_or_udise.strip())
|
| 112 |
out["kys"] = kys_res
|
|
@@ -115,17 +106,20 @@ def search_workflow(school_name_or_udise, tavily_url, tavily_key, use_tavily, us
|
|
| 115 |
|
| 116 |
|
| 117 |
with gr.Blocks() as demo:
|
| 118 |
-
|
| 119 |
-
# Tavily + KYS Search (Hugging Face Space)
|
| 120 |
-
|
| 121 |
-
Enter a school name or a UDISE code and call KYS (UDISE) or your Tavily search endpoint.
|
| 122 |
-
""")
|
| 123 |
|
|
|
|
|
|
|
| 124 |
|
| 125 |
with gr.Row():
|
| 126 |
-
inp = gr.Textbox(label="School name
|
| 127 |
tavily_url = gr.Textbox(label="Tavily endpoint URL (POST/GET)", placeholder="https://your-tavily.example/api/search")
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
with gr.Row():
|
| 131 |
use_tavily = gr.Checkbox(value=True, label="Call Tavily")
|
|
@@ -134,36 +128,43 @@ with gr.Blocks() as demo:
|
|
| 134 |
run = gr.Button("Search Tavily")
|
| 135 |
|
| 136 |
output_json = gr.JSON(label="Raw Tavily Output (JSON)")
|
| 137 |
-
tavily_table = gr.DataFrame(headers=None, label="
|
| 138 |
|
| 139 |
-
gr.Markdown(
|
| 140 |
-
|
| 141 |
-
suggestions_dropdown = gr.Dropdown(choices=[], label="UDISE candidates (from Tavily)''')
|
| 142 |
udise_input = gr.Textbox(label="UDISE to lookup (editable)", placeholder="Pick a candidate or type a UDISE code...", lines=1)
|
| 143 |
lookup_btn = gr.Button("Lookup UDISE (Call KYS)")
|
| 144 |
|
| 145 |
kys_output_json = gr.JSON(label="KYS Raw Output")
|
| 146 |
kys_table = gr.DataFrame(headers=None, label="KYS results (table)")
|
| 147 |
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
tav_df = pd.DataFrame()
|
| 152 |
if res.get("tavily") and res["tavily"].get("ok"):
|
| 153 |
tav_df = json_to_table(res["tavily"]["data"])
|
| 154 |
-
# suggestions for dropdown
|
| 155 |
suggestions = res.get("suggestions", [])
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
-
run.click
|
|
|
|
| 159 |
|
| 160 |
-
# When user selects a suggestion, populate the editable UDISE textbox
|
| 161 |
def on_select_suggestion(choice):
|
| 162 |
return choice or ""
|
| 163 |
|
| 164 |
suggestions_dropdown.change(on_select_suggestion, inputs=[suggestions_dropdown], outputs=[udise_input])
|
| 165 |
|
| 166 |
-
# Lookup button handler: call KYS for whatever is in udise_input
|
| 167 |
def on_lookup_udise(udise_code):
|
| 168 |
if not udise_code or not udise_code.strip().isdigit():
|
| 169 |
return {"ok": False, "error": "Provide a numeric UDISE code (6-14 digits)."}, pd.DataFrame()
|
|
@@ -175,12 +176,18 @@ The dropdown will list all numeric tokens resembling UDISE codes found in Tavily
|
|
| 175 |
|
| 176 |
lookup_btn.click(on_lookup_udise, inputs=[udise_input], outputs=[kys_output_json, kys_table])
|
| 177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
gr.Markdown('''---
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
|
| 185 |
if __name__ == "__main__":
|
| 186 |
demo.launch()
|
|
|
|
| 1 |
+
# tavily_kys_space.py
|
| 2 |
+
# Gradio app for Hugging Face Spaces (Tavily API key textbox + Save key in session)
|
| 3 |
+
|
| 4 |
import gradio as gr
|
| 5 |
import requests
|
|
|
|
| 6 |
import pandas as pd
|
|
|
|
| 7 |
|
| 8 |
KYS_SAMPLE = "https://kys.udiseplus.gov.in/webapp/api/search-schools?searchType=3&searchParam={udise}"
|
| 9 |
|
|
|
|
| 24 |
if api_key:
|
| 25 |
headers["Authorization"] = api_key
|
| 26 |
body = {"query": payload_text}
|
| 27 |
+
|
| 28 |
try:
|
| 29 |
resp = requests.post(tavily_url, json=body, headers=headers, timeout=12)
|
| 30 |
resp.raise_for_status()
|
| 31 |
return {"ok": True, "url": tavily_url, "status_code": resp.status_code, "data": resp.json()}
|
| 32 |
except Exception as e:
|
|
|
|
| 33 |
try:
|
| 34 |
params = {"q": payload_text}
|
| 35 |
resp = requests.get(tavily_url, params=params, headers=headers, timeout=12)
|
|
|
|
| 40 |
|
| 41 |
|
| 42 |
def extract_udise_candidates_from_tavily(tavily_json):
|
|
|
|
| 43 |
found = set()
|
| 44 |
|
| 45 |
def walk(obj):
|
|
|
|
| 61 |
|
| 62 |
|
| 63 |
def json_to_table(obj):
|
|
|
|
| 64 |
try:
|
| 65 |
if isinstance(obj, list):
|
| 66 |
+
return pd.json_normalize(obj)
|
|
|
|
| 67 |
if isinstance(obj, dict):
|
| 68 |
+
for k in ("results", "data", "hits", "items"):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
if k in obj and isinstance(obj[k], list):
|
| 70 |
return pd.json_normalize(obj[k])
|
|
|
|
| 71 |
return pd.json_normalize([obj])
|
| 72 |
except Exception:
|
| 73 |
pass
|
|
|
|
| 81 |
items = items["data"]
|
| 82 |
if not items:
|
| 83 |
return pd.DataFrame()
|
| 84 |
+
return pd.json_normalize(items)
|
|
|
|
| 85 |
except Exception:
|
| 86 |
return pd.DataFrame()
|
| 87 |
|
|
|
|
| 98 |
else:
|
| 99 |
out["tavily"] = {"ok": False, "error": "Tavily disabled or no URL provided"}
|
| 100 |
|
|
|
|
| 101 |
if use_kys and school_name_or_udise and school_name_or_udise.strip().isdigit() and 6 <= len(school_name_or_udise.strip()) <= 14:
|
| 102 |
kys_res = call_kys_by_udise(school_name_or_udise.strip())
|
| 103 |
out["kys"] = kys_res
|
|
|
|
| 106 |
|
| 107 |
|
| 108 |
with gr.Blocks() as demo:
|
| 109 |
+
# Fixed: properly terminated Markdown strings
|
| 110 |
+
gr.Markdown('''# Tavily + KYS Search (Hugging Face Space)
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
+
Enter a school name or a UDISE code, Tavily endpoint, and optionally an API key.
|
| 113 |
+
Use the 'Save key in session' toggle to keep the key for the current browser session.''')
|
| 114 |
|
| 115 |
with gr.Row():
|
| 116 |
+
inp = gr.Textbox(label="School name", placeholder="'Govt High School...'", lines=1)
|
| 117 |
tavily_url = gr.Textbox(label="Tavily endpoint URL (POST/GET)", placeholder="https://your-tavily.example/api/search")
|
| 118 |
+
|
| 119 |
+
# Tavily API key textbox + save toggle
|
| 120 |
+
tavily_key = gr.Textbox(label="Tavily API Key (optional)", placeholder="Bearer ... or APIKEY ...", lines=1)
|
| 121 |
+
save_key_toggle = gr.Checkbox(value=False, label="Save key in session (keeps key between interactions)")
|
| 122 |
+
clear_key_btn = gr.Button("Clear saved key")
|
| 123 |
|
| 124 |
with gr.Row():
|
| 125 |
use_tavily = gr.Checkbox(value=True, label="Call Tavily")
|
|
|
|
| 128 |
run = gr.Button("Search Tavily")
|
| 129 |
|
| 130 |
output_json = gr.JSON(label="Raw Tavily Output (JSON)")
|
| 131 |
+
tavily_table = gr.DataFrame(headers=None, label="Search Results (table)")
|
| 132 |
|
| 133 |
+
gr.Markdown("### UDISE candidates found in search results")
|
| 134 |
+
suggestions_dropdown = gr.Dropdown(choices=[], label="UDISE candidates")
|
|
|
|
| 135 |
udise_input = gr.Textbox(label="UDISE to lookup (editable)", placeholder="Pick a candidate or type a UDISE code...", lines=1)
|
| 136 |
lookup_btn = gr.Button("Lookup UDISE (Call KYS)")
|
| 137 |
|
| 138 |
kys_output_json = gr.JSON(label="KYS Raw Output")
|
| 139 |
kys_table = gr.DataFrame(headers=None, label="KYS results (table)")
|
| 140 |
|
| 141 |
+
# A small gr.State to persist the saved key value inside the session
|
| 142 |
+
saved_key_state = gr.State("")
|
| 143 |
+
|
| 144 |
+
def on_run(school, turl, tkey, save_key, utav, ukys, saved_key):
|
| 145 |
+
# Determine effective key: if save_key and saved_key present use saved_key, else use tkey
|
| 146 |
+
effective_key = saved_key if saved_key else tkey
|
| 147 |
+
res = search_workflow(school, turl, effective_key, utav, ukys)
|
| 148 |
tav_df = pd.DataFrame()
|
| 149 |
if res.get("tavily") and res["tavily"].get("ok"):
|
| 150 |
tav_df = json_to_table(res["tavily"]["data"])
|
|
|
|
| 151 |
suggestions = res.get("suggestions", [])
|
| 152 |
+
# If user asked to save the key for session, return it into saved_key_state and keep the textbox value
|
| 153 |
+
new_saved_key = saved_key
|
| 154 |
+
textbox_value = tkey
|
| 155 |
+
if save_key:
|
| 156 |
+
new_saved_key = tkey or saved_key
|
| 157 |
+
textbox_value = new_saved_key
|
| 158 |
+
return res.get("tavily"), tav_df, suggestions, new_saved_key, textbox_value
|
| 159 |
|
| 160 |
+
# run.click outputs: tavily json, tavily table, suggestions dropdown, saved_key_state, tavily_key (textbox)
|
| 161 |
+
run.click(on_run, inputs=[inp, tavily_url, tavily_key, save_key_toggle, use_tavily, use_kys, saved_key_state], outputs=[output_json, tavily_table, suggestions_dropdown, saved_key_state, tavily_key])
|
| 162 |
|
|
|
|
| 163 |
def on_select_suggestion(choice):
|
| 164 |
return choice or ""
|
| 165 |
|
| 166 |
suggestions_dropdown.change(on_select_suggestion, inputs=[suggestions_dropdown], outputs=[udise_input])
|
| 167 |
|
|
|
|
| 168 |
def on_lookup_udise(udise_code):
|
| 169 |
if not udise_code or not udise_code.strip().isdigit():
|
| 170 |
return {"ok": False, "error": "Provide a numeric UDISE code (6-14 digits)."}, pd.DataFrame()
|
|
|
|
| 176 |
|
| 177 |
lookup_btn.click(on_lookup_udise, inputs=[udise_input], outputs=[kys_output_json, kys_table])
|
| 178 |
|
| 179 |
+
# Clear saved key handler: empties both the saved_key_state and the textbox
|
| 180 |
+
def on_clear_key(_):
|
| 181 |
+
return "", ""
|
| 182 |
+
|
| 183 |
+
clear_key_btn.click(on_clear_key, inputs=[saved_key_state], outputs=[saved_key_state, tavily_key])
|
| 184 |
+
|
| 185 |
gr.Markdown('''---
|
| 186 |
+
**Notes:**
|
| 187 |
+
- The 'Save key in session' toggle will keep the key active for the current browser session and across interactions in the Space UI.
|
| 188 |
+
- The key is stored only in-memory for the session (not written to disk) and is not logged.
|
| 189 |
+
- Use the 'Clear saved key' button to remove it from the session.''')
|
| 190 |
+
|
| 191 |
|
| 192 |
if __name__ == "__main__":
|
| 193 |
demo.launch()
|