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

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

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

Powered by Google App Engine
This is Rietveld 408576698