perspective_viewer/session/
column_defaults_update.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use std::iter::IntoIterator;
14
15use itertools::Itertools;
16use perspective_client::config::*;
17
18use super::metadata::*;
19use crate::js::plugin::*;
20
21#[extend::ext]
22pub impl ViewConfigUpdate {
23    /// Appends additional columns to the `columns` field of this
24    /// `ViewConfigUpdate` by picking appropriate new columns from the
25    /// `SessionMetadata`, give the necessary column requirements of the plugin
26    /// provided by a `ViewConfigRequirements`.  For example, an "X/Y Scatter"
27    /// chart needs a minimum of 2 numeric columns to be drawable.
28    fn set_update_column_defaults(
29        &mut self,
30        metadata: &SessionMetadata,
31        columns: &[Option<String>],
32        requirements: &ViewConfigRequirements,
33    ) {
34        if let (
35            None,
36            ViewConfigRequirements {
37                min: Some(min_cols),
38                names,
39                ..
40            },
41        ) = (&self.columns, &requirements)
42        {
43            // first try to take 2 numeric columns from existing config
44            let numeric_config_columns = columns
45                .iter()
46                .flatten()
47                .filter(|x| {
48                    matches!(
49                        metadata.get_column_table_type(x),
50                        Some(ColumnType::Float | ColumnType::Integer)
51                    )
52                })
53                .take(*min_cols)
54                .cloned()
55                .map(Some)
56                .collect::<Vec<_>>();
57
58            if numeric_config_columns.len() == *min_cols {
59                self.columns = Some(
60                    numeric_config_columns
61                        .into_iter()
62                        .pad_using(names.as_ref().map_or(0, |x| x.len()), |_| None)
63                        .collect::<Vec<_>>(),
64                );
65            } else {
66                // Append numeric columns from all columns and try again
67                let config_columns = numeric_config_columns
68                    .clone()
69                    .into_iter()
70                    .chain(
71                        metadata
72                            .get_table_columns()
73                            .into_iter()
74                            .flatten()
75                            .filter(|x| {
76                                !numeric_config_columns
77                                    .iter()
78                                    .any(|y| y.as_ref() == Some(*x))
79                            })
80                            .filter(|x| {
81                                matches!(
82                                    metadata.get_column_table_type(x),
83                                    Some(ColumnType::Float | ColumnType::Integer)
84                                )
85                            })
86                            .cloned()
87                            .map(Some),
88                    )
89                    .take(*min_cols)
90                    .collect::<Vec<_>>();
91
92                if config_columns.len() == *min_cols {
93                    self.columns = Some(
94                        config_columns
95                            .into_iter()
96                            .pad_using(names.as_ref().map_or(0, |x| x.len()), |_| None)
97                            .collect::<Vec<_>>(),
98                    );
99                } else {
100                    self.columns = Some(
101                        metadata
102                            .get_table_columns()
103                            .into_iter()
104                            .flatten()
105                            .take(*min_cols)
106                            .cloned()
107                            .map(Some)
108                            .collect::<Vec<_>>(),
109                    );
110                }
111            }
112        } else if self.columns.is_none() {
113            let mut columns = columns.to_vec();
114            let initial_len = columns.len();
115            if let Some(last_filled) = columns.iter().rposition(|x| x.is_some()) {
116                columns.truncate(last_filled + 1);
117                if let ViewConfigRequirements {
118                    names: Some(names), ..
119                } = &requirements
120                {
121                    columns = columns
122                        .into_iter()
123                        .enumerate()
124                        .filter(|(idx, x)| *idx < names.len() || x.is_some())
125                        .map(|(_, x)| x)
126                        .pad_using(names.len(), |_| None)
127                        .collect::<Vec<_>>();
128                } else {
129                    columns = columns
130                        .into_iter()
131                        .filter(|x| x.is_some())
132                        .collect::<Vec<_>>();
133                }
134
135                if initial_len != columns.len() {
136                    self.columns = Some(columns);
137                }
138            }
139        }
140    }
141}