Raymond Weitekamp commited on
Commit
99f73bb
·
1 Parent(s): f5f2ad3

ui improvements for logged in vs not

Browse files
Files changed (1) hide show
  1. app.py +153 -68
app.py CHANGED
@@ -1,11 +1,13 @@
1
  import gradio as gr
2
- import random
3
- import os
4
- from datetime import datetime
5
- from huggingface_hub import HfApi
6
- from typing import Optional
7
- from PIL import Image # Needed for working with PIL images
8
- import datasets
 
 
9
 
10
  # The list of sentences from our previous conversation.
11
  sentences = [
@@ -64,13 +66,19 @@ sentences = [
64
  class OCRDataCollector:
65
  def __init__(self):
66
  self.collected_pairs = []
67
- self.current_text_block = self.get_random_text_block()
68
  self.hf_api = HfApi()
69
 
70
- def get_random_text_block(self):
71
  block_length = random.randint(1, 5)
72
  start_index = random.randint(0, len(sentences) - block_length)
73
  block = " ".join(sentences[start_index:start_index + block_length])
 
 
 
 
 
 
74
  return block
75
 
76
  def submit_image(self, image, text_block, username: Optional[str] = None):
@@ -82,10 +90,10 @@ class OCRDataCollector:
82
  "timestamp": timestamp,
83
  "username": username
84
  })
85
- return self.get_random_text_block()
86
 
87
  def skip_text(self, text_block, username: Optional[str] = None):
88
- return self.get_random_text_block()
89
 
90
 
91
  def strip_metadata(image: Image.Image) -> Image.Image:
@@ -103,50 +111,118 @@ def create_gradio_interface():
103
  collector = OCRDataCollector()
104
 
105
  with gr.Blocks() as demo:
106
- gr.Markdown("## Crowdsourcing Handwriting OCR Dataset")
 
 
 
107
  with gr.Row():
108
- with gr.Column(scale=1, min_width=100): # Just use scale and min_width to constrain the column
109
- gr.LoginButton()
110
- with gr.Column(scale=4):
 
 
 
 
 
 
 
111
  pass
112
- user_info = gr.Markdown()
113
- profile_state = gr.JSON(visible=False)
114
 
 
115
  gr.Markdown(
 
116
  "You will be shown between 1 and 5 consecutive sentences. Please handwrite them on paper and upload an image of your handwriting. "
 
117
  "If you wish to skip the current text, click 'Skip'."
118
  )
119
 
 
120
  text_box = gr.Textbox(
121
- value=collector.current_text_block,
122
- label="Text to Handwrite",
123
- interactive=False,
124
- visible=False,
125
- lines=10, # Allow multiple lines
126
- show_copy_button=True # Optional: allows users to easily copy the text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  )
128
- image_input = gr.Image(type="pil", label="Upload Handwritten Image", sources=["upload"], visible=False)
 
 
129
 
130
- # Add checkboxes and explanation text
131
- gr.Markdown("### Select Datasets")
132
- with gr.Column():
133
- private_checkbox = gr.Checkbox(value=True, label="Private", interactive=True, visible=False)
134
- private_explanation = gr.Markdown("*Private: Creates a new dataset on your account named '/handwriting-ocr-private' and appends data there.*", visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- public_checkbox = gr.Checkbox(value=True, label="Public", interactive=True, visible=False)
137
- public_explanation = gr.Markdown("*Public: Will be added to our public dataset. By submitting, you are giving permission to be added to the dataset.*", visible=False)
 
 
 
 
 
 
 
 
138
 
139
  with gr.Row(visible=False) as button_row:
140
- submit_btn = gr.Button("Submit")
141
- skip_btn = gr.Button("Skip")
142
-
143
- def update_user_info(profile: gr.OAuthProfile | None):
 
 
 
 
 
 
 
 
 
 
 
144
  if profile is None:
145
- return "Please log in with your Hugging Face account to contribute to the dataset.", {}
146
- # Use the username provided by the profile (from the "profile" scope)
147
- return f"Logged in as: {profile.username}", {"username": profile.username}
148
-
149
- def handle_submit(profile, private_checkbox, public_checkbox, image, text):
150
  if not profile or "username" not in profile:
151
  raise gr.Error("Please log in to use this application")
152
  username = profile["username"]
@@ -199,42 +275,51 @@ def create_gradio_interface():
199
  "dataset": "private"
200
  })
201
 
202
- new_text = collector.get_random_text_block()
203
  return None, new_text
204
  elif public_checkbox:
205
  # Fallback to public submission
206
- new_text = collector.submit_image(image, text, username)
207
  return None, new_text
208
 
