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

chore: reorganise code out of main, and docstrings in input_handling

Browse files
Files changed (2) hide show
  1. src/input/input_handling.py +88 -31
  2. src/main.py +6 -18
src/input/input_handling.py CHANGED
@@ -90,7 +90,21 @@ 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 buffer_files():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  # buffer info from the file_uploader that doesn't require further user input
95
  # - the image, the hash, the filename
96
  # a separate function takes care of per-file user inputs for metadata
@@ -126,6 +140,17 @@ def buffer_files():
126
 
127
 
128
  def load_file_and_hash(file:UploadedFile) -> Tuple[np.ndarray, str]:
 
 
 
 
 
 
 
 
 
 
 
129
  # two operations that require reading the file done together for efficiency
130
  # load the file, compute the hash, return the image and hash
131
  _bytes = file.read()
@@ -137,13 +162,23 @@ def load_file_and_hash(file:UploadedFile) -> Tuple[np.ndarray, str]:
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
141
 
142
  if st.session_state.container_metadata_inputs is not None:
143
  _viewcontainer = st.session_state.container_metadata_inputs
144
  else:
145
  _viewcontainer = st.sidebar
146
- print(f"[W] `container_metadata_inputs` is None, using sidebar")
147
 
148
 
149
 
@@ -197,12 +232,18 @@ def metadata_inputs_one_file(file:UploadedFile, image_hash:str, dbg_ix:int=0) ->
197
  uploaded_file=file, image_md5=image_hash
198
  )
199
 
200
- # TODO: pass in the hash to InputObservation, so it is done once only. (need to refactor the class a bit)
201
  return observation
202
 
203
 
204
 
205
  def _setup_dynamic_inputs() -> None:
 
 
 
 
 
 
 
206
 
207
  # for each file uploaded,
208
  # - add the UI elements for the metadata
@@ -237,26 +278,14 @@ def _setup_dynamic_inputs() -> None:
237
 
238
  def _setup_oneoff_inputs() -> None:
239
  '''
240
- Add the UI input elements for which we have one each
241
 
 
 
242
  '''
243
- st.title("Input image and data")
244
 
245
- # setup containers for consistent layout order with dynamic elements
246
- #container_file_uploader = st.container(border=False, key="container_file_uploader")
247
  container_file_uploader = st.session_state.container_file_uploader
248
- # - a container for the dynamic input elements (this one matters)
249
- #if "container_per_file_input_elems" not in st.session_state:
250
- # if st.session_state.container_per_file_input_elems is None:
251
- # #st.session_state.container_per_file_input_elems = None
252
- # c = st.container(border=True, key="container_per_file_input_elems")
253
- # with c:
254
- # st.write("No files uploaded yet.")
255
- # print(f"[D] initialised the container..... {id(c)} | {c=}")
256
- # st.session_state.container_per_file_input_elems = c
257
- # else:
258
- # print(f"[D] already present, don't redo... {id(st.session_state.container_per_file_input_elems)} | {st.session_state.container_per_file_input_elems=}")
259
-
260
 
261
  with container_file_uploader:
262
  # 1. Input the author email
@@ -266,18 +295,12 @@ def _setup_oneoff_inputs() -> None:
266
  st.error("Please enter a valid email address.")
267
 
268
  # 2. Image Selector
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
 
275
- uploaded_files = st.session_state.file_uploader_data
276
-
277
- for ix, file in enumerate(uploaded_files):
278
- print(f"[DD] rechecking file {file.name}. {file.file_id} {file.type} {file.size}")
279
- pass
280
-
281
 
282
 
283
 
@@ -302,4 +325,38 @@ def setup_input() -> None:
302
  # setup dynamic UI input elements, based on the data that is buffered in session_state
303
  _setup_dynamic_inputs()
304
 
305
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  return all([v is not None for v in vals])
91
 
92
 
