Spaces:
Running
Running
File size: 9,316 Bytes
e2df94c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
import gradio as gr
import requests
import base64
from PIL import Image
import io
import logging
from typing import List, Dict, Optional, Tuple
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
MAX_PAIRS = 20
SERVER_URL = "https://c73b-163-1-222-182.ngrok-free.app"
def create_qa_boxes(num_pairs: int) -> Tuple[List[gr.Textbox], List[gr.Textbox]]:
"""Dynamically create question and answer textboxes"""
questions = []
answers = []
for i in range(num_pairs):
questions.append(gr.Textbox(
lines=2,
label=f"Question {i+1}",
placeholder="Enter question here...",
visible=True
))
answers.append(gr.Textbox(
lines=2,
label=f"Answer {i+1}",
placeholder="Enter answer here...",
visible=True
))
return questions, answers
def validate_password(password: str) -> Tuple[bool, str, Optional[str]]:
"""Validate the password with the server and return the API key if correct"""
try:
response = requests.post(f"{SERVER_URL}/validate", json={"password": password})
if response.status_code == 200:
data = response.json()
if data.get("valid"):
return True, "Password correct. Loading data...", data.get("api_key")
return False, "Incorrect password", None
except Exception as e:
print(f"Error validating password: {e}")
return False, "Server error during validation", None
def base64_to_image(base64_str: str) -> Image.Image:
"""Convert base64 string to PIL Image"""
image_bytes = base64.b64decode(base64_str)
return Image.open(io.BytesIO(image_bytes))
def get_next_item(api_key: str) -> Optional[Dict]:
"""Get next item from the server"""
try:
headers = {"X-API-Key": api_key} if api_key else {}
response = requests.get(f"{SERVER_URL}/get_data", headers=headers)
if response.status_code == 200:
return response.json()
return None
except Exception as e:
print(f"Error getting next item: {e}")
return None
def save_item(data: Dict, api_key: str) -> bool:
"""Save item to the server"""
try:
# Remove image_base64 if present
data_to_save = {k: v for k, v in data.items() if k != "image_base64"}
headers = {"X-API-Key": api_key} if api_key else {}
response = requests.post(f"{SERVER_URL}/save_data", json=data_to_save, headers=headers)
return response.status_code == 200
except Exception as e:
print(f"Error saving item: {e}")
return False
def update_interface(data: Optional[Dict]) -> Tuple[Optional[Image.Image], str, List[str], List[str], List[bool], List[bool]]:
"""Update interface with new data"""
if data is None:
logging.warning("update_interface: No data provided")
return None, "No data available", [], [], [], []
logging.info(f"update_interface: Received data with {len(data['questions'])} questions")
# Convert base64 to image
image = base64_to_image(data.get("image_base64")) if data.get("image_base64") else None
# Extract questions and answers
questions = [qa["question"] for qa in data["questions"]]
answers = [qa["answer"] for qa in data["questions"]]
updates = []
# First add all question updates
for i in range(MAX_PAIRS):
if i < len(questions):
logging.info(f"Making question {i+1} visible with: {questions[i]}")
updates.append(gr.update(value=questions[i], visible=True))
updates.append(gr.update(value=answers[i], visible=True))
else:
updates.append(gr.update(value="", visible=False))
updates.append(gr.update(value="", visible=False))
return image, "Data loaded successfully", updates
def login(password: str, state: Dict):
"""Handle login and load first item"""
logging.info("Attempting login")
success, message, api_key = validate_password(password)
if not success:
logging.warning(f"Login failed: {message}")
# Return empty updates for both questions and answers
empty_updates = [gr.update(value="", visible=False)] * MAX_PAIRS * 2
return [None, message, gr.update(visible=True), gr.update(visible=False), {"api_key": None, "current_data": None}] + empty_updates
logging.info("Login successful, fetching first item")
# Get first item
current_data = get_next_item(api_key)
if current_data is None:
logging.warning("No items available after login")
empty_updates = [gr.update(value="", visible=False)] * MAX_PAIRS * 2
return [None, "No items available", gr.update(visible=True), gr.update(visible=False), {"api_key": api_key, "current_data": None}] + empty_updates
# Update interface with new data
image, status, question_answers = update_interface(current_data)
logging.info("Returning login updates")
return [image, status, gr.update(visible=False), gr.update(visible=True), {"api_key": api_key, "current_data": current_data}] + question_answers
def save_and_next(*args):
"""Save current item and load next one"""
# Extract state from the last argument
qa_inputs = args[:-1]
state = args[-1]
current_data = state.get("current_data")
api_key = state.get("api_key")
logging.info("save_and_next called")
if current_data is not None and api_key is not None:
# Split inputs into questions and answers
questions = qa_inputs[::2]
answers = qa_inputs[1::2]
# Filter out hidden inputs
valid_qa_pairs = []
for q, a in zip(questions, answers):
if q is not None and q.strip() and a is not None and a.strip(): # If question exists
valid_qa_pairs.append({"question": q, "answer": a})
current_data["questions"] = valid_qa_pairs
if not save_item(current_data, api_key):
logging.error("Failed to save current item")
return [None, "Failed to save item", {"api_key": api_key, "current_data": None}] + [gr.update(value="", visible=False)] * MAX_PAIRS * 2
logging.info("Successfully saved current item, fetching next")
# Get next item
next_data = get_next_item(api_key)
if next_data is None:
logging.warning("No more items available after save")
empty_updates = [gr.update(value="", visible=False)] * MAX_PAIRS * 2
return [None, "No more items available", {"api_key": api_key, "current_data": None}] + empty_updates
image, status, updates = update_interface(next_data)
return [image, status, {"api_key": api_key, "current_data": next_data}] + updates
# Create the Gradio interface
with gr.Blocks() as app:
# Initialize session state
state = gr.State(value={"api_key": None, "current_data": None})
logging.info("Creating Gradio interface")
# Login section
with gr.Row(visible=True) as login_row:
password_input = gr.Textbox(type="password", label="Password")
login_button = gr.Button("Login")
login_message = gr.Textbox(label="Status", interactive=False)
# Main interface (hidden initially)
with gr.Row(visible=False) as main_interface:
# Left column - Image
with gr.Column(scale=1):
image_output = gr.Image(type="pil", label="Image")
# Right column - Q&A pairs and controls
with gr.Column(scale=1):
# Create maximum number of Q&A pairs (they'll be hidden/shown as needed)
questions_list = []
answers_list = []
# Create containers for Q&A pairs
# Create interleaved Q&A pairs
for i in range(MAX_PAIRS):
with gr.Group():
# Question box
questions_list.append(gr.Textbox(
lines=2,
label=f"Question {i+1}",
placeholder="Enter question here...",
visible=False
))
# Answer box immediately after its question
answers_list.append(gr.Textbox(
lines=2,
label=f"Answer {i+1}",
placeholder="Enter answer here...",
visible=False
))
save_button = gr.Button("Save and Load Next")
status_message = gr.Textbox(label="Status", interactive=False)
logging.info("Setting up event handlers")
# Set up event handlers
all_components = []
for i in range(MAX_PAIRS):
all_components.extend([questions_list[i], answers_list[i]])
login_button.click(
login,
inputs=[password_input, state],
outputs=[image_output, login_message, login_row, main_interface, state] + all_components
)
save_button.click(
save_and_next,
inputs=all_components + [state],
outputs=[image_output, status_message, state] + all_components
)
if __name__ == "__main__":
logging.info("Starting Gradio app")
app.launch()
|