209
- def handle_skip(profile, text):
210
- if not profile or "username" not in profile:
211
- raise gr.Error("Please log in to use this application")
212
- return collector.skip_text(text, profile["username"])
213
-
214
- def update_visibility(profile: gr.OAuthProfile | None):
215
- is_visible = profile is not None
216
- return [
217
- gr.update(visible=is_visible), # text_box
218
- gr.update(visible=is_visible), # image_input
219
- gr.update(visible=is_visible), # button_row
220
- gr.update(visible=is_visible), # private_checkbox
221
- gr.update(visible=is_visible), # public_checkbox
222
- gr.update(visible=is_visible), # private explanation
223
- gr.update(visible=is_visible), # public explanation
224
- ]
225
 
226
  # On load, update both the display message and the hidden profile state.
227
- demo.load(update_user_info, inputs=None, outputs=[user_info, profile_state])
228
- demo.load(update_visibility, inputs=None,
229
- outputs=[text_box, image_input, button_row,
230
- private_checkbox, public_checkbox,
231
- private_explanation, public_explanation])
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
- # Bind the submit and skip actions with updated inputs.
234
- submit_btn.click(handle_submit,
235
- inputs=[profile_state, private_checkbox, public_checkbox, image_input, text_box],
236
- outputs=[image_input, text_box])
237
- skip_btn.click(handle_skip, inputs=[profile_state, text_box], outputs=text_box)
 
 
 
 
 
 
 
 
 
 
238
 
239
  return demo
240
 
 
1
  import gradio as gr
2
+ # Import statements that should only run once
3
+ if gr.NO_RELOAD:
4
+ import random
5
+ import os
6
+ from datetime import datetime
7
+ from huggingface_hub import HfApi
8
+ from typing import Optional
9
+ from PIL import Image # Needed for working with PIL images
10
+ import datasets
11
 
12
  # The list of sentences from our previous conversation.
13
  sentences = [
 
66
  class OCRDataCollector:
67
  def __init__(self):
68
  self.collected_pairs = []
69
+ self.current_text_block = self.get_random_text_block(201) # Default max words
70
  self.hf_api = HfApi()
71
 
72
+ def get_random_text_block(self, max_words: int):
73
  block_length = random.randint(1, 5)
74
  start_index = random.randint(0, len(sentences) - block_length)
75
  block = " ".join(sentences[start_index:start_index + block_length])
76
+
77
+ # Truncate to max_words if necessary
78
+ words = block.split()
79
+ if len(words) > max_words:
80
+ block = " ".join(words[:max_words])
81
+
82
  return block
83
 
84
  def submit_image(self, image, text_block, username: Optional[str] = None):
 
90
  "timestamp": timestamp,
91
  "username": username
92
  })
93
+ return self.get_random_text_block(201)
94
 
95
  def skip_text(self, text_block, username: Optional[str] = None):
96
+ return self.get_random_text_block(201)
97
 
98
 
99
  def strip_metadata(image: Image.Image) -> Image.Image:
 
111
  collector = OCRDataCollector()
112
 
113
  with gr.Blocks() as demo:
114
+ gr.Markdown("# Handwriting OCR Dataset Creator")
115
+ gr.Markdown("## After almost 100 years, handwriting recognition still sucks. Together, we can change that.")
116
+ gr.Markdown("### Step 1: Log in with your Hugging Face account to use this app.")
117
+ # Login section - centered
118
  with gr.Row():
119
+ with gr.Column(scale=1):
120
+ pass
121
+ with gr.Column(scale=2, min_width=200):
122
+ login_btn = gr.LoginButton(elem_id="login_btn")
123
+ user_info = gr.Markdown(
124
+ value="<center>Please log in with your Hugging Face account to contribute to the dataset.</center>",
125
+ elem_id="user_info"
126
+ )
127
+ profile_state = gr.JSON(visible=False, elem_id="profile_state")
128
+ with gr.Column(scale=1):
129
  pass
 
 
130
 
131
+ # Instructions (always visible)
132
  gr.Markdown(
133
+ "### Step 2: Read the text. "
134
  "You will be shown between 1 and 5 consecutive sentences. Please handwrite them on paper and upload an image of your handwriting. "
135
+ "You can change the maximum number of words you are willing to write by using the slider below. "
136
  "If you wish to skip the current text, click 'Skip'."
137
  )
138
 
139
+ # Main interface elements (initially visible)
140
  text_box = gr.Textbox(
141
+ value=collector.current_text_block,
142
+ label="Text to Handwrite",
143
+ interactive=False,
144
+ lines=10,
145
+ show_copy_button=True,
146
+ visible=True,
147
+ elem_id="text_box"
148
+ )
149
+
150
+ max_words_slider = gr.Slider(
151
+ 1, 201, step=5, value=201,
152
+ label="Maximum Number of Words",
153
+ interactive=True,
154
+ visible=True,
155
+ elem_id="max_words_slider"
156
+ )
157
+
158
+ regenerate_btn = gr.Button(
159
+ "Regenerate Text",
160
+ visible=True,
161
+ elem_id="regenerate_btn"
162
  )
