openvibers/openvibe.bench Commits

SEO-13: each listed kind is a Dataset for the JSONL the API serves (#2)

OP OpenVibers committed 15 days ago · 18655dc5c2 · 2 files, +78 −1

* Bench: each listed kind is a Dataset for the JSONL the API serves (SEO-13)

* Bench: the home page's JSON-LD is one script, one @graph (SEO-13)

Changes

ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex 52ba18c..f938b07 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -14,6 +14,34 @@ const views = require('../views');
const PUBLIC_CACHE = 'public, max-age=60';
++/**
++ * One Dataset node per kind the home lists. The dataset is the JSONL the API already serves for that kind, so its url
++ * and its download are the endpoint itself; the description restates the kind's own room, judge and entry type.
++ * Bench states no licence for the data, so no licence is claimed here.
++ */
++function datasetJsonLd(k, base) {
++ const contentUrl = `${base}/api/v1/datasets/${encodeURIComponent(k.id)}.jsonl`;
++ return {
++ '@context': 'https://schema.org',
++ '@type': 'Dataset',
++ name: k.name,
++ description: `${k.name}: a ${String(k.judge).replace(/_/g, ' ')} benchmark of ${String(k.entry).replace(/_/g, ' ')} entries from the ${k.room} room.`,
++ creator: { '@type': 'Organization', name: 'OpenVibe.Bench', url: base },
++ keywords: [k.id, k.room, k.judge, k.entry],
++ distribution: { '@type': 'DataDownload', contentUrl, encodingFormat: 'application/x-ndjson' },
++ url: contentUrl,
++ };
++}
++
++/**
++ * The page's JSON-LD in one script: every node in one @graph, so a page carries exactly one JSON-LD script.
++ * The nodes keep their own fields; only their own @context goes, because the graph carries it once.
++ */
++function oneGraph(nodes) {
++ const graph = nodes.filter(Boolean).map((n) => { const c = { ...n }; delete c['@context']; return c; });
++ return [{ '@context': 'https://schema.org', '@graph': graph }];
++}
++
function createPageRoutes(ctx) {
const { config, bench } = ctx;
const r = asyncRouter();
@@@ -26,7 +54,7 @@ function createPageRoutes(ctx) {
index: true, cache: PUBLIC_CACHE,
title: null,
description: 'OpenVibe.Bench is one AI benchmark and comparison engine for every room: blind votes, rubrics, graders, test suites and game outcomes, with reproducible pinned rounds and an open dataset per kind.',
-- jsonLd: homeJsonLd(config),
++ jsonLd: oneGraph([...homeJsonLd(config), ...kinds.map((k) => datasetJsonLd(k, base())), seo.jsonLd.organization()]),
body: raw(views.home({ kinds, counts: { kinds: kinds.length } })),
});
});
ddiff --git a/test/dataset-jsonld.test.js b/test/dataset-jsonld.test.js
nnew file mode 100644
iindex 0000000..9bc27b7
---- /dev/null
++++ b/test/dataset-jsonld.test.js
@@@ -0,0 +1,49 @@
++'use strict';
++/**
++ * The home page lists the kinds, so each kind it lists is a schema.org Dataset: the JSONL the API serves for that kind
++ * is the dataset itself (url and distribution), the description restates the kind's own room, judge and entry type, and
++ * no licence is claimed because Bench states none. /docs publishes no dataset, so it carries no Dataset node.
++ */
++const assert = require('assert');
++const { boot, check, done } = require('./helpers/boot');
++
++// One JSON-LD script per page: every node lives in the page's @graph.
++const scripts = (html) => [...String(html).matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1]));
++const nodes = (html, type) => scripts(html)
++ .flatMap((x) => (Array.isArray(x['@graph']) ? x['@graph'] : [x])).filter((x) => x['@type'] === type);
++
++(async () => {
++ const t = await boot();
++ let kinds;
++
++ await check('the home page carries one Dataset per kind, describing what the page lists', async () => {
++ const r = await t.get('/');
++ assert.strictEqual(r.status, 200);
++ kinds = (await t.get('/api/v1/kinds')).json().kinds;
++ assert.ok(kinds.length >= 2, `only ${kinds.length} kinds`);
++ assert.strictEqual(scripts(r.text).length, 1, 'the page carries exactly one JSON-LD script');
++ const sets = nodes(r.text, 'Dataset');
++ assert.deepStrictEqual(sets.map((d) => d.name), kinds.map((k) => k.name));
++ for (const [i, d] of sets.entries()) {
++ const k = kinds[i];
++ const url = `https://bench.openvibe.services/api/v1/datasets/${encodeURIComponent(k.id)}.jsonl`;
++ assert.strictEqual(d.url, url);
++ assert.strictEqual(d.distribution['@type'], 'DataDownload');
++ assert.strictEqual(d.distribution.contentUrl, url);
++ assert.strictEqual(d.creator.name, 'OpenVibe.Bench');
++ assert.ok(d.description.includes(k.room) && d.description.includes(k.name), d.description);
++ assert.ok(!('license' in d), 'no licence is claimed for data Bench does not license');
++ assert.ok(r.text.includes(`href="/api/v1/boards/${k.id}?type=elo"`), `${k.id} is not listed on the page`);
++ }
++ });
++
++ await check('/docs publishes no dataset and therefore carries no Dataset node', async () => {
++ const r = await t.get('/docs');
++ assert.strictEqual(r.status, 200);
++ assert.deepStrictEqual(nodes(r.text, 'Dataset'), []);
++ });
++
++ await check('stop', async () => { await t.close(); });
++
++ done();
++})();