@@ -172,157 +172,172 @@ def render_content(
172
172
start_date , end_date , timezone , n_intervals , key_list , current_page ,
173
173
loaded_uuids_store , all_data_loaded
174
174
):
175
- initial_batch_size = 10 # Define the batch size for loading UUIDs
176
-
177
- # Update selected tab
178
- selected_tab = tab
179
- logging .debug (f"Callback - { selected_tab } Stage 1: Selected tab updated." )
180
-
181
- # Handle the UUIDs tab without fullscreen loading spinner
182
- if tab == 'tab-uuids-datatable' :
183
- start_time = time .time ()
184
- logging .debug (f"Callback - { selected_tab } Stage 2: Handling UUIDs tab." )
185
-
186
- # Ensure store_uuids contains the key 'data' which is a list of dictionaries
187
- if not isinstance (store_uuids , dict ) or 'data' not in store_uuids :
188
- logging .error (f"Expected store_uuids to be a dict with a 'data' key, but got { type (store_uuids )} " )
189
- return html .Div ([html .P ("Data structure error." )]), loaded_uuids_store , True
190
-
191
- uuids_list = store_uuids ['data' ]
192
-
193
- # Ensure uuids_list is a list for slicing
194
- if not isinstance (uuids_list , list ):
195
- logging .error (f"Expected store_uuids['data'] to be a list but got { type (uuids_list )} " )
196
- return html .Div ([html .P ("Data structure error." )]), loaded_uuids_store , True
197
-
198
- loaded_data = loaded_uuids_store .get ('data' , [])
199
- total_loaded = len (loaded_data )
200
-
201
- # Handle lazy loading
202
- if not loaded_uuids_store .get ('loaded' , False ):
203
- total_to_load = total_loaded + initial_batch_size
204
- total_to_load = min (total_to_load , len (uuids_list )) # Avoid loading more than available
205
-
206
- logging .debug (f"Callback - { selected_tab } Stage 3: Loading next batch of UUIDs from { total_loaded } to { total_to_load } ." )
207
-
208
- new_data = uuids_list [total_loaded :total_to_load ]
209
-
210
- if new_data :
211
- # Process and append the new data to the loaded store
212
- processed_data = db_utils .add_user_stats (new_data , initial_batch_size )
213
- loaded_data .extend (processed_data )
214
-
215
- # Update the store with the new data
216
- loaded_uuids_store ['data' ] = loaded_data # Mark all data as loaded if done
217
- loaded_uuids_store ['loaded' ] = len (loaded_data ) >= len (uuids_list )
218
-
219
- logging .debug (f"Callback - { selected_tab } Stage 4: New batch loaded. Total loaded: { len (loaded_data )} ." )
220
-
221
- # Prepare the data to be displayed
222
- columns = perm_utils .get_uuids_columns () # Get the relevant columns
223
- df = pd .DataFrame (loaded_data )
224
-
225
- if df .empty or not perm_utils .has_permission ('data_uuids' ):
226
- logging .debug (f"Callback - { selected_tab } Error Stage: No data available or permission issues." )
227
- return html .Div ([html .P ("No data available or you don't have permission." )]), loaded_uuids_store , True
228
-
229
- df = df .drop (columns = [col for col in df .columns if col not in columns ])
230
-
231
- logging .debug (f"Callback - { selected_tab } Stage 5: Returning appended data to update the UI." )
232
- content = html .Div ([
233
- populate_datatable (df , table_id = 'uuid-table' , page_current = current_page ), # Pass current_page
234
- html .P (
235
- f"Showing { len (loaded_data )} of { len (uuids_list )} UUIDs." +
236
- (f" Loading 10 more..." if not loaded_uuids_store .get ('loaded' , False ) else "" ),
237
- style = {'margin' : '15px 5px' }
175
+ with ect .Timer () as total_timer :
176
+ initial_batch_size = 10 # Define the batch size for loading UUIDs
177
+
178
+ # Stage 1: Update selected tab
179
+ selected_tab = tab
180
+ logging .debug (f"Callback - { selected_tab } Stage 1: Selected tab updated." )
181
+
182
+ # Handle the UUIDs tab without fullscreen loading spinner
183
+ if tab == 'tab-uuids-datatable' :
184
+ with ect .Timer () as handle_uuids_timer :
185
+ logging .debug (f"Callback - { selected_tab } Stage 2: Handling UUIDs tab." )
186
+
187
+ # Ensure store_uuids contains the key 'data' which is a list of dictionaries
188
+ if not isinstance (store_uuids , dict ) or 'data' not in store_uuids :
189
+ logging .error (f"Expected store_uuids to be a dict with a 'data' key, but got { type (store_uuids )} " )
190
+ return html .Div ([html .P ("Data structure error." )]), loaded_uuids_store , True
191
+
192
+ uuids_list = store_uuids ['data' ]
193
+
194
+ # Ensure uuids_list is a list for slicing
195
+ if not isinstance (uuids_list , list ):
196
+ logging .error (f"Expected store_uuids['data'] to be a list but got { type (uuids_list )} " )
197
+ return html .Div ([html .P ("Data structure error." )]), loaded_uuids_store , True
198
+
199
+ loaded_data = loaded_uuids_store .get ('data' , [])
200
+ total_loaded = len (loaded_data )
201
+
202
+ # Handle lazy loading
203
+ if not loaded_uuids_store .get ('loaded' , False ):
204
+ total_to_load = total_loaded + initial_batch_size
205
+ total_to_load = min (total_to_load , len (uuids_list )) # Avoid loading more than available
206
+
207
+ logging .debug (f"Callback - { selected_tab } Stage 3: Loading next batch of UUIDs from { total_loaded } to { total_to_load } ." )
208
+
209
+ new_data = uuids_list [total_loaded :total_to_load ]
210
+
211
+ if new_data :
212
+ # Process and append the new data to the loaded store
213
+ processed_data = db_utils .add_user_stats (new_data , initial_batch_size )
214
+ loaded_data .extend (processed_data )
215
+
216
+ # Update the store with the new data
217
+ loaded_uuids_store ['data' ] = loaded_data # Mark all data as loaded if done
218
+ loaded_uuids_store ['loaded' ] = len (loaded_data ) >= len (uuids_list )
219
+
220
+ logging .debug (f"Callback - { selected_tab } Stage 4: New batch loaded. Total loaded: { len (loaded_data )} ." )
221
+
222
+ # Prepare the data to be displayed
223
+ columns = perm_utils .get_uuids_columns () # Get the relevant columns
224
+ df = pd .DataFrame (loaded_data )
225
+
226
+ if df .empty or not perm_utils .has_permission ('data_uuids' ):
227
+ logging .debug (f"Callback - { selected_tab } Error Stage: No data available or permission issues." )
228
+ return html .Div ([html .P ("No data available or you don't have permission." )]), loaded_uuids_store , True
229
+
230
+ df = df .drop (columns = [col for col in df .columns if col not in columns ])
231
+
232
+ logging .debug (f"Callback - { selected_tab } Stage 5: Returning appended data to update the UI." )
233
+ content = html .Div ([
234
+ populate_datatable (df , table_id = 'uuid-table' , page_current = current_page ), # Pass current_page
235
+ html .P (
236
+ f"Showing { len (loaded_data )} of { len (uuids_list )} UUIDs." +
237
+ (f" Loading 10 more..." if not loaded_uuids_store .get ('loaded' , False ) else "" ),
238
+ style = {'margin' : '15px 5px' }
239
+ )
240
+ ])
241
+
242
+ return content , loaded_uuids_store , False if not loaded_uuids_store ['loaded' ] else True
243
+
244
+ esdsq .store_dashboard_time (
245
+ "admin/data/render_content/handle_uuids_tab" ,
246
+ handle_uuids_timer
238
247
)
239
- ])
240
248
241
- elapsed_time = time .time () - start_time
242
- logging .info (f"Callback - { selected_tab } Stage 6: Total Time for UUIDs Tab: { elapsed_time :.2f} seconds" )
249
+ # Handle Trips tab
250
+ elif tab == 'tab-trips-datatable' :
251
+ with ect .Timer () as handle_trips_timer :
252
+ logging .debug (f"Callback - { selected_tab } Stage 2: Handling Trips tab." )
243
253
244
- return content , loaded_uuids_store , False if not loaded_uuids_store ['loaded' ] else True
254
+ data = store_trips ["data" ]
255
+ columns = perm_utils .get_allowed_trip_columns ()
256
+ columns .update (col ['label' ] for col in perm_utils .get_allowed_named_trip_columns ())
257
+ columns .update (store_trips ["userinputcols" ])
258
+ has_perm = perm_utils .has_permission ('data_trips' )
245
259
246
- # Handle other tabs normally
247
- elif tab == 'tab-trips-datatable' :
248
- start_time = time . time ( )
249
- logging . debug ( f"Callback - { selected_tab } Stage 2: Handling Trips tab." )
260
+ df = pd . DataFrame ( data )
261
+ if df . empty or not has_perm :
262
+ logging . debug ( f"Callback - { selected_tab } Error Stage: No data available or permission issues." )
263
+ return None , loaded_uuids_store , True
250
264
251
- data = store_trips ["data" ]
252
- columns = perm_utils .get_allowed_trip_columns ()
253
- columns .update (col ['label' ] for col in perm_utils .get_allowed_named_trip_columns ())
254
- columns .update (store_trips ["userinputcols" ])
255
- has_perm = perm_utils .has_permission ('data_trips' )
265
+ df = df .drop (columns = [col for col in df .columns if col not in columns ])
266
+ df = clean_location_data (df )
256
267
257
- df = pd .DataFrame (data )
258
- if df .empty or not has_perm :
259
- logging .debug (f"Callback - { selected_tab } Error Stage: No data available or permission issues." )
260
- return None , loaded_uuids_store , True
268
+ trips_table = populate_datatable (df )
261
269
262
- df = df .drop (columns = [col for col in df .columns if col not in columns ])
263
- df = clean_location_data (df )
264
-
265
- trips_table = populate_datatable (df )
266
- elapsed_time = time .time () - start_time
267
- logging .info (f"Callback - { selected_tab } Stage 3: Total Time for Trips Tab: { elapsed_time :.2f} seconds" )
268
-
269
- return html .Div ([
270
- html .Button ('Display columns with raw units' , id = 'button-clicked' , n_clicks = 0 , style = {'marginLeft' : '5px' }),
271
- trips_table
272
- ]), loaded_uuids_store , True
273
-
274
- elif tab == 'tab-demographics-datatable' :
275
- start_time = time .time ()
276
- logging .debug (f"Callback - { selected_tab } Stage 2: Handling Demographics tab." )
277
-
278
- data = store_demographics ["data" ]
279
- has_perm = perm_utils .has_permission ('data_demographics' )
280
-
281
- if len (data ) == 1 :
282
- data = list (data .values ())[0 ]
283
- columns = list (data [0 ].keys ())
284
- elif len (data ) > 1 :
285
- if not has_perm :
286
- return None , loaded_uuids_store , True
287
- return html .Div ([
288
- dcc .Tabs (id = 'subtabs-demographics' , value = list (data .keys ())[0 ], children = [
289
- dcc .Tab (label = key , value = key ) for key in data
290
- ]),
291
- html .Div (id = 'subtabs-demographics-content' )
292
- ]), loaded_uuids_store , True
293
-
294
- elapsed_time = time .time () - start_time
295
- logging .info (f"Callback - { selected_tab } Stage 3: Total Time for Demographics Tab: { elapsed_time :.2f} seconds" )
296
-
297
- elif tab == 'tab-trajectories-datatable' :
298
- start_time = time .time ()
299
- logging .debug (f"Callback - { selected_tab } Stage 2: Handling Trajectories tab." )
300
-
301
- (start_date , end_date ) = iso_to_date_only (start_date , end_date )
302
- # Fetch new data based on the selected key_list from the keylist-switch
303
- if store_trajectories == {} or key_list :
304
- store_trajectories = update_store_trajectories (start_date , end_date , timezone , store_excluded_uuids , key_list )
305
-
306
- data = store_trajectories .get ("data" , [])
307
- if data :
308
- columns = list (data [0 ].keys ())
309
- columns = perm_utils .get_trajectories_columns (columns )
310
- has_perm = perm_utils .has_permission ('data_trajectories' )
311
-
312
- df = pd .DataFrame (data )
313
- if df .empty or not has_perm :
314
- logging .debug (f"Callback - { selected_tab } Error Stage: No data available or permission issues." )
315
- return None , loaded_uuids_store , True
270
+ return html .Div ([
271
+ html .Button ('Display columns with raw units' , id = 'button-clicked' , n_clicks = 0 , style = {'marginLeft' : '5px' }),
272
+ trips_table
273
+ ]), loaded_uuids_store , True
274
+
275
+ esdsq .store_dashboard_time (
276
+ "admin/data/render_content/handle_trips_tab" ,
277
+ handle_trips_timer
278
+ )
279
+
280
+ # Handle Demographics tab
281
+ elif tab == 'tab-demographics-datatable' :
282
+ with ect .Timer () as handle_demographics_timer :
283
+ data = store_demographics ["data" ]
284
+ has_perm = perm_utils .has_permission ('data_demographics' )
285
+
286
+ if len (data ) == 1 :
287
+ data = list (data .values ())[0 ]
288
+ columns = list (data [0 ].keys ())
289
+ elif len (data ) > 1 :
290
+ if not has_perm :
291
+ return None , loaded_uuids_store , True
292
+ return html .Div ([
293
+ dcc .Tabs (id = 'subtabs-demographics' , value = list (data .keys ())[0 ], children = [
294
+ dcc .Tab (label = key , value = key ) for key in data
295
+ ]),
296
+ html .Div (id = 'subtabs-demographics-content' )
297
+ ]), loaded_uuids_store , True
298
+
299
+ esdsq .store_dashboard_time (
300
+ "admin/data/render_content/handle_demographics_tab" ,
301
+ handle_demographics_timer
302
+ )
303
+
304
+ # Handle Trajectories tab
305
+ elif tab == 'tab-trajectories-datatable' :
306
+ with ect .Timer () as handle_trajectories_timer :
307
+ (start_date , end_date ) = iso_to_date_only (start_date , end_date )
308
+ # Fetch new data based on the selected key_list from the keylist-switch
309
+ if store_trajectories == {} or key_list :
310
+ store_trajectories = update_store_trajectories (start_date , end_date , timezone , store_excluded_uuids , key_list )
311
+
312
+ data = store_trajectories .get ("data" , [])
313
+ if data :
314
+ columns = list (data [0 ].keys ())
315
+ columns = perm_utils .get_trajectories_columns (columns )
316
+ has_perm = perm_utils .has_permission ('data_trajectories' )
317
+
318
+ df = pd .DataFrame (data )
319
+ if df .empty or not has_perm :
320
+ logging .debug (f"Callback - { selected_tab } Error Stage: No data available or permission issues." )
321
+ return None , loaded_uuids_store , True
316
322
317
- df = df .drop (columns = [col for col in df .columns if col not in columns ])
323
+ df = df .drop (columns = [col for col in df .columns if col not in columns ])
318
324
319
- elapsed_time = time .time () - start_time
320
- logging .info (f"Callback - { selected_tab } Stage 3: Total Time for Trajectories Tab: { elapsed_time :.2f} seconds" )
325
+ return populate_datatable (df ), loaded_uuids_store , True
321
326
322
- return populate_datatable (df ), loaded_uuids_store , True
327
+ esdsq .store_dashboard_time (
328
+ "admin/data/render_content/handle_trajectories_tab" ,
329
+ handle_trajectories_timer
330
+ )
331
+
332
+ # Handle unhandled tabs or errors
333
+ else :
334
+ logging .debug (f"Callback - { selected_tab } Error Stage: No data loaded or unhandled tab." )
335
+ return None , loaded_uuids_store , True
323
336
324
- # Default case: if no data is loaded or the tab is not handled
325
- logging .debug (f"Callback - { selected_tab } Error Stage: No data loaded or unhandled tab." )
337
+ esdsq .store_dashboard_time (
338
+ "admin/data/render_content/total_time" ,
339
+ total_timer
340
+ )
326
341
return None , loaded_uuids_store , True
327
342
328
343
# Handle subtabs for demographic table when there are multiple surveys
@@ -422,27 +437,47 @@ def update_dropdowns_trips(n_clicks, button_label):
422
437
423
438
424
439
def populate_datatable (df , table_id = '' , page_current = 0 ):
425
- if not isinstance (df , pd .DataFrame ):
426
- raise PreventUpdate
427
- return dash_table .DataTable (
428
- id = table_id ,
429
- # columns=[{"name": i, "id": i} for i in df.columns],
430
- data = df .to_dict ('records' ),
431
- export_format = "csv" ,
432
- filter_options = {"case" : "sensitive" },
433
- # filter_action="native",
434
- sort_action = "native" , # give user capability to sort columns
435
- sort_mode = "single" , # sort across 'multi' or 'single' columns
436
- page_current = page_current , # set to current page
437
- page_size = 50 , # number of rows visible per page
438
- style_cell = {
439
- 'textAlign' : 'left' ,
440
- # 'minWidth': '100px',
441
- # 'width': '100px',
442
- # 'maxWidth': '100px',
443
- },
444
- style_table = {'overflowX' : 'auto' },
445
- css = [{"selector" :".show-hide" , "rule" :"display:none" }]
440
+ with ect .Timer () as total_timer :
441
+ # Stage 1: Check if df is a DataFrame and raise PreventUpdate if not
442
+ with ect .Timer () as stage1_timer :
443
+ if not isinstance (df , pd .DataFrame ):
444
+ raise PreventUpdate
445
+ esdsq .store_dashboard_time (
446
+ "admin/data/populate_datatable/check_dataframe_type" ,
447
+ stage1_timer
448
+ )
449
+
450
+ # Stage 2: Create DataTable
451
+ with ect .Timer () as stage2_timer :
452
+ table = dash_table .DataTable (
453
+ id = table_id ,
454
+ # columns=[{"name": i, "id": i} for i in df.columns],
455
+ data = df .to_dict ('records' ),
456
+ export_format = "csv" ,
457
+ filter_options = {"case" : "sensitive" },
458
+ # filter_action="native",
459
+ sort_action = "native" , # give user capability to sort columns
460
+ sort_mode = "single" , # sort across 'multi' or 'single' columns
461
+ page_current = page_current , # set to current page
462
+ page_size = 50 , # number of rows visible per page
463
+ style_cell = {
464
+ 'textAlign' : 'left' ,
465
+ # 'minWidth': '100px',
466
+ # 'width': '100px',
467
+ # 'maxWidth': '100px',
468
+ },
469
+ style_table = {'overflowX' : 'auto' },
470
+ css = [{"selector" :".show-hide" , "rule" :"display:none" }]
471
+ )
472
+ esdsq .store_dashboard_time (
473
+ "admin/db_utils/populate_datatable/create_datatable" ,
474
+ stage2_timer
475
+ )
476
+
477
+ esdsq .store_dashboard_time (
478
+ "admin/db_utils/populate_datatable/total_time" ,
479
+ total_timer
446
480
)
481
+ return table
482
+
447
483
448
- return result
0 commit comments