163
+
164
+ # Step 3 section
165
+ gr.Markdown("### Step 3: Upload an image of your handwritten version of the text")
166
 
167
+ # Message that changes based on login state
168
+ upload_info = gr.Markdown(
169
+ value="You must be logged in to do this, to help us prevent spam submissions",
170
+ elem_id="upload_info"
171
+ )
172
+
173
+ # Image upload and related components
174
+ image_input = gr.Image(
175
+ type="pil",
176
+ label="Upload Handwritten Image",
177
+ sources=["upload"],
178
+ visible=False,
179
+ elem_id="image_input"
180
+ )
181
+
182
+ with gr.Column(visible=False) as dataset_options:
183
+ private_checkbox = gr.Checkbox(
184
+ value=True,
185
+ label="Private",
186
+ interactive=True,
187
+ elem_id="private_cb"
188
+ )
189
+ private_explanation = gr.Markdown(
190
+ "*Private: Creates a new dataset on your account named '/handwriting-ocr-private' and appends data there.*",
191
+ elem_id="private_exp"
192
+ )
193
 
194
+ public_checkbox = gr.Checkbox(
195
+ value=True,
196
+ label="Public",
197
+ interactive=True,
198
+ elem_id="public_cb"
199
+ )
200
+ public_explanation = gr.Markdown(
201
+ "*Public: Will be added to our public dataset. By submitting, you are giving permission to be added to the dataset.*",
202
+ elem_id="public_exp"
203
+ )
204
 
205
  with gr.Row(visible=False) as button_row:
206
+ submit_btn = gr.Button("Submit", elem_id="submit_btn")
207
+
208
+ def update_ui_visibility(profile: gr.OAuthProfile | None) -> dict:
209
+ """Update visibility of UI elements based on login state"""
210
+ is_logged_in = profile is not None
211
+ message = "Please upload your handwritten image of the text below." if is_logged_in else "You must be logged in to do this, to help us prevent spam submissions"
212
+
213
+ return {
214
+ upload_info: gr.update(value=message),
215
+ image_input: gr.update(visible=is_logged_in),
216
+ dataset_options: gr.update(visible=is_logged_in),
217
+ button_row: gr.update(visible=is_logged_in)
218
+ }
219
+
220
+ def update_user_info(profile: Optional[dict]) -> tuple[str, dict]:
221
  if profile is None:
222
+ return "<center>Please log in with your Hugging Face account to contribute to the dataset.</center>", {}
223
+ return f"<center>Logged in as: {profile['username']}</center>", {"username": profile["username"]}
224
+
225
+ def handle_submit(profile, private_checkbox, public_checkbox, image, text, max_words):
 
226
  if not profile or "username" not in profile:
227
  raise gr.Error("Please log in to use this application")
228
  username = profile["username"]
 
275
  "dataset": "private"
276
  })
277
 
278
+ new_text = collector.get_random_text_block(max_words)
279
  return None, new_text
280
  elif public_checkbox:
281
  # Fallback to public submission
282
+ new_text = collector.get_random_text_block(max_words)
283
  return None, new_text
284
 
285
+ def handle_regenerate(profile, text, max_words):
286
+ # Remove the login check - allow anyone to regenerate text
287
+ return collector.get_random_text_block(max_words)
 
 
 
 
 
 
 
 
 
 
 
 
 
288
 
289
  # On load, update both the display message and the hidden profile state.
290
+ demo.load(
291
+ fn=update_user_info,
292
+ inputs=None,
293
+ outputs=[user_info, profile_state]
294
+ )
295
+
296
+ # Update UI when login state changes
297
+ demo.load(
298
+ fn=update_ui_visibility,
299
+ inputs=None,
300
+ outputs=[
301
+ upload_info,
302
+ image_input,
303
+ dataset_options,
304
+ button_row
305
+ ]
306
+ )
307
 
308
+ # Bind the submit and skip actions
309
+ submit_btn.click(
310
+ fn=handle_submit,
311
+ inputs=[
312
+ profile_state, private_checkbox, public_checkbox,
313
+ image_input, text_box, max_words_slider
314
+ ],
315
+ outputs=[image_input, text_box]
316
+ )
317
+
318
+ regenerate_btn.click(
319
+ fn=handle_regenerate,
320
+ inputs=[profile_state, text_box, max_words_slider],
321
+ outputs=text_box
322
+ )
323
 
324
  return demo
325