1212import boto3 # type: ignore
1313import dash
1414import logging_config
15+ import numpy as np
1516import pandas as pd
1617from botocore .exceptions import ClientError # type: ignore
1718from dash import Input , Output , State , ctx
@@ -198,15 +199,22 @@ def update_sequence_on_dropdown_change(selected_sequence_id):
198199 Output ("image-slider" , "min" ),
199200 Output ("slider-container" , "style" ),
200201 ],
201- [Input ("image-slider" , "value" ), Input ("sequence_on_display" , "data" )],
202+ [
203+ Input ("image-slider" , "value" ),
204+ Input ("sequence_on_display" , "data" ),
205+ Input ("detection_fetch_desc" , "value" ),
206+ ],
202207 [
203208 State ("sequence-list-container" , "children" ),
204209 State ("language" , "data" ),
205- State ("alert-end-date-value" , "children" ),
206210 ],
207211 prevent_initial_call = True ,
208212)
209- def update_image_and_bbox (slider_value , sequence_on_display , sequence_list , lang , alert_end_value ):
213+ def update_image_and_bbox (slider_value , sequence_on_display , detection_fetch_desc , sequence_list , lang ):
214+ from io import StringIO
215+
216+ import pandas as pd
217+
210218 no_alert_image_src = "./assets/images/no-alert-default.png"
211219 if lang == "es" :
212220 no_alert_image_src = "./assets/images/no-alert-default-es.png"
@@ -216,8 +224,15 @@ def update_image_and_bbox(slider_value, sequence_on_display, sequence_list, lang
216224 if sequence_on_display .empty or not len (sequence_list ):
217225 return no_alert_image_src , * [{"display" : "none" }] * 3 , 0 , {}, 0 , {"display" : "none" }
218226
219- images , boxes = zip (
220- * ((alert ["url" ], alert ["processed_bboxes" ]) for _ , alert in sequence_on_display .iterrows () if alert ["url" ]),
227+ if not detection_fetch_desc :
228+ sequence_on_display = sequence_on_display [::- 1 ].reset_index (drop = True )
229+
230+ images , boxes , created_at_local_list = zip (
231+ * (
232+ (alert ["url" ], alert ["processed_bboxes" ], alert .get ("created_at_local" ))
233+ for _ , alert in sequence_on_display .iterrows ()
234+ if alert ["url" ]
235+ ),
221236 strict = False ,
222237 )
223238
@@ -244,14 +259,15 @@ def update_image_and_bbox(slider_value, sequence_on_display, sequence_list, lang
244259 }
245260
246261 try :
247- if isinstance (alert_end_value , str ) and alert_end_value .strip ():
248- last_time = datetime .strptime (alert_end_value .strip (), "%H:%M" )
249- else :
250- raise ValueError ("Empty or invalid date string" )
262+ latest_time = pd .to_datetime (sequence_on_display ["created_at_local" ].dropna ().max ())
251263 except Exception :
252- last_time = datetime .now ()
264+ latest_time = datetime .now ()
265+
266+ # Compute 5 evenly spaced tick positions
267+ num_marks = 5
268+ tick_indices = sorted (set (int (round (i )) for i in np .linspace (0 , n_images - 1 , num = num_marks )))
253269
254- marks = {i : (last_time - timedelta (seconds = 30 * (n_images - 1 - i ))).strftime ("%H:%M:%S" ) for i in range ( n_images ) }
270+ marks = {i : (latest_time - timedelta (seconds = 30 * (n_images - 1 - i ))).strftime ("%H:%M:%S" ) for i in tick_indices }
255271
256272 return [img_src , * bbox_styles , n_images - 1 , marks , 0 , {"display" : "block" }]
257273
@@ -541,25 +557,25 @@ def update_fire_markers(smoke_location_str, api_sequences):
541557 Input ("confirm-non-wildfire" , "n_clicks" ),
542558 Input ("cancel-confirmation" , "n_clicks" ),
543559 ],
544- [State ("sequence_id_on_display" , "data" ), State ("user_token" , "data" )],
560+ [
561+ State ("sequence_id_on_display" , "data" ),
562+ State ("user_token" , "data" ),
563+ ],
545564 prevent_initial_call = True ,
546565)
547566def acknowledge_event (
548567 acknowledge_clicks , confirm_wildfire , confirm_non_wildfire , cancel , sequence_id_on_display , user_token
549568):
550569 ctx = dash .callback_context
551570
552- logger .info ("acknowledge_event" )
553-
554571 if not ctx .triggered :
555- raise dash . exceptions . PreventUpdate
572+ raise PreventUpdate
556573
557574 if user_token is None :
558- return dash . no_update
575+ raise PreventUpdate
559576
560577 triggered_id = ctx .triggered [0 ]["prop_id" ].split ("." )[0 ]
561578
562- # Modal styles
563579 modal_visible_style = {
564580 "position" : "fixed" ,
565581 "top" : "50%" ,
@@ -571,29 +587,31 @@ def acknowledge_event(
571587 modal_hidden_style = {"display" : "none" }
572588
573589 client = get_client (user_token )
590+ client .token = user_token
574591
575592 if triggered_id == "acknowledge-button" :
576- # Show the modal
577- if acknowledge_clicks > 0 :
578- return modal_visible_style , dash .no_update
593+ if acknowledge_clicks is None or acknowledge_clicks == 0 :
594+ raise PreventUpdate
595+ return modal_visible_style , dash .no_update
579596
580597 elif triggered_id == "confirm-wildfire" :
581- # Send wildfire confirmation to the API
582- client . token = user_token
598+ if confirm_wildfire is None or confirm_wildfire == 0 :
599+ raise PreventUpdate
583600 client .label_sequence (sequence_id_on_display , True )
584601 return modal_hidden_style , sequence_id_on_display
585602
586603 elif triggered_id == "confirm-non-wildfire" :
587- # Send non-wildfire confirmation to the API
588- client . token = user_token
604+ if confirm_non_wildfire is None or confirm_non_wildfire == 0 :
605+ raise PreventUpdate
589606 client .label_sequence (sequence_id_on_display , False )
590607 return modal_hidden_style , sequence_id_on_display
591608
592609 elif triggered_id == "cancel-confirmation" :
593- # Cancel action
610+ if cancel is None or cancel == 0 :
611+ raise PreventUpdate
594612 return modal_hidden_style , dash .no_update
595613
596- raise dash . exceptions . PreventUpdate
614+ raise PreventUpdate
597615
598616
599617# Modal issue let's add this later
@@ -740,12 +758,15 @@ def update_datepicker(open_clicks, selected_date):
740758def pick_live_stream_camera (n_clicks , azimuth , camera_label , azimuth_label ):
741759 logger .info ("pick_live_stream_camera" )
742760
761+ if n_clicks is None or n_clicks == 0 :
762+ raise PreventUpdate
763+
743764 if not camera_label or not azimuth_label :
744765 raise PreventUpdate
766+
745767 try :
746768 cam_name , _ , _ = camera_label .split (" " )
747769 azimuth = int (azimuth .replace ("°" , "" ))
748- # detection_azimuth = int(azimuth_label.replace("°", "").strip()) Need azimuth refine first
749770 except Exception as e :
750771 logger .warning (f"[pick_live_stream_camera] Failed to parse camera info: { e } " )
751772 raise PreventUpdate
@@ -1032,7 +1053,7 @@ def hide_button_callback(sub_api_sequences, sequence_id, event_id_table_json, se
10321053 if df_sequences .empty :
10331054 return hide_style , hide_style , hide_style
10341055 except Exception as e :
1035- print (f"[hide_button_callback] Failed to read sub_api_sequences: { e } " )
1056+ logger . error (f"[hide_button_callback] Failed to read sub_api_sequences: { e } " )
10361057 return hide_style , hide_style , hide_style
10371058
10381059 # Default: show stream & mask buttons
@@ -1054,6 +1075,6 @@ def hide_button_callback(sub_api_sequences, sequence_id, event_id_table_json, se
10541075 if isinstance (sequences , list ) and len (sequences ) > 1 :
10551076 unmatch_style = show_style
10561077 except Exception as e :
1057- print (f"[hide_button_callback] Failed to process event_id_table: { e } " )
1078+ logger . error (f"[hide_button_callback] Failed to process event_id_table: { e } " )
10581079
10591080 return stream_style , mask_style , unmatch_style
0 commit comments