mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix: say when the camera catalog serves only part of a region
Camera packs keep their nearest cameras and the catalog cap thins them further, so London answered 250 cameras while TfL offers many more. The catalog now records each pack's full count, tags cameras with their pack, and serves the packs it trimmed; find_cctv_cameras names them.
This commit is contained in:
1 parent
e779432925
commit
66b07418bd
6 files changed
+117
-11
No files matched your search
@@ -128,6 +128,10 @@ test('the catalog applies the fair cap to configured packs end to end', async ()
|
||||
sources.slice(0, 3).map((s) => s.id),
|
||||
['env-0', 'env-1', 'env-2'],
|
||||
);
|
||||
assert.equal(sources[0].pack, 'env');
|
||||
assert.deepEqual(getSources.trimmedPacks(), [
|
||||
{ pack: 'env', available: 12, served: 9 },
|
||||
]);
|
||||
} finally {
|
||||
for (const key of Object.keys(process.env)) {
|
||||
if (!(key in saved)) delete process.env[key];
|
||||
@@ -135,3 +139,19 @@ test('the catalog applies the fair cap to configured packs end to end', async ()
|
||||
Object.assign(process.env, saved);
|
||||
}
|
||||
});
|
||||
|
||||
test('a pack trimmed to its nearest cameras reports how many it offered', async () => {
|
||||
const { prioritizeSources } =
|
||||
await import('../../server/providers/cctv/normalize.js');
|
||||
const cameras = Array.from({ length: 5 }, (_, i) => ({
|
||||
id: `c${i}`,
|
||||
lat: i,
|
||||
lon: 0,
|
||||
}));
|
||||
const kept = prioritizeSources(cameras, 2, [{ lat: 0, lon: 0 }]);
|
||||
assert.deepEqual(
|
||||
kept.map((camera) => camera.id),
|
||||
['c0', 'c1'],
|
||||
);
|
||||
assert.equal(kept.available, 5);
|
||||
});
|
||||
@@ -17,12 +17,22 @@ const round = (value, digits) =>
|
||||
Number.isFinite(value) ? Number(value.toFixed(digits)) : null;
|
||||
const text = (value) => (typeof value === 'string' && value ? value : null);
|
||||
|
||||
async function readCameras(services, signal) {
|
||||
async function readCatalog(services, signal) {
|
||||
const payload = await services.cctv.getCatalog({ signal });
|
||||
return payload.sources.filter(
|
||||
(camera) =>
|
||||
camera?.id && Number.isFinite(camera.lat) && Number.isFinite(camera.lon),
|
||||
);
|
||||
return {
|
||||
cameras: payload.sources.filter(
|
||||
(camera) =>
|
||||
camera?.id &&
|
||||
Number.isFinite(camera.lat) &&
|
||||
Number.isFinite(camera.lon),
|
||||
),
|
||||
// Regional packs the catalog serves only part of, nearest their centers.
|
||||
trimmed: Array.isArray(payload.trimmedPacks) ? payload.trimmedPacks : [],
|
||||
};
|
||||
}
|
||||
|
||||
async function readCameras(services, signal) {
|
||||
return (await readCatalog(services, signal)).cameras;
|
||||
}
|
||||
|
||||
export const findCctvCameras = defineTool({
|
||||
@@ -41,8 +51,13 @@ export const findCctvCameras = defineTool({
|
||||
async run(args, { services, signal }) {
|
||||
const area = await resolveArea(args.area, { services, signal });
|
||||
const center = areaCenter(area);
|
||||
const rows = (await readCameras(services, signal))
|
||||
.filter((camera) => areaContains(area, camera))
|
||||
const catalog = await readCatalog(services, signal);
|
||||
const found = catalog.cameras.filter((camera) =>
|
||||
areaContains(area, camera),
|
||||
);
|
||||
const packs = new Set(found.map((camera) => camera.pack).filter(Boolean));
|
||||
const trimmed = catalog.trimmed.filter((entry) => packs.has(entry.pack));
|
||||
const rows = found
|
||||
.map((camera) => ({
|
||||
id: camera.id,
|
||||
name: text(camera.name),
|
||||
@@ -57,8 +72,21 @@ export const findCctvCameras = defineTool({
|
||||
}))
|
||||
.sort((a, b) => a.distance_km - b.distance_km);
|
||||
return {
|
||||
summary: `${countNoun(rows.length, 'public camera')} in ${area.label}.`,
|
||||
data: capRows(rows, args.limit),
|
||||
summary:
|
||||
`${countNoun(rows.length, 'public camera')} in ${area.label}` +
|
||||
(trimmed.length
|
||||
? ` (the catalog serves only some cameras here: ${trimmed
|
||||
.map(
|
||||
(entry) =>
|
||||
`${entry.served} of ${entry.available} from ${entry.pack}`,
|
||||
)
|
||||
.join(', ')}).`
|
||||
: '.'),
|
||||
data: {
|
||||
...capRows(rows, args.limit),
|
||||
complete: trimmed.length === 0,
|
||||
catalog_trimmed: trimmed,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -246,3 +246,39 @@ test('a stale or degraded radio directory is reported', async () => {
|
||||
);
|
||||
assert.equal(result.data.degraded, true);
|
||||
});
|
||||
|
||||
test('cameras from a trimmed catalog pack are reported as partial', async () => {
|
||||
const catalog = composeCatalog({
|
||||
tools: coreTools,
|
||||
services: {
|
||||
cctv: {
|
||||
getCatalog: async () => ({
|
||||
trimmedPacks: [
|
||||
{ pack: 'tfl', available: 900, served: 250 },
|
||||
{ pack: 'other', available: 10, served: 5 },
|
||||
],
|
||||
sources: [
|
||||
{
|
||||
id: 'jam-1',
|
||||
name: 'Strand',
|
||||
lat: 51.51,
|
||||
lon: -0.12,
|
||||
pack: 'tfl',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
const result = await catalog.call('find_cctv_cameras', {
|
||||
area: { lat: 51.51, lon: -0.12, radius_km: 5 },
|
||||
});
|
||||
assert.equal(result.data.complete, false);
|
||||
assert.deepEqual(result.data.catalog_trimmed, [
|
||||
{ pack: 'tfl', available: 900, served: 250 },
|
||||
]);
|
||||
assert.match(
|
||||
result.summary,
|
||||
/\(the catalog serves only some cameras here: 250 of 900 from tfl\)\.$/,
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user