@@ -10,6 +10,7 @@ use amalthea::comm::event::CommManagerEvent;
10
10
use amalthea:: comm:: variables_comm:: ClipboardFormatFormat ;
11
11
use amalthea:: comm:: variables_comm:: FormattedVariable ;
12
12
use amalthea:: comm:: variables_comm:: InspectedVariable ;
13
+ use amalthea:: comm:: variables_comm:: QueryTableSummaryResult ;
13
14
use amalthea:: comm:: variables_comm:: RefreshParams ;
14
15
use amalthea:: comm:: variables_comm:: UpdateParams ;
15
16
use amalthea:: comm:: variables_comm:: Variable ;
@@ -40,6 +41,7 @@ use stdext::spawn;
40
41
41
42
use crate :: data_explorer:: r_data_explorer:: DataObjectEnvInfo ;
42
43
use crate :: data_explorer:: r_data_explorer:: RDataExplorer ;
44
+ use crate :: data_explorer:: summary_stats:: summary_stats;
43
45
use crate :: lsp:: events:: EVENTS ;
44
46
use crate :: r_task;
45
47
use crate :: thread:: RThreadSafe ;
@@ -119,7 +121,7 @@ impl RVariables {
119
121
// Validate that the RObject we were passed is actually an environment
120
122
if let Err ( err) = r_assert_type ( env. sexp , & [ ENVSXP ] ) {
121
123
log:: warn!(
122
- "Environment : Attempt to monitor or list non-environment object {env:?} ({err:?})"
124
+ "Variables : Attempt to monitor or list non-environment object {env:?} ({err:?})"
123
125
) ;
124
126
}
125
127
@@ -191,17 +193,17 @@ impl RVariables {
191
193
// appropriate. Retrying is likely to just lead to a busy
192
194
// loop.
193
195
log:: error!(
194
- "Environment : Error receiving message from frontend: {err:?}"
196
+ "Variables : Error receiving message from frontend: {err:?}"
195
197
) ;
196
198
197
199
break ;
198
200
} ,
199
201
} ;
200
- log:: info!( "Environment : Received message from frontend: {msg:?}" ) ;
202
+ log:: info!( "Variables : Received message from frontend: {msg:?}" ) ;
201
203
202
204
// Break out of the loop if the frontend has closed the channel
203
205
if let CommMsg :: Close = msg {
204
- log:: info!( "Environment : Closing down after receiving comm_close from frontend." ) ;
206
+ log:: info!( "Variables : Closing down after receiving comm_close from frontend." ) ;
205
207
206
208
// Remember that the user initiated the close so that we can
207
209
// avoid sending a duplicate close message from the back end
@@ -295,8 +297,9 @@ impl RVariables {
295
297
let viewer_id = self . view ( & params. path ) ?;
296
298
Ok ( VariablesBackendReply :: ViewReply ( viewer_id) )
297
299
} ,
298
- VariablesBackendRequest :: QueryTableSummary ( _) => {
299
- return Err ( anyhow ! ( "Variables: QueryTableSummary not yet supported" ) ) ;
300
+ VariablesBackendRequest :: QueryTableSummary ( params) => {
301
+ let result = self . query_table_summary ( & params. path , & params. query_types ) ?;
302
+ Ok ( VariablesBackendReply :: QueryTableSummaryReply ( result) )
300
303
} ,
301
304
}
302
305
}
@@ -400,6 +403,98 @@ impl RVariables {
400
403
} )
401
404
}
402
405
406
+ /// Query table summary for the given variable.
407
+ ///
408
+ /// - `path`: The path to the variable to summarize, as an array of access keys
409
+ /// - `query_types`: A list of query types (e.g. "summary_stats")
410
+ ///
411
+ /// Returns summary information about the table including schemas and profiles.
412
+ fn query_table_summary (
413
+ & mut self ,
414
+ path : & Vec < String > ,
415
+ query_types : & Vec < String > ,
416
+ ) -> anyhow:: Result < QueryTableSummaryResult > {
417
+ r_task ( || {
418
+ let env = self . env . get ( ) . clone ( ) ;
419
+ let table = PositronVariable :: resolve_data_object ( env, & path) ?;
420
+
421
+ let kind = if harp:: utils:: r_is_data_frame ( table. sexp ) {
422
+ harp:: TableKind :: Dataframe
423
+ } else if harp:: utils:: r_is_matrix ( table. sexp ) {
424
+ harp:: TableKind :: Matrix
425
+ } else {
426
+ return Err ( anyhow ! (
427
+ "Object is not a supported table type (data.frame or matrix)"
428
+ ) ) ;
429
+ } ;
430
+
431
+ let num_cols = match kind {
432
+ harp:: TableKind :: Dataframe => {
433
+ let ncol = harp:: DataFrame :: n_col ( table. sexp ) ?;
434
+ ncol as i64
435
+ } ,
436
+ harp:: TableKind :: Matrix => {
437
+ let ( _nrow, ncol) = harp:: Matrix :: dim ( table. sexp ) ?;
438
+ ncol as i64
439
+ } ,
440
+ } ;
441
+
442
+ let shapes = RDataExplorer :: r_get_shape ( table. clone ( ) ) ?;
443
+
444
+ let column_schemas: Vec < String > = shapes
445
+ . columns
446
+ . iter ( )
447
+ . map ( |schema| serde_json:: to_string ( schema) )
448
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
449
+
450
+ let mut column_profiles: Vec < String > = vec ! [ ] ;
451
+
452
+ if query_types. contains ( & "summary_stats" . to_string ( ) ) {
453
+ let profiles: Vec < String > = shapes
454
+ . columns
455
+ . iter ( )
456
+ . enumerate ( )
457
+ . map ( |( i, schema) | -> anyhow:: Result < String > {
458
+ let column = harp:: tbl_get_column ( table. sexp , i as i32 , kind) ?;
459
+
460
+ let format_options = amalthea:: comm:: data_explorer_comm:: FormatOptions {
461
+ large_num_digits : 4 ,
462
+ small_num_digits : 6 ,
463
+ max_integral_digits : 7 ,
464
+ max_value_length : 1000 ,
465
+ thousands_sep : None ,
466
+ } ;
467
+
468
+ let summary_stats =
469
+ summary_stats ( column. sexp , schema. type_display , & format_options) . map (
470
+ |stats| {
471
+ serde_json:: to_value ( stats) . unwrap_or ( serde_json:: Value :: Null )
472
+ } ,
473
+ ) ?;
474
+
475
+ let profile = serde_json:: json!( {
476
+ "column_name" : schema. column_name,
477
+ "type_display" : format!( "{:?}" , schema. type_display) . to_lowercase( ) ,
478
+ "summary_stats" : summary_stats,
479
+ } )
480
+ . to_string ( ) ;
481
+
482
+ Ok ( profile)
483
+ } )
484
+ . collect :: < anyhow:: Result < Vec < String > > > ( ) ?;
485
+
486
+ column_profiles. extend ( profiles) ;
487
+ }
488
+
489
+ Ok ( QueryTableSummaryResult {
490
+ num_rows : shapes. num_rows as i64 ,
491
+ num_columns : num_cols,
492
+ column_schemas,
493
+ column_profiles,
494
+ } )
495
+ } )
496
+ }
497
+
403
498
fn send_event ( & mut self , message : VariablesFrontendEvent , request_id : Option < String > ) {
404
499
let data = serde_json:: to_value ( message) ;
405
500
@@ -415,7 +510,7 @@ impl RVariables {
415
510
self . comm . outgoing_tx . send ( comm_msg) . unwrap ( )
416
511
} ,
417
512
Err ( err) => {
418
- log:: error!( "Environment : Failed to serialize environment data: {err}" ) ;
513
+ log:: error!( "Variables : Failed to serialize environment data: {err}" ) ;
419
514
} ,
420
515
}
421
516
}
@@ -449,7 +544,7 @@ impl RVariables {
449
544
Err ( err) => {
450
545
// This isn't a critical error but would also be very
451
546
// unexpected.
452
- log:: error!( "Environment : Could not evaluate .Last.value ({err:?})" ) ;
547
+ log:: error!( "Variables : Could not evaluate .Last.value ({err:?})" ) ;
453
548
None
454
549
} ,
455
550
}
0 commit comments