Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: packages/charted/lib/charts/data_transformers/aggregation_transformer.dart

Issue 1521693002: Roll Observatory deps (charted -> ^0.3.0) (Closed) Base URL: https://chromium.googlesource.com/external/github.com/dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 //
2 * Copyright 2014 Google Inc. All rights reserved. 2 // Copyright 2014 Google Inc. All rights reserved.
3 * 3 //
4 * Use of this source code is governed by a BSD-style 4 // Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at 5 // license that can be found in the LICENSE file or at
6 * https://developers.google.com/open-source/licenses/bsd 6 // https://developers.google.com/open-source/licenses/bsd
7 */ 7 //
8 8
9 part of charted.charts; 9 part of charted.charts;
10 10
11 /** 11 /// Transforms the ChartData based on the specified dimension columns and facts
12 * Transforms the ChartData base on the specified dimension columns and facts 12 /// columns indices. The values in the facts columns will be aggregated by the
13 * columns indices. The values in the facts columns will be aggregated by the 13 /// tree hierarchy generated by the dimension columns. Expand and Collapse
14 * tree hierarchy generated by the dimension columns. Expand and Collapse 14 /// methods may be called to display different levels of aggregation.
15 * methods may be called to display different levels of aggregation. 15 ///
16 * 16 /// The output ChartData produced by transform() will contain only columns in th e
17 * The output ChartData produced by transform() will contain only columns in the 17 /// original ChartData that were specified in dimensions or facts column indices .
18 * original ChartData that were specified in dimensions or facts column indices. 18 /// The output column will be re-ordered first by the indices specified in the
19 * The output column will be re-ordered first by the indices specified in the 19 /// dimension column indices then by the facts column indices. The data in the
20 * dimension column indices then by the facts column indices. The data in the 20 /// cells of each row will also follow this rule.
21 * cells of each row will also follow this rule.
22 */
23 class AggregationTransformer extends ChangeNotifier 21 class AggregationTransformer extends ChangeNotifier
24 implements ChartDataTransform, ChartData { 22 implements ChartDataTransform, ChartData {
25
26 static const String AGGREGATION_TYPE_SUM = 'sum'; 23 static const String AGGREGATION_TYPE_SUM = 'sum';
27 static const String AGGREGATION_TYPE_MIN = 'min'; 24 static const String AGGREGATION_TYPE_MIN = 'min';
28 static const String AGGREGATION_TYPE_MAX = 'max'; 25 static const String AGGREGATION_TYPE_MAX = 'max';
29 static const String AGGREGATION_TYPE_VALID = 'valid'; 26 static const String AGGREGATION_TYPE_VALID = 'valid';
30 final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer(); 27 final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer();
31 final Set<List> _expandedSet = new Set(); 28 final Set<List> _expandedSet = new Set();
32 Iterable<ChartColumnSpec> columns; 29 Iterable<ChartColumnSpec> columns;
33 ObservableList<Iterable> rows = new ObservableList(); 30 ObservableList<Iterable> rows = new ObservableList();
34 List<int> _dimensionColumnIndices; 31 List<int> _dimensionColumnIndices;
35 List<int> _factsColumnIndices; 32 List<int> _factsColumnIndices;
36 String _aggregationType; 33 String _aggregationType;
37 AggregationModel _model; 34 AggregationModel _model;
38 bool _expandAllDimension = false; 35 bool _expandAllDimension = false;
39 List _selectedColumns = []; 36 List _selectedColumns = [];
40 FieldAccessor _indexFieldAccessor = (List row, int index) => row[index]; 37 FieldAccessor _indexFieldAccessor = (List row, int index) => row[index];
41 ChartData _data; 38 ChartData _data;
42 39
43 AggregationTransformer(this._dimensionColumnIndices, 40 AggregationTransformer(this._dimensionColumnIndices, this._factsColumnIndices,
44 this._factsColumnIndices,
45 [String aggregationType = AGGREGATION_TYPE_SUM]) { 41 [String aggregationType = AGGREGATION_TYPE_SUM]) {
46 _aggregationType = aggregationType; 42 _aggregationType = aggregationType;
47 } 43 }
48 44
49 /** 45 /// Transforms the ChartData base on the specified dimension columns and facts
50 * Transforms the ChartData base on the specified dimension columns and facts 46 /// columns, aggregation type and currently expanded dimensions.
51 * columns, aggregation type and currently expanded dimensions.
52 */
53 ChartData transform(ChartData data) { 47 ChartData transform(ChartData data) {
54 assert(data.columns.length > max(_dimensionColumnIndices)); 48 assert(data.columns.length > max(_dimensionColumnIndices));
55 assert(data.columns.length > max(_factsColumnIndices)); 49 assert(data.columns.length > max(_factsColumnIndices));
56 _data = data; 50 _data = data;
57 _registerListeners(); 51 _registerListeners();
58 _transform(); 52 _transform();
59 return this; 53 return this;
60 } 54 }
61 55
62 /** Registers listeners if data.rows or data.columns are Observable. */ 56 /// Registers listeners if data.rows or data.columns are Observable.
63 _registerListeners() { 57 _registerListeners() {
64 _dataSubscriptions.dispose(); 58 _dataSubscriptions.dispose();
65 59
66 if(_data is Observable) { 60 if (_data is Observable) {
67 var observable = (_data as Observable); 61 var observable = (_data as Observable);
68 _dataSubscriptions.add(observable.changes.listen((records) { 62 _dataSubscriptions.add(observable.changes.listen((records) {
69 _transform(); 63 _transform();
70 64
71 // NOTE: Currently we're only passing the first change because the chart 65 // NOTE: Currently we're only passing the first change because the chart
72 // area just draw with the updated data. When we add partial update 66 // area just draw with the updated data. When we add partial update
73 // to chart area, we'll need to handle this better. 67 // to chart area, we'll need to handle this better.
74 notifyChange(records.first); 68 notifyChange(records.first);
75 })); 69 }));
76 } 70 }
77 } 71 }
78 72
79 /** 73 /// Performs the filter transform with _data. This is called on transform and
80 * Performs the filter transform with _data. This is called on transform and 74 /// onChange if the input ChartData is Observable.
81 * onChange if the input ChartData is Observable.
82 */
83 _transform() { 75 _transform() {
84 _model = new AggregationModel(_data.rows, _dimensionColumnIndices, 76 _model = new AggregationModel(
85 _factsColumnIndices, aggregationTypes: [_aggregationType], 77 _data.rows, _dimensionColumnIndices, _factsColumnIndices,
78 aggregationTypes: [_aggregationType],
86 dimensionAccessor: _indexFieldAccessor, 79 dimensionAccessor: _indexFieldAccessor,
87 factsAccessor: _indexFieldAccessor); 80 factsAccessor: _indexFieldAccessor);
88 _model.compute(); 81 _model.compute();
89 82
90 // If user called expandAll prior to model initiation, do it now. 83 // If user called expandAll prior to model initiation, do it now.
91 if (_expandAllDimension) { 84 if (_expandAllDimension) {
92 expandAll(); 85 expandAll();
93 } 86 }
94 87
95 _selectedColumns.clear(); 88 _selectedColumns.clear();
96 _selectedColumns.addAll(_dimensionColumnIndices); 89 _selectedColumns.addAll(_dimensionColumnIndices);
97 _selectedColumns.addAll(_factsColumnIndices); 90 _selectedColumns.addAll(_factsColumnIndices);
98 91
99 // Process rows. 92 // Process rows.
100 rows.clear(); 93 rows.clear();
101 var transformedRows = <Iterable>[]; 94 var transformedRows = <Iterable>[];
102 for (var value in _model.valuesForDimension(_dimensionColumnIndices[0])) { 95 for (var value in _model.valuesForDimension(_dimensionColumnIndices[0])) {
103 _generateAggregatedRow(transformedRows, [value]); 96 _generateAggregatedRow(transformedRows, [value]);
104 } 97 }
105 rows.addAll(transformedRows); 98 rows.addAll(transformedRows);
106 99
107 // Process columns. 100 // Process columns.
108 columns = new List<ChartColumnSpec>.generate(_selectedColumns.length, (index ) => 101 columns = new List<ChartColumnSpec>.generate(_selectedColumns.length,
109 _data.columns.elementAt(_selectedColumns[index])); 102 (index) => _data.columns.elementAt(_selectedColumns[index]));
110 } 103 }
111 104
112 /** 105 /// Fills the aggregatedRows List with data base on the set of expanded values
113 * Fills the aggregatedRows List with data base on the set of expanded values 106 /// recursively. Currently when a dimension is expanded, rows are
114 * recursively. Currently when a dimension is expanded, rows are 107 /// generated for its children but not for itself. If we want to change the
115 * generated for its children but not for itself. If we want to change the 108 /// logic to include itself, just move the expand check around the else clause
116 * logic to include itself, just move the expand check around the else clause 109 /// and always write a row of data whether it's expanded or not.
117 * and always write a row of data whether it's expanded or not.
118 */
119 _generateAggregatedRow(List<Iterable> aggregatedRows, List dimensionValues) { 110 _generateAggregatedRow(List<Iterable> aggregatedRows, List dimensionValues) {
120 var entity = _model.facts(dimensionValues); 111 var entity = _model.facts(dimensionValues);
121 var dimensionLevel = dimensionValues.length - 1; 112 var dimensionLevel = dimensionValues.length - 1;
122 113
123 // Dimension is not expanded at this level. Generate data rows and fill int 114 // Dimension is not expanded at this level. Generate data rows and fill int
124 // value base on whether the column is dimension column or facts column. 115 // value base on whether the column is dimension column or facts column.
125 if (!_isExpanded(dimensionValues) || 116 if (!_isExpanded(dimensionValues) ||
126 dimensionValues.length == _dimensionColumnIndices.length) { 117 dimensionValues.length == _dimensionColumnIndices.length) {
127 aggregatedRows.add(new List.generate(_selectedColumns.length, (index) { 118 aggregatedRows.add(new List.generate(_selectedColumns.length, (index) {
128
129 // Dimension column. 119 // Dimension column.
130 if (index < _dimensionColumnIndices.length) { 120 if (index < _dimensionColumnIndices.length) {
131 if (index < dimensionLevel) { 121 if (index < dimensionLevel) {
132 // If column index is in a higher level, write parent value. 122 // If column index is in a higher level, write parent value.
133 return dimensionValues[0]; 123 return dimensionValues[0];
134 } else if (index == dimensionLevel) { 124 } else if (index == dimensionLevel) {
135 // If column index is at current level, write value. 125 // If column index is at current level, write value.
136 return dimensionValues.last; 126 return dimensionValues.last;
137 } else { 127 } else {
138 // If column Index is in a lower level, write empty string. 128 // If column Index is in a lower level, write empty string.
139 return ''; 129 return '';
140 } 130 }
141 } else { 131 } else {
142 // Write aggregated value for facts column. 132 // Write aggregated value for facts column.
143 return entity['${_aggregationType}(${_selectedColumns[index]})']; 133 return entity['${_aggregationType}(${_selectedColumns[index]})'];
144 } 134 }
145 })); 135 }));
146 } else { 136 } else {
147 // Dimension is expanded, process each child dimension in the expanded 137 // Dimension is expanded, process each child dimension in the expanded
148 // dimension. 138 // dimension.
149 for (AggregationItem childAggregation in entity['aggregations']) { 139 for (AggregationItem childAggregation in entity['aggregations']) {
150 _generateAggregatedRow(aggregatedRows, childAggregation.dimensions); 140 _generateAggregatedRow(aggregatedRows, childAggregation.dimensions);
151 } 141 }
152 } 142 }
153 } 143 }
154 144
155 /** 145 /// Expands a specific dimension and optionally expands all of its parent
156 * Expands a specific dimension and optionally expands all of its parent 146 /// dimensions.
157 * dimensions.
158 */
159 void expand(List dimension, [bool expandParent = true]) { 147 void expand(List dimension, [bool expandParent = true]) {
160 _expandAllDimension = false; 148 _expandAllDimension = false;
161 _expandedSet.add(dimension); 149 _expandedSet.add(dimension);
162 if (expandParent && dimension.length > 1) { 150 if (expandParent && dimension.length > 1) {
163 Function eq = const ListEquality().equals; 151 Function eq = const ListEquality().equals;
164 var dim = dimension.take(dimension.length - 1).toList(); 152 var dim = dimension.take(dimension.length - 1).toList();
165 if (!_expandedSet.any((e) => eq(e, dim))) { 153 if (!_expandedSet.any((e) => eq(e, dim))) {
166 expand(dim); 154 expand(dim);
167 } 155 }
168 } 156 }
169 } 157 }
170 158
171 /** 159 /// Collapses a specific dimension and optionally collapse all of its
172 * Collapses a specific dimension and optionally collapse all of its 160 /// Children dimensions.
173 * Children dimensions.
174 */
175 void collapse(List dimension, [bool collapseChildren = true]) { 161 void collapse(List dimension, [bool collapseChildren = true]) {
176 _expandAllDimension = false; 162 _expandAllDimension = false;
177 if (collapseChildren) { 163 if (collapseChildren) {
178 Function eq = const ListEquality().equals; 164 Function eq = const ListEquality().equals;
179 // Doing this because _expandedSet.where doesn't work. 165 // Doing this because _expandedSet.where doesn't work.
180 var collapseList = []; 166 var collapseList = [];
181 for (List dim in _expandedSet) { 167 for (List dim in _expandedSet) {
182 if (eq(dim.take(dimension.length).toList(), dimension)) { 168 if (eq(dim.take(dimension.length).toList(), dimension)) {
183 collapseList.add(dim); 169 collapseList.add(dim);
184 } 170 }
185 } 171 }
186 _expandedSet.removeAll(collapseList); 172 _expandedSet.removeAll(collapseList);
187 } else { 173 } else {
188 _expandedSet.remove(dimension); 174 _expandedSet.remove(dimension);
189 } 175 }
190 } 176 }
191 177
192 /** Expands all dimensions. */ 178 /// Expands all dimensions.
193 void expandAll() { 179 void expandAll() {
194 if (_model != null) { 180 if (_model != null) {
195 for (var value in _model.valuesForDimension(_dimensionColumnIndices[0])) { 181 for (var value in _model.valuesForDimension(_dimensionColumnIndices[0])) {
196 _expandAll([value]); 182 _expandAll([value]);
197 } 183 }
198 _expandAllDimension = false; 184 _expandAllDimension = false;
199 } else { 185 } else {
200 _expandAllDimension = true; 186 _expandAllDimension = true;
201 } 187 }
202 } 188 }
203 189
204 void _expandAll(value) { 190 void _expandAll(value) {
205 var entity = _model.facts(value); 191 var entity = _model.facts(value);
206 _expandedSet.add(value); 192 _expandedSet.add(value);
207 for (AggregationItem childAggregation in entity['aggregations']) { 193 for (AggregationItem childAggregation in entity['aggregations']) {
208 _expandAll(childAggregation.dimensions); 194 _expandAll(childAggregation.dimensions);
209 } 195 }
210 } 196 }
211 197
212 /** Collapses all dimensions. */ 198 /// Collapses all dimensions.
213 void collapseAll() { 199 void collapseAll() {
214 _expandAllDimension = false; 200 _expandAllDimension = false;
215 _expandedSet.clear(); 201 _expandedSet.clear();
216 } 202 }
217 203
218 /** Tests if specific dimension is expanded. */ 204 /// Tests if specific dimension is expanded.
219 bool _isExpanded(List dimension) { 205 bool _isExpanded(List dimension) {
220 Function eq = const ListEquality().equals; 206 Function eq = const ListEquality().equals;
221 return _expandedSet.any((e) => eq(e, dimension)); 207 return _expandedSet.any((e) => eq(e, dimension));
222 } 208 }
223 } 209 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698