93
+ def buffer_uploaded_files():
94
+ """
95
+ Buffers uploaded files to session_state (images, image_hashes, filenames).
96
+
97
+ Buffers uploaded files by extracting and storing filenames, images, and
98
+ image hashes in the session state.
99
+
100
+ Adds the following keys to `st.session_state`:
101
+ - `images`: dict mapping image hashes to image data (numpy arrays)
102
+ - `files`: list of uploaded files
103
+ - `image_hashes`: list of image hashes
104
+ - `image_filenames`: list of filenames
105
+ """
106
+
107
+
108
  # buffer info from the file_uploader that doesn't require further user input
109
  # - the image, the hash, the filename
110
  # a separate function takes care of per-file user inputs for metadata
 
140
 
141
 
142
  def load_file_and_hash(file:UploadedFile) -> Tuple[np.ndarray, str]:
143
+ """
144
+ Loads an image file and computes its MD5 hash.
145
+
146
+ Since both operations require reading the full file contentsV, they are done
147
+ together for efficiency.
148
+
149
+ Args:
150
+ file (UploadedFile): The uploaded file to be processed.
151
+ Returns:
152
+ Tuple[np.ndarray, str]: A tuple containing the decoded image as a NumPy array and the MD5 hash of the file's contents.
153
+ """
154
  # two operations that require reading the file done together for efficiency
155
  # load the file, compute the hash, return the image and hash
156
  _bytes = file.read()
 
162
 
163
 
164
  def metadata_inputs_one_file(file:UploadedFile, image_hash:str, dbg_ix:int=0) -> InputObservation:
165
+ """
166
+ Creates and parses metadata inputs for a single file
167
+
168
+ Args:
169
+ file (UploadedFile): The uploaded file for which metadata is being handled.
170
+ image_hash (str): The hash of the image.
171
+ dbg_ix (int, optional): Debug index to differentiate data in each input group. Defaults to 0.
172
+ Returns:
173
+ InputObservation: An object containing the metadata and other information for the input file.
174
+ """
175
  # dbg_ix is a hack to have different data in each input group, checking persistence
176
 
177
  if st.session_state.container_metadata_inputs is not None:
178
  _viewcontainer = st.session_state.container_metadata_inputs
179
  else:
180
  _viewcontainer = st.sidebar
181
+ m_logger.warning(f"[W] `container_metadata_inputs` is None, using sidebar")
182
 
183
 
184
 
 
232
  uploaded_file=file, image_md5=image_hash
233
  )
234
 
 
235
  return observation
236
 
237
 
238
 
239
  def _setup_dynamic_inputs() -> None:
240
+ """
241
+ Setup metadata inputs dynamically for each uploaded file, and process.
242
+
243
+ This operates on the data buffered in the session state, and writes
244
+ the observation objects back to the session state.
245
+
246
+ """
247
 
248
  # for each file uploaded,
249
  # - add the UI elements for the metadata
 
278
 
279
  def _setup_oneoff_inputs() -> None:
280
  '''
281
+ Add the UI input elements for which we have one covering all files
282
 
283
+ - author email
284
+ - file uploader (accepts multiple files)
285
  '''
 
286
 
287
+ # fetch the container for the file uploader input elements
 
288
  container_file_uploader = st.session_state.container_file_uploader
 
 
 
 
 
 
 
 
 
 
 
 
289
 
290
  with container_file_uploader:
291
  # 1. Input the author email
 
295
  st.error("Please enter a valid email address.")
296
 
297
  # 2. Image Selector
298
+ st.file_uploader(
299
+ "Upload one or more images", type=["png", 'jpg', 'jpeg', 'webp'],
300
+ accept_multiple_files=True,
301
+ key="file_uploader_data", on_change=buffer_uploaded_files)
 
302
 
303
+
 
 
 
 
 
304
 
305
 
306
 
 
325
  # setup dynamic UI input elements, based on the data that is buffered in session_state
326
  _setup_dynamic_inputs()
327
 
