rmm commited on
Commit
a491624
·
1 Parent(s): d437f49

chore: cleanup input_handling - removing unused functions

Browse files

- also stopped passing the viewcontainer arg to setup_input, since it
uses the `with st.sidebar` context, which is simpler.

Files changed (2) hide show
  1. src/input/input_handling.py +15 -220
  2. src/main.py +3 -2
src/input/input_handling.py CHANGED
@@ -90,84 +90,6 @@ def check_inputs_are_set(empty_ok:bool=False, debug:bool=False) -> bool:
90
  return all([v is not None for v in vals])
91
 
92
 
93
- def process_one_file(file:UploadedFile, ix:int=0) -> Tuple[np.ndarray, str, str, InputObservation]:
94
- # do all the non-UI calcs
95
- # add the UI elements
96
- # and in-line, do processing/validation of the inputs
97
- # - how to deal with the gathered data? a) push into session state, b) return all the elements needed?
98
-
99
- #viewcontainer = st.sidebarif st.session_state.container_per_file_input_elems is None:
100
- if st.session_state.container_metadata_inputs is not None:
101
- viewcontainer = st.session_state.container_metadata_inputs
102
- else:
103
- viewcontainer = st.sidebar
104
- msg = f"[W] `container_metadata_inputs` is None, using sidebar"
105
- m_logger.warning(msg) ; print(msg)
106
-
107
-
108
- # do all the non-UI calcs first
109
- ## get the bytes first, then convert into 1) image, 2) md5
110
- _bytes = file.read()
111
- image_hash = hashlib.md5(_bytes).hexdigest()
112
- #file_bytes = np.asarray(bytearray(_bytes), dtype=np.uint8)
113
- image: np.ndarray = cv2.imdecode(np.asarray(bytearray(_bytes), dtype=np.uint8), 1)
114
- filename:str = file.name
115
- image_datetime = get_image_datetime(file)
116
- m_logger.debug(f"image date extracted as {image_datetime} (from {file})")
117
-
118
- author_email = st.session_state["input_author_email"]
119
-
120
-
121
- # add the UI elements
122
- viewcontainer.title(f"Metadata for {filename}")
123
- ukey = image_hash
124
-
125
- # 3. Latitude Entry Box
126
- latitude = viewcontainer.text_input(
127
- "Latitude for " + filename,
128
- spoof_metadata.get('latitude', 0) + ix,
129
- key=f"input_latitude_{ukey}")
130
- if latitude and not is_valid_number(latitude):
131
- viewcontainer.error("Please enter a valid latitude (numerical only).")
132
- m_logger.error(f"Invalid latitude entered: {latitude}.")
133
- # 4. Longitude Entry Box
134
- longitude = viewcontainer.text_input(
135
- "Longitude for " + filename,
136
- spoof_metadata.get('longitude', ""),
137
- key=f"input_longitude_{ukey}")
138
- if longitude and not is_valid_number(longitude):
139
- viewcontainer.error("Please enter a valid longitude (numerical only).")
140
- m_logger.error(f"Invalid latitude entered: {latitude}.")
141
-
142
- # 5. Date/time
143
- ## first from image metadata
144
- if image_datetime is not None:
145
- time_value = datetime.datetime.strptime(image_datetime, '%Y:%m:%d %H:%M:%S').time()
146
- date_value = datetime.datetime.strptime(image_datetime, '%Y:%m:%d %H:%M:%S').date()
147
- else:
148
- time_value = datetime.datetime.now().time() # Default to current time
149
- date_value = datetime.datetime.now().date()
150
-
151
- ## if not, give user the option to enter manually
152
- date_option = viewcontainer.date_input("Date for "+filename, value=date_value, key=f"input_date_{ukey}")
153
- time_option = viewcontainer.time_input("Time for "+filename, time_value, key=f"input_time_{ukey}")
154
-
155
- observation = InputObservation(image=file, latitude=latitude, longitude=longitude,
156
- author_email=author_email, date=image_datetime, time=None,
157
- date_option=date_option, time_option=time_option,
158
- uploaded_file=file,
159
- )
160
-
161
- #the_data = [] \
162
- # + [image, file, image_hash, filename, ] \
163
- # + [latitude, longitude, date_option, time_option]
164
- # TODO: pass in the hash to InputObservation, so it is done once only. (need to refactor the class a bit)
165
-
166
- the_data = (image, image_hash, filename, observation)
167
-
168
- return the_data
169
-
170
-
171
  def buffer_files():
