diff --git a/pkg/web_app/lib/src/widget/downloads_chart/computations.dart b/pkg/web_app/lib/src/widget/downloads_chart/computations.dart new file mode 100644 index 0000000000..1d59151509 --- /dev/null +++ b/pkg/web_app/lib/src/widget/downloads_chart/computations.dart @@ -0,0 +1,36 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:_pub_shared/data/download_counts_data.dart'; + +Iterable prepareRanges(List rangeDownloads) { + return rangeDownloads.map((e) => e.versionRange); +} + +/// Returns an iterable containing data to be shown in a chart displaying the +/// ranges in [rangeDownloads]. +/// +/// The 'i'th entry in the iterable is a list of the download values +/// (y coordinates) for the 'i'th week (x coordinate). +List> prepareWeekLists( + List totals, + List rangeDownloads, + int displayLength, +) { + final result = >[]; + + final showOther = + totals[0] > rangeDownloads.fold(0, (sum, d) => sum + d.counts[0]); + + for (int week = 0; week < displayLength; week++) { + final weekList = []; + if (showOther) { + weekList.add(totals[week] - + rangeDownloads.fold(0, (sum, d) => sum + d.counts[week])); + } + rangeDownloads.forEach((d) => weekList.add(d.counts[week])); + result.add(weekList); + } + return result.reversed.toList(); +} diff --git a/pkg/web_app/lib/src/widget/downloads_chart/widget.dart b/pkg/web_app/lib/src/widget/downloads_chart/widget.dart index 009f08d93b..009bf59458 100644 --- a/pkg/web_app/lib/src/widget/downloads_chart/widget.dart +++ b/pkg/web_app/lib/src/widget/downloads_chart/widget.dart @@ -3,10 +3,13 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:convert'; +import 'dart:math' as math; import 'package:_pub_shared/data/download_counts_data.dart'; import 'package:web/web.dart'; +import 'computations.dart'; + void create(HTMLElement element, Map options) { final dataPoints = options['points']; if (dataPoints == null) { @@ -18,7 +21,19 @@ void create(HTMLElement element, Map options) { final data = WeeklyVersionDownloadCounts.fromJson((utf8.decoder .fuse(json.decoder) .convert(base64Decode(dataPoints)) as Map)); - drawChart(svg, data); + + final weeksToDisplay = math.min(28, data.totalWeeklyDownloads.length); + + final majorDisplayLists = prepareWeekLists( + data.totalWeeklyDownloads, + data.majorRangeWeeklyDownloads, + weeksToDisplay, + ); + final majorRanges = data.majorRangeWeeklyDownloads.map((e) => e.versionRange); + + drawChart(svg, majorRanges, majorDisplayLists, data.newestDate); } -void drawChart(Element svg, WeeklyVersionDownloadCounts data) {} +void drawChart(Element svg, Iterable ranges, Iterable> values, + DateTime newestDate, + {bool stacked = true}) {} diff --git a/pkg/web_app/test/widget/downloads_chart/downloads_chart_test.dart b/pkg/web_app/test/widget/downloads_chart/downloads_chart_test.dart new file mode 100644 index 0000000000..c94aff92ca --- /dev/null +++ b/pkg/web_app/test/widget/downloads_chart/downloads_chart_test.dart @@ -0,0 +1,81 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:test/test.dart'; +import 'package:web_app/src/widget/downloads_chart/computations.dart'; +// import 'package:web_app/src/widget/downloads_chart/widget.dart'; + +void main() { + test('week lists - simpler', () { + final List l1 = List.from(List.filled(10, 10)) + ..addAll(List.filled(10, 20)) + ..add(2) + ..addAll(List.filled(31, 0)); + + final List l2 = List.from(List.filled(10, 20)) + ..addAll(List.filled(10, 40)) + ..add(4) + ..addAll(List.filled(31, 0)); + + final List l3 = List.from(List.filled(10, 70)) + ..addAll(List.filled(10, 140)) + ..add(14) + ..addAll(List.filled(31, 0)); + + final totals = List.from(List.filled(10, 110)) + ..addAll(List.filled(10, 220)) + ..add(22) + ..addAll(List.filled(31, 0)); + + final patchRangeDownloads = [ + (counts: l1, versionRange: '>=6.2.0-0 <6.2.1'), + (counts: l1, versionRange: '>=6.2.1-0 <6.2.2'), + (counts: l1, versionRange: '>=6.3.1-0 <6.3.2'), + (counts: l1, versionRange: '>=6.4.0-0 <6.4.1'), + (counts: l1, versionRange: '>=6.6.1-0 <6.6.2'), + ]; + + final minorRangeDownloads = [ + (counts: l1, versionRange: '>=6.1.0-0 <6.2.0'), + (counts: l2, versionRange: '>=6.2.0-0 <6.3.0'), + (counts: l1, versionRange: '>=6.3.0-0 <6.4.0'), + (counts: l1, versionRange: '>=6.4.0-0 <6.5.0'), + (counts: l1, versionRange: '>=6.6.0-0 <6.7.0') + ]; + + final majorRangeDownloads = [ + (counts: l1, versionRange: '>=1.0.0-0 <2.0.0'), + (counts: l1, versionRange: '>=2.0.0-0 <3.0.0'), + (counts: l1, versionRange: '>=3.0.0-0 <4.0.0'), + (counts: l1, versionRange: '>=4.0.0-0 <5.0.0'), + (counts: l3, versionRange: '>=6.0.0-0 <7.0.0') + ]; + + final w1 = prepareWeekLists(totals, majorRangeDownloads, 52).toList(); + final w2 = prepareWeekLists(totals, minorRangeDownloads, 52).toList(); + final w3 = prepareWeekLists(totals, patchRangeDownloads, 52).toList(); + + for (int i = 42; i < 52; i++) { + expect(w1[i], [10, 10, 10, 10, 70]); + expect(w2[i], [50, 10, 20, 10, 10, 10]); + expect(w3[i], [60, 10, 10, 10, 10, 10]); + } + + for (int i = 32; i < 42; i++) { + expect(w1[i], [20, 20, 20, 20, 140]); + expect(w2[i], [100, 20, 40, 20, 20, 20]); + expect(w3[i], [120, 20, 20, 20, 20, 20]); + } + + expect(w1[31], [2, 2, 2, 2, 14]); + expect(w2[31], [10, 2, 4, 2, 2, 2]); + expect(w3[31], [12, 2, 2, 2, 2, 2]); + + for (int i = 0; i < 31; i++) { + expect(w1[i], [0, 0, 0, 0, 0]); + expect(w2[i], [0, 0, 0, 0, 0, 0]); + expect(w3[i], [0, 0, 0, 0, 0, 0]); + } + }); +}