328
+
329
+ def init_input_container_states() -> None:
330
+ '''
331
+ Initialise the layout containers used in the input handling
332
+ '''
333
+ #if "container_per_file_input_elems" not in st.session_state:
334
+ # st.session_state.container_per_file_input_elems = None
335
+
336
+ if "container_file_uploader" not in st.session_state:
337
+ st.session_state.container_file_uploader = None
338
+
339
+ if "container_metadata_inputs" not in st.session_state:
340
+ st.session_state.container_metadata_inputs = None
341
+
342
+
343
+ def add_input_UI_elements() -> None:
344
+ '''
345
+ Create the containers within which user input elements will be placed
346
+ '''
347
+ # we make containers ahead of time, allowing consistent order of elements
348
+ # which are not created in the same order.
349
+
350
+ st.divider()
351
+ st.title("Input image and data")
352
+
353
+ # create and style a container for the file uploader/other one-off inputs
354
+ st.markdown('<style>.st-key-container_file_uploader_id { border: 1px solid skyblue; border-radius: 5px; }</style>', unsafe_allow_html=True)
355
+ container_file_uploader = st.container(border=True, key="container_file_uploader_id")
356
+ st.session_state.container_file_uploader = container_file_uploader
357
+
358
+ # create and style a container for the dynamic metadata inputs
359
+ st.markdown('<style>.st-key-container_metadata_inputs_id { border: 1px solid lightgreen; border-radius: 5px; }</style>', unsafe_allow_html=True)
360
+ container_metadata_inputs = st.container(border=True, key="container_metadata_inputs_id")
361
+ container_metadata_inputs.write("Metadata Inputs... wait for file upload ")
362
+ st.session_state.container_metadata_inputs = container_metadata_inputs
src/main.py CHANGED
@@ -17,6 +17,8 @@ disable_caching()
17
  import whale_gallery as gallery
18
  import whale_viewer as viewer
19
  from input.input_handling import setup_input, check_inputs_are_set
 
 
20
  from maps.alps_map import present_alps_map
21
  from maps.obs_map import present_obs_map
22
  from utils.st_logs import setup_logging, parse_log_buffer
@@ -82,14 +84,7 @@ if "workflow_fsm" not in st.session_state:
82
  # create and init the state machine
83
  st.session_state.workflow_fsm = WorkflowFSM(FSM_STATES)
84
 
85
- if "container_per_file_input_elems" not in st.session_state:
86
- st.session_state.container_per_file_input_elems = None
87
-
88
- if "container_file_uploader" not in st.session_state:
89
- st.session_state.container_file_uploader = None
90
-
91
- if "container_metadata_inputs" not in st.session_state:
92
- st.session_state.container_metadata_inputs = None
93
 
94
  def refresh_progress():
95
  with st.sidebar:
@@ -157,16 +152,9 @@ def main() -> None:
157
 
158
  # create a sidebar, and parse all the input (returned as `observations` object)
159
  with st.sidebar:
160
- st.divider()
161
-
162
- st.markdown('<style>.st-key-container_file_uploader_id { border: 1px solid skyblue; border-radius: 5px; }</style>', unsafe_allow_html=True)
163
- container_file_uploader = st.container(border=True, key="container_file_uploader_id")
164
- st.session_state.container_file_uploader = container_file_uploader
165
- st.markdown('<style>.st-key-container_metadata_inputs_id { border: 1px solid lightgreen; border-radius: 5px; }</style>', unsafe_allow_html=True)
166
- container_metadata_inputs = st.container(border=True, key="container_metadata_inputs_id")
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
 
 
17
  import whale_gallery as gallery
18
  import whale_viewer as viewer
19
  from input.input_handling import setup_input, check_inputs_are_set
20
+ from input.input_handling import init_input_container_states, add_input_UI_elements
21
+
22
  from maps.alps_map import present_alps_map
23
  from maps.obs_map import present_obs_map
24
  from utils.st_logs import setup_logging, parse_log_buffer
 
84
  # create and init the state machine
85
  st.session_state.workflow_fsm = WorkflowFSM(FSM_STATES)
86
 
87
+ init_input_container_states()
 
 
 
 
 
 
 
88
 
89
  def refresh_progress():
90
  with st.sidebar:
 
152
 
153
  # create a sidebar, and parse all the input (returned as `observations` object)
154
  with st.sidebar:
155
+ # layout handling
156
+ add_input_UI_elements()
157
+ # input elements (file upload, text input, etc)
 
 
 
 
 
 
 
158
  setup_input()
159
 
160