172
  # buffer info from the file_uploader that doesn't require further user input
173
  # - the image, the hash, the filename
@@ -213,40 +135,6 @@ def load_file_and_hash(file:UploadedFile) -> Tuple[np.ndarray, str]:
213
  return (image, image_hash)
214
 
215
 
216
-
217
- def process_files():
218
- # this is triggered whenever the uploaded files are changed.
219
-
220
- # process one file: add UI elements, and process the inputs
221
- # generate an observation from the return info
222
- # finally, put all the relevant stuff into the session state
223
- # - note: here we overwrite the session state, we aren't extending it.
224
-
225
- # get files from state
226
- uploaded_files = st.session_state.file_uploader_data
227
-
228
- observations = {}
229
- images = {}
230
- image_hashes = []
231
- filenames = []
232
-
233
- for ix, file in enumerate(uploaded_files):
234
- print(f"[D] processing file {file.name}. {file.file_id} {file.type} {file.size}")
235
- (image, image_hash, filename, observation) = process_one_file(file, ix)
236
- # big old debug because of pain.
237
-
238
- filenames.append(filename)
239
- image_hashes.append(image_hash)
240
-
241
- observations[image_hash] = observation
242
- images[image_hash] = image
243
-
244
- st.session_state.images = images
245
- st.session_state.files = uploaded_files
246
- st.session_state.observations = observations
247
- st.session_state.image_hashes = image_hashes
248
- st.session_state.image_filenames = filenames
249
-
250
 
251
  def metadata_inputs_one_file(file:UploadedFile, image_hash:str, dbg_ix:int=0) -> InputObservation:
252
  # dbg_ix is a hack to have different data in each input group, checking persistence
@@ -334,11 +222,14 @@ def _setup_dynamic_inputs() -> None:
334
  old_obs = st.session_state.observations.get(hash, None)
335
  if old_obs is not None:
336
  if old_obs == observation:
 
337
  observations[hash] = old_obs
338
  else:
 
339
  observations[hash] = observation
340
  observation.show_diff(old_obs)
341
  else:
 
342
  observations[hash] = observation
343
 
344
  st.session_state.observations = observations
@@ -378,7 +269,6 @@ def _setup_oneoff_inputs() -> None:
378
  st.file_uploader("Upload one or more images", type=["png", 'jpg', 'jpeg', 'webp'],
379
  accept_multiple_files=True,
380
  key="file_uploader_data",
381
- #on_change=process_files)
382
  on_change=buffer_files)
383
  if 1:
384
 
@@ -393,118 +283,23 @@ def _setup_oneoff_inputs() -> None:
393
 
394
 
395
 
396
- def setup_input(
397
- viewcontainer: DeltaGenerator=None,
398
- _allowed_image_types: list=None, ) -> None:
399
  '''
400
- Set up the input handling for the whale observation guidance tool
 
 
 
 
 
 
 
 
401
 
402
  '''
 
403
  _setup_oneoff_inputs()
404
- # amazingly we just have to add the uploader and its callback, and the rest is dynamic.
405
- # or not... the situation is more complex :(
406
 
407
  # setup dynamic UI input elements, based on the data that is buffered in session_state
408
  _setup_dynamic_inputs()
409
 
