ragavsachdeva commited on
Commit
e2df94c
·
verified ·
1 Parent(s): 7b4afdf

Create app.py

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