Skip to content

Commit 69d649f

Browse files
committed
compute and upload 30 day total download counts
1 parent 4ce0164 commit 69d649f

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

app/lib/service/download_counts/backend.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:math';
6+
7+
import 'package:basics/basics.dart';
58
import 'package:gcloud/service_scope.dart' as ss;
9+
import 'package:gcloud/storage.dart';
610
import 'package:pub_dev/service/download_counts/download_counts.dart';
711
import 'package:pub_dev/service/download_counts/models.dart';
12+
import 'package:pub_dev/shared/configuration.dart';
813
import 'package:pub_dev/shared/datastore.dart';
914
import 'package:pub_dev/shared/redis_cache.dart';
15+
import 'package:pub_dev/shared/storage.dart';
16+
import 'package:pub_dev/shared/utils.dart';
1017

1118
/// Sets the download counts backend service.
1219
void registerDownloadCountsBackend(DownloadCountsBackend backend) =>
@@ -29,6 +36,11 @@ class DownloadCountsBackend {
2936
}));
3037
}
3138

39+
Future<Stream<DownloadCounts>> listAllDownloadCounts() async {
40+
final query = _db.query<DownloadCounts>();
41+
return query.run();
42+
}
43+
3244
Future<DownloadCounts> updateDownloadCounts(
3345
String pkg,
3446
Map<String, int> dayCounts,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
import 'dart:async';
5+
import 'dart:math';
6+
7+
import 'package:gcloud/storage.dart';
8+
import 'package:pub_dev/service/download_counts/backend.dart';
9+
import 'package:pub_dev/service/download_counts/models.dart';
10+
import 'package:pub_dev/shared/configuration.dart';
11+
import 'package:pub_dev/shared/storage.dart';
12+
import 'package:pub_dev/shared/utils.dart';
13+
14+
Future<void> compute30DaysTotalTask() async {
15+
final allDownloadCounts = await downloadCountsBackend.listAllDownloadCounts();
16+
final totals = await compute30DayTotals(allDownloadCounts);
17+
await updload30DaysTotal(totals);
18+
}
19+
20+
Future<Map<String, int>> compute30DayTotals(
21+
Stream<DownloadCounts> downloadCounts) async {
22+
final res = <String, int>{};
23+
await for (final dc in downloadCounts) {
24+
res[dc.package] = compute30DayTotal(dc);
25+
}
26+
27+
return res;
28+
}
29+
30+
int compute30DayTotal(DownloadCounts downloadCounts) {
31+
final totals = downloadCounts.countData.totalCounts;
32+
return totals
33+
.take(30)
34+
.fold(0, (previousValue, element) => previousValue + max(0, element));
35+
}
36+
37+
final downloadCounts30DaysTotalsFileName = 'download-counts-30-days-total.json';
38+
39+
Future<void> updload30DaysTotal(Map<String, int> counts) async {
40+
final reportsBucket =
41+
storageService.bucket(activeConfiguration.reportsBucketName!);
42+
await uploadBytesWithRetry(reportsBucket, downloadCounts30DaysTotalsFileName,
43+
jsonUtf8Encoder.convert(counts));
44+
}

app/lib/tool/neat_task/pub_dev_tasks.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'dart:io';
88
import 'package:gcloud/service_scope.dart' as ss;
99
import 'package:logging/logging.dart';
1010
import 'package:neat_periodic_task/neat_periodic_task.dart';
11+
import 'package:pub_dev/service/download_counts/compute_30_days_total_counts.dart';
1112

1213
import '../../account/backend.dart';
1314
import '../../account/consent_backend.dart';
@@ -187,6 +188,11 @@ void _setupGenericPeriodicTasks() {
187188
isRuntimeVersioned: false,
188189
task: syncDownloadCounts);
189190

191+
_daily(
192+
name: 'compute-download-counts-30-days-totals',
193+
isRuntimeVersioned: false,
194+
task: compute30DaysTotalTask);
195+
190196
_daily(name: 'count-topics', isRuntimeVersioned: false, task: countTopics);
191197

192198
_daily(
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
import 'dart:convert';
5+
6+
import 'package:basics/basics.dart';
7+
import 'package:gcloud/storage.dart';
8+
import 'package:pub_dev/service/download_counts/backend.dart';
9+
import 'package:pub_dev/service/download_counts/compute_30_days_total_counts.dart';
10+
import 'package:pub_dev/shared/configuration.dart';
11+
import 'package:test/test.dart';
12+
13+
import '../../shared/test_services.dart';
14+
15+
void main() {
16+
group('', () {
17+
testWithProfile('compute download counts 30 day totals', fn: () async {
18+
final pkg = 'foo';
19+
final versionsCounts = {
20+
'1.0.1': 2,
21+
'2.0.0-alpha': 2,
22+
'2.0.0': 2,
23+
'2.1.0': 2,
24+
'3.1.0': 2,
25+
'4.0.0-0': 2,
26+
'6.1.0': 2,
27+
};
28+
final date = DateTime.parse('1986-02-16');
29+
var downloadCounts1 = await downloadCountsBackend.updateDownloadCounts(
30+
pkg, versionsCounts, date);
31+
for (var i = 1; i < 5; i++) {
32+
downloadCounts1 = await downloadCountsBackend.updateDownloadCounts(
33+
pkg, versionsCounts, date.addCalendarDays(i));
34+
}
35+
36+
expect(compute30DayTotal(downloadCounts1), 70);
37+
38+
final pkg2 = 'bar';
39+
final versionsCounts2 = {
40+
'1.0.1': 3,
41+
'2.0.0-alpha': 3,
42+
'2.0.0': 3,
43+
'2.1.0': 3,
44+
'3.1.0': 3,
45+
'4.0.0-0': 3,
46+
'6.1.0': 3,
47+
};
48+
var downloadCounts2 = await downloadCountsBackend.updateDownloadCounts(
49+
pkg2, versionsCounts2, date);
50+
for (var i = 1; i < 5; i++) {
51+
downloadCounts2 = await downloadCountsBackend.updateDownloadCounts(
52+
pkg2, versionsCounts2, date.addCalendarDays(i));
53+
}
54+
55+
expect(compute30DayTotal(downloadCounts2), 105);
56+
57+
final pkg3 = 'baz';
58+
final versionsCounts3 = {
59+
'1.0.1': 4,
60+
'2.0.0-alpha': 4,
61+
'2.0.0': 4,
62+
'2.1.0': 4,
63+
'3.1.0': 4,
64+
'4.0.0-0': 4,
65+
'6.1.0': 4,
66+
};
67+
var downloadCounts3 = await downloadCountsBackend.updateDownloadCounts(
68+
pkg3, versionsCounts3, date);
69+
for (var i = 1; i < 5; i++) {
70+
downloadCounts3 = await downloadCountsBackend.updateDownloadCounts(
71+
pkg3, versionsCounts3, date.addCalendarDays(i));
72+
}
73+
expect(compute30DayTotal(downloadCounts3), 140);
74+
75+
final downloadCounts = [
76+
downloadCounts1,
77+
downloadCounts2,
78+
downloadCounts3
79+
];
80+
81+
final res = await compute30DayTotals(Stream.fromIterable(downloadCounts));
82+
83+
expect(
84+
res,
85+
{'foo': 70, 'bar': 105, 'baz': 140},
86+
);
87+
});
88+
89+
testWithProfile('succesful 30 day totals upload', fn: () async {
90+
await updload30DaysTotal({'foo': 70, 'bar': 105, 'baz': 140});
91+
92+
final data = await storageService
93+
.bucket(activeConfiguration.reportsBucketName!)
94+
.read(downloadCounts30DaysTotalsFileName)
95+
.transform(utf8.decoder)
96+
.transform(json.decoder)
97+
.single;
98+
99+
expect(data, {'foo': 70, 'bar': 105, 'baz': 140});
100+
});
101+
});
102+
}

0 commit comments

Comments
 (0)