410
-
411
-
412
- def setup_input_monolithic(
413
- viewcontainer: DeltaGenerator=None,
414
- _allowed_image_types: list=None, ) -> InputObservation:
415
- """
416
- Sets up the input interface for uploading an image and entering metadata.
417
-
418
- It provides input fields for an image upload, lat/lon, author email, and date-time.
419
- In the ideal case, the image metadata will be used to populate location and datetime.
420
-
421
- Parameters:
422
- viewcontainer (DeltaGenerator, optional): The Streamlit container to use for the input interface. Defaults to st.sidebar.
423
- _allowed_image_types (list, optional): List of allowed image file types for upload. Defaults to allowed_image_types.
424
-
425
- Returns:
426
- InputObservation: An object containing the uploaded image and entered metadata.
427
-
428
- """
429
-
430
- if viewcontainer is None:
431
- viewcontainer = st.sidebar
432
-
433
- if _allowed_image_types is None:
434
- _allowed_image_types = allowed_image_types
435
-
436
-
437
- viewcontainer.title("Input image and data")
438
-
439
- # 1. Input the author email
440
- author_email = viewcontainer.text_input("Author Email", spoof_metadata.get('author_email', ""))
441
- if author_email and not is_valid_email(author_email):
442
- viewcontainer.error("Please enter a valid email address.")
443
-
444
- # 2. Image Selector
445
- uploaded_files = viewcontainer.file_uploader("Upload an image", type=allowed_image_types, accept_multiple_files=True)
446
- observations = {}
447
- images = {}
448
- image_hashes = []
449
- filenames = []
450
- if uploaded_files is not None:
451
- for file in uploaded_files:
452
-
453
- viewcontainer.title(f"Metadata for {file.name}")
454
-
455
- # Display the uploaded image
456
- # load image using cv2 format, so it is compatible with the ML models
457
- file_bytes = np.asarray(bytearray(file.read()), dtype=np.uint8)
458
- filename = file.name
459
- filenames.append(filename)
460
- image = cv2.imdecode(file_bytes, 1)
461
- # Extract and display image date-time
462
- image_datetime = None # For storing date-time from image
463
- image_datetime = get_image_datetime(file)
464
- m_logger.debug(f"image date extracted as {image_datetime} (from {uploaded_files})")
465
-
466
-
467
- # 3. Latitude Entry Box
468
- latitude = viewcontainer.text_input(
469
- "Latitude for "+filename,
470
- spoof_metadata.get('latitude', ""),
471
- key=f"input_latitude_{filename}")
472
- if latitude and not is_valid_number(latitude):
473
- viewcontainer.error("Please enter a valid latitude (numerical only).")
474
- m_logger.error(f"Invalid latitude entered: {latitude}.")
475
- # 4. Longitude Entry Box
476
- longitude = viewcontainer.text_input(
477
- "Longitude for "+filename,
478
- spoof_metadata.get('longitude', ""),
479
- key=f"input_longitude_{filename}")
480
- if longitude and not is_valid_number(longitude):
481
- viewcontainer.error("Please enter a valid longitude (numerical only).")
482
- m_logger.error(f"Invalid latitude entered: {latitude}.")
483
- # 5. Date/time
484
- ## first from image metadata
485
- if image_datetime is not None:
486
- time_value = datetime.datetime.strptime(image_datetime, '%Y:%m:%d %H:%M:%S').time()
487
- date_value = datetime.datetime.strptime(image_datetime, '%Y:%m:%d %H:%M:%S').date()
488
- else:
489
- time_value = datetime.datetime.now().time() # Default to current time
490
- date_value = datetime.datetime.now().date()
491
-
492
- ## if not, give user the option to enter manually
493
- date_option = st.sidebar.date_input("Date for "+filename, value=date_value)
494
- time_option = st.sidebar.time_input("Time for "+filename, time_value)
495
-
496
- observation = InputObservation(image=file, latitude=latitude, longitude=longitude,
497
- author_email=author_email, date=image_datetime, time=None,
498
- date_option=date_option, time_option=time_option)
499
- image_hash = observation.to_dict()["image_md5"]
500
- observations[image_hash] = observation
501
- images[image_hash] = image
502
- image_hashes.append(image_hash)
503
-
504
- st.session_state.images = images
505
- st.session_state.files = uploaded_files
506
- st.session_state.observations = observations
507
- st.session_state.image_hashes = image_hashes
508
- st.session_state.image_filenames = filenames
509
-
510
-
 
