@@ -237,48 +237,53 @@ def get_data(entry_id):
237237# =========================================================
238238
239239@app .callback (
240- [Output ('ai-analysis-status' , 'children' ), Output ('log-interval' , 'disabled' ), Output ('ai-log-output-wrapper' , 'style' )],
241- [Input ('run-ai-btn' , 'n_clicks' )],
240+ [Output ('ai-analysis-status' , 'children' ),
241+ Output ('log-interval' , 'disabled' ),
242+ Output ('ai-log-output-wrapper' , 'style' ),
243+ Output ('ai-log-output' , 'children' )],
244+ [Input ('run-ai-btn' , 'n_clicks' ), Input ('log-interval' , 'n_intervals' )],
242245 [State ('entry-id' , 'data' )]
243246)
244- def start_ai_analysis (n_clicks , entry_id ):
245- if n_clicks == 0 or not entry_id :
246- return dash .no_update , dash .no_update , dash .no_update
247+ def handle_ai_analysis (n_clicks , n_intervals , entry_id ):
248+ ctx = dash .callback_context
249+ if not ctx .triggered :
250+ return dash .no_update , dash .no_update , dash .no_update , dash .no_update
247251
248- try :
249- analysis = MFPAnalysis .objects .get (Entry_id = entry_id )
250- dia = getattr (analysis , 'Particle_Diameter' , 51 )
251- minmass = getattr (analysis , 'Threshold' , 0.05 )
252-
253- threading .Thread (target = run_cellpose_cement_analysis , args = (entry_id , dia , minmass , None , None , 'all' , None )).start ()
254-
255- style = {'margin' : '10px' , 'padding' : '10px' , 'backgroundColor' : '#eef2f5' , 'borderRadius' : '5px' , 'maxHeight' : '150px' , 'overflowY' : 'auto' , 'fontFamily' : 'monospace' , 'fontSize' : '12px' , 'display' : 'block' }
256-
257- return html .Div ("🚀 KI-Analyse läuft im Hintergrund..." , style = {'color' : 'blue' , 'fontWeight' : 'bold' }), False , style
258- except Exception as e :
259- return html .Div (f"❌ Fehler: { str (e )} " , style = {'color' : 'red' , 'fontWeight' : 'bold' }), dash .no_update , dash .no_update
260-
261- @app .callback (
262- Output ('ai-log-output' , 'children' ),
263- [Input ('log-interval' , 'n_intervals' )],
264- [State ('entry-id' , 'data' )]
265- )
266- def update_log (n , entry_id ):
267- if not entry_id : return dash .no_update
268- log_file = f"/tmp/cellpose_log_{ entry_id } .txt"
269- if os .path .exists (log_file ):
252+ trigger_id = ctx .triggered [0 ]['prop_id' ].split ('.' )[0 ]
253+
254+ # FALL 1: Der Start-Button wurde gedrückt
255+ if trigger_id == 'run-ai-btn' :
256+ if n_clicks == 0 or not entry_id :
257+ return dash .no_update , dash .no_update , dash .no_update , dash .no_update
270258 try :
271- with open (log_file , "r" ) as f :
272- lines = f .readlines ()
273- return html .Div ([
274- html .Div (line , style = {
275- 'color' : 'green' if '✅' in line else ('red' if '❌' in line else ('orange' if '⚠️' in line else 'black' )),
276- 'marginBottom' : '2px'
277- }) for line in lines if line .strip ()
278- ])
279- except :
280- return html .Div ("Lese Logdatei..." )
281- return html .Div ("Warte auf Logdatei..." )
259+ analysis = MFPAnalysis .objects .get (Entry_id = entry_id )
260+ dia = getattr (analysis , 'Particle_Diameter' , 51 )
261+ minmass = getattr (analysis , 'Threshold' , 0.05 )
262+
263+ threading .Thread (target = run_cellpose_cement_analysis , args = (entry_id , dia , minmass , None , None , 'all' , None )).start ()
264+
265+ style = {'margin' : '10px' , 'padding' : '10px' , 'backgroundColor' : '#eef2f5' , 'borderRadius' : '5px' , 'maxHeight' : '150px' , 'overflowY' : 'auto' , 'fontFamily' : 'monospace' , 'fontSize' : '12px' , 'display' : 'block' }
266+ return html .Div ("🚀 KI-Analyse läuft im Hintergrund..." , style = {'color' : 'blue' , 'fontWeight' : 'bold' }), False , style , "Warte auf Logdatei..."
267+ except Exception as e :
268+ return html .Div (f"❌ Fehler: { str (e )} " , style = {'color' : 'red' , 'fontWeight' : 'bold' }), dash .no_update , dash .no_update , dash .no_update
269+
270+ # FALL 2: Das Intervall holt sich neue Log-Updates
271+ elif trigger_id == 'log-interval' :
272+ if not entry_id : return dash .no_update , dash .no_update , dash .no_update , dash .no_update
273+ log_file = f"/tmp/cellpose_log_{ entry_id } .txt"
274+ if os .path .exists (log_file ):
275+ try :
276+ with open (log_file , "r" ) as f :
277+ lines = f .readlines ()
278+ log_divs = [html .Div (line , style = {'color' : 'green' if '✅' in line else ('red' if '❌' in line else ('orange' if '⚠️' in line else 'black' )), 'marginBottom' : '2px' }) for line in lines if line .strip ()]
279+ text_content = "" .join (lines )
280+ if "✅ Analyse erfolgreich abgeschlossen" in text_content or "❌ Systemfehler" in text_content :
281+ status = html .Div ("✅ Analyse fertig! Lade die Seite neu (🔄 Reload)." , style = {'color' : 'green' , 'fontWeight' : 'bold' })
282+ return status , True , dash .no_update , log_divs
283+ else :
284+ return dash .no_update , dash .no_update , dash .no_update , log_divs
285+ except : return dash .no_update , dash .no_update , dash .no_update , html .Div ("Lese Logdatei..." )
286+ return dash .no_update , dash .no_update , dash .no_update , html .Div ("Warte auf Logdatei..." )
282287
283288@app .callback (
284289 [Output ('entry-id' , 'data' ), Output ('loading-status' , 'children' ),
@@ -509,8 +514,9 @@ def update_glob(sel, metric, tab, eid):
509514 if col not in tracks .columns and col == 'radius_cellpose' and 'radius' in tracks .columns :
510515 col = 'radius'
511516
512- df = tracks [tracks ['particle' ].isin (sel )] if sel else tracks
513- _ , t_unit = General .get_smart_time (df ['time' ])
517+ df = tracks [tracks ['particle' ].isin (sel )].copy () if sel else tracks .copy ()
518+ scaled_time , t_unit = General .get_smart_time (df ['time' ])
519+ df ['scaled_time' ] = scaled_time
514520
515521 # --- SPAGHETTI PLOT ---
516522 fig_s = go .Figure ()
@@ -519,7 +525,7 @@ def update_glob(sel, metric, tab, eid):
519525
520526 for p in uids [:limit ]:
521527 d = df [df ['particle' ] == p ].sort_values ('time' )
522- t_vals , _ = General . get_smart_time ( d ['time' ])
528+ t_vals = d ['scaled_time' ]
523529
524530 y_vals = d [col ].fillna (0 ) if col in d .columns else [0 ] * len (d )
525531 fig_s .add_trace (go .Scatter (
@@ -533,11 +539,11 @@ def update_glob(sel, metric, tab, eid):
533539
534540 # Durchschnittslinie (AVG)
535541 if col in df .columns :
536- avg = df .groupby ('frame' ).agg ({'time ' : 'first' , col : 'mean' })
542+ avg = df .groupby ('frame' ).agg ({'scaled_time ' : 'first' , col : 'mean' })
537543 else :
538- avg = df .groupby ('frame' ).agg ({'time ' : 'first' })
544+ avg = df .groupby ('frame' ).agg ({'scaled_time ' : 'first' })
539545 avg [col ] = 0
540- avg_t , _ = General . get_smart_time ( avg ['time' ])
546+ avg_t = avg ['scaled_time' ]
541547
542548 fig_s .add_trace (go .Scatter (
543549 x = avg_t , y = avg [col ],
0 commit comments