@@ -73,34 +73,31 @@ def _render_buttons(node_id: NodeID, service: TrackedServiceModel) -> None:
7373 on_click = lambda : ui .navigate .to (f"/service/{ node_id } :details" ),
7474 )
7575
76- def _render_progress () -> None :
77- ui .spinner (size = "lg" )
78-
79- storage_key = f"removing-{ node_id } "
80- if app .storage .general .get (storage_key , None ):
81- # removal is in progress just render progress bar
82- _render_progress ()
76+ if service .current_state != SchedulerServiceState .RUNNING :
8377 return
8478
85- if service .current_state == SchedulerServiceState .RUNNING :
86- with ui .row (align_items = "baseline" ).classes ("p-0 m-0" ) as container :
87-
88- async def async_task ():
89- container .clear ()
79+ async def stop_process_task ():
80+ ui .notify (f"Started service stop request for { node_id } " )
9081
91- _render_progress ()
92- app .storage .general [storage_key ] = True
82+ await httpx .AsyncClient (timeout = 10 ).get (
83+ f"http://localhost:{ DEFAULT_FASTAPI_PORT } /service/{ node_id } :stop"
84+ )
9385
94- ui .notify (f"Started service stop request for { node_id } " )
86+ ui .notify (
87+ f"Stop request accepted by { node_id } . Please give the service some time to stop!"
88+ )
9589
96- await httpx .AsyncClient (timeout = 10 ).get (
97- f"http://localhost:{ DEFAULT_FASTAPI_PORT } /service/{ node_id } :stop"
98- )
90+ with ui .dialog () as confirm_dialog , ui .card ():
91+ ui .label (f"Are you sure you want to stop the service { node_id } ?" )
92+ ui .label ("The service will also result sopped for the user in his project." )
93+ with ui .row ():
94+ ui .button ("Yes" , on_click = stop_process_task )
95+ ui .button ("No" , on_click = lambda : confirm_dialog .submit ("No" ))
9996
100- app . storage . general . pop ( "removing-{node_id}" , None )
101- ui . notify ( f"Finished service stop request for { node_id } " )
97+ async def display_confirm_dialog ():
98+ await confirm_dialog
10299
103- ui .button ("Stop service" , icon = "stop" , on_click = async_task )
100+ ui .button ("Stop service" , icon = "stop" , on_click = display_confirm_dialog )
104101
105102
106103def _render_card (
@@ -112,20 +109,41 @@ def _render_card(
112109 _render_buttons (node_id , service )
113110
114111
112+ def _get_stable_hash (model : TrackedServiceModel ) -> dict :
113+ data = model .model_dump (mode = "json" )
114+ data .pop ("last_state_change" )
115+ data .pop ("check_status_after" )
116+ data .pop ("last_status_notification" )
117+ return data
118+
119+
120+ def _get_hash (items : list [tuple [NodeID , TrackedServiceModel ]]) -> int :
121+ return hash (
122+ json .dumps ([(f"{ key } " , _get_stable_hash (model )) for key , model in items ])
123+ )
124+
125+
115126class CardUpdater :
116127 def __init__ (self , parent_app : FastAPI , container : Element ) -> None :
117128 self .parent_app = parent_app
118129 self .container = container
130+ self .last_hash : int = _get_hash ([])
119131
120132 async def update (self ) -> None :
121- # TODO: rerender only if data changed
133+ tracked_services = await get_all_tracked_services (self .parent_app )
134+ tracked_items : list [tuple [NodeID , TrackedServiceModel ]] = sorted (
135+ tracked_services .items (), reverse = True
136+ )
122137
123- self . container . clear () # Clear the current cards
138+ current_hash = _get_hash ( tracked_items )
124139
125- tracked_services = await get_all_tracked_services (self .parent_app )
140+ if self .last_hash != current_hash :
141+ # Clear the current cards
142+ self .container .clear ()
143+ for node_id , service in tracked_items :
144+ _render_card (self .container , node_id , service )
126145
127- for node_id , service in tracked_services .items ():
128- _render_card (self .container , node_id , service )
146+ self .last_hash = current_hash
129147
130148
131149@router .page ("/" )
@@ -142,4 +160,4 @@ async def index():
142160 # render cards when page is loaded
143161 await updater .update ()
144162 # update card at a set interval
145- ui .timer (1 , lambda : updater .update () )
163+ ui .timer (1 , updater .update )
0 commit comments