90
  return all([v is not None for v in vals])
91
 
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  def buffer_files():
94
  # buffer info from the file_uploader that doesn't require further user input
95
  # - the image, the hash, the filename
 
135
  return (image, image_hash)
136
 
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  def metadata_inputs_one_file(file:UploadedFile, image_hash:str, dbg_ix:int=0) -> InputObservation:
140
  # dbg_ix is a hack to have different data in each input group, checking persistence
 
222
  old_obs = st.session_state.observations.get(hash, None)
223
  if old_obs is not None:
224
  if old_obs == observation:
225
+ m_logger.debug(f"[D] {ix}th observation is the same as before. retaining")
226
  observations[hash] = old_obs
227
  else:
228
+ m_logger.debug(f"[D] {ix}th observation is different from before. updating")
229
  observations[hash] = observation
230
  observation.show_diff(old_obs)
231
  else:
232
+ m_logger.debug(f"[D] {ix}th observation is new (image_hash not seen before). Storing")
233
  observations[hash] = observation
234
 
235
  st.session_state.observations = observations
 
269
  st.file_uploader("Upload one or more images", type=["png", 'jpg', 'jpeg', 'webp'],
270
  accept_multiple_files=True,
271
  key="file_uploader_data",
 
272
  on_change=buffer_files)
273
  if 1:
274
 
 
283
 
284
 
285
 
286
+ def setup_input() -> None:
 
 
287
  '''
288
+ Set up the user input handling (files and metadata)
289
+
290
+ It provides input fields for an image upload, and author email.
291
+ Then for each uploaded image,
292
+ - it provides input fields for lat/lon, date-time.
293
+ - In the ideal case, the image metadata will be used to populate location and datetime.
294
+
295
+ Data is stored in the Streamlit session state for downstream processing,
296
+ nothing is returned
297
 
298
  '''
299
+ # configure the author email and file_uploader (with callback to buffer files)
300
  _setup_oneoff_inputs()
 
 
301
 
302
  # setup dynamic UI input elements, based on the data that is buffered in session_state
303
  _setup_dynamic_inputs()
304
 
305
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/main.py CHANGED
@@ -113,8 +113,9 @@ def dbg_show_obs_hashes():
113
  for hash in st.session_state.observations.keys():
114
  obs = st.session_state.observations[hash]
115
  s += f"- [D] observation {hash} ({obs._inst_id}) has {len(obs.top_predictions)} predictions\n"
 
116
 
117
- #st.markdown(f"- [D] observation {hash} has {len(st.session_state.observations[hash].top_predictions)} predictions")
118
 
119
  st.markdown(s)
120
 
@@ -166,7 +167,7 @@ def main() -> None:
166
  container_metadata_inputs.write("Metadata Inputs... wait for file upload ")
167
  st.session_state.container_metadata_inputs = container_metadata_inputs
168
 
169
- setup_input(viewcontainer=st.sidebar)
170
 
171
 
172
  if 0:## WIP
 
113
  for hash in st.session_state.observations.keys():
114
  obs = st.session_state.observations[hash]
115
  s += f"- [D] observation {hash} ({obs._inst_id}) has {len(obs.top_predictions)} predictions\n"
116
+ #s += f" - {repr(obs)}\n" # check the str / repr method
117
 
118
+ #print(obs)
119
 
120
  st.markdown(s)
121
 
 
167
  container_metadata_inputs.write("Metadata Inputs... wait for file upload ")
168
  st.session_state.container_metadata_inputs = container_metadata_inputs
169
 
170
+ setup_input()
171
 
172
 
173
  if 0:## WIP