Skip to content

Commit 46e32c2

Browse files
committed
feat: expose function for loading local reports from a directory
This function will be useful for advanced users of web-codegen-scorer results. Advanced users may build tooling around local reports and want to fetch them from a local directory.
1 parent 724d5fe commit 46e32c2

File tree

3 files changed

+57
-43
lines changed

3 files changed

+57
-43
lines changed

report-app/report-server.ts

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@ import {
44
isMainModule,
55
writeResponseToNodeResponse,
66
} from '@angular/ssr/node';
7-
import { glob } from 'tinyglobby';
87
import express from 'express';
9-
import { readFile } from 'node:fs/promises';
108
import { dirname, isAbsolute, join, resolve } from 'node:path';
119
import { fileURLToPath } from 'node:url';
10+
import {
11+
FetchedLocalReports,
12+
fetchReportsFromDisk,
13+
} from '../runner/reporting/report-local-disk';
1214

1315
const app = express();
1416
const reportsLoaderPromise = getReportLoader();
1517
const options = getOptions();
1618
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
1719
const browserDistFolder = resolve(serverDistFolder, '../browser');
1820
const angularApp = new AngularNodeAppEngine();
19-
let localDataPromise: Promise<LocalData> | null = null;
21+
let localDataPromise: Promise<FetchedLocalReports> | null = null;
2022

2123
// Endpoint for fetching all available report groups.
2224
app.get('/api/reports', async (_, res) => {
@@ -26,8 +28,8 @@ app.get('/api/reports', async (_, res) => {
2628
]);
2729
const results = remoteGroups.slice();
2830

29-
for (const [, group] of localData) {
30-
results.unshift(group.overview);
31+
for (const [, data] of localData) {
32+
results.unshift(data.group);
3133
}
3234

3335
res.json(results);
@@ -77,14 +79,6 @@ interface ReportLoader {
7779
getGroupsList: () => Promise<{ id: string }[]>;
7880
}
7981

80-
type LocalData = Map<
81-
string,
82-
{
83-
overview: { id: string };
84-
run: { group: string };
85-
}
86-
>;
87-
8882
/** Gets the server options from the command line. */
8983
function getOptions() {
9084
const defaultPort = 4200;
@@ -135,37 +129,9 @@ async function getReportLoader() {
135129
async function resolveLocalData(directory: string) {
136130
// Reuse the same promise so that concurrent requests get the same response.
137131
if (!localDataPromise) {
138-
let resolveFn: (data: LocalData) => void;
132+
let resolveFn: (data: FetchedLocalReports) => void;
139133
localDataPromise = new Promise((resolve) => (resolveFn = resolve));
140-
141-
const data: LocalData = new Map();
142-
const groupFiles = await glob('**/groups.json', {
143-
cwd: directory,
144-
absolute: true,
145-
});
146-
147-
await Promise.all(
148-
// Note: sort the groups so that the indexes stay consistent no matter how the files
149-
// appear on disk. It appears to be non-deterministic when using the async glob.
150-
groupFiles.sort().map(async (configPath, index) => {
151-
const [groupContent, runContent] = await Promise.all([
152-
readFile(configPath, 'utf8'),
153-
readFile(join(dirname(configPath), 'summary.json'), 'utf8'),
154-
]);
155-
156-
// Note: Local reports only have one group.
157-
const overview = (JSON.parse(groupContent) as { id: string }[])[0];
158-
const run = JSON.parse(runContent) as { group: string };
159-
160-
// Local runs should not be grouped by their group ID, but rather if they
161-
// were part of the same invocation. Add a unique suffix to the ID to
162-
// prevent further grouping.
163-
run.group = overview.id = `${overview.id}-l${index}`;
164-
data.set(overview.id, { overview, run });
165-
})
166-
);
167-
168-
resolveFn!(data);
134+
resolveFn!(await fetchReportsFromDisk(directory));
169135
}
170136

171137
return localDataPromise;

runner/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ export { getRunnerByName, type RunnerName } from './codegen/runner-creation.js';
2929
export { getEnvironmentByPath } from './configuration/environment-resolution.js';
3030
export { type Environment } from './configuration/environment.js';
3131
export { autoRateFiles } from './ratings/autoraters/rate-files.js';
32+
export { fetchReportsFromDisk } from './reporting/report-local-disk.js';
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { readFile } from 'node:fs/promises';
2+
import { dirname, join } from 'node:path';
3+
import { RunGroup, RunInfo } from '../shared-interfaces.js';
4+
import { glob } from 'tinyglobby';
5+
6+
/** Type describing a map from group report IDs to their runs. */
7+
export type FetchedLocalReports = Map<
8+
/* groupId */ string,
9+
{
10+
group: RunGroup;
11+
run: RunInfo;
12+
}
13+
>;
14+
15+
/** Fetches local report data from the given directory. */
16+
export async function fetchReportsFromDisk(
17+
directory: string
18+
): Promise<FetchedLocalReports> {
19+
const data: FetchedLocalReports = new Map();
20+
const groupFiles = await glob('**/groups.json', {
21+
cwd: directory,
22+
absolute: true,
23+
});
24+
25+
await Promise.all(
26+
// Note: sort the groups so that the indexes stay consistent no matter how the files
27+
// appear on disk. It appears to be non-deterministic when using the async glob.
28+
groupFiles.sort().map(async (configPath, index) => {
29+
const [groupContent, runContent] = await Promise.all([
30+
readFile(configPath, 'utf8'),
31+
readFile(join(dirname(configPath), 'summary.json'), 'utf8'),
32+
]);
33+
34+
// Note: Local reports only have one group.
35+
const group = (JSON.parse(groupContent) as RunGroup[])[0];
36+
const run = JSON.parse(runContent) as RunInfo;
37+
38+
// Local runs should not be grouped by their group ID, but rather if they
39+
// were part of the same invocation. Add a unique suffix to the ID to
40+
// prevent further grouping.
41+
run.group = group.id = `${group.id}-l${index}`;
42+
data.set(group.id, { group, run });
43+
})
44+
);
45+
46+
return data;
47+
}

0 commit comments

Comments
 (0)