openvibers/openvibe.bench Commits
SEO-13: each listed kind is a Dataset for the JSONL the API serves (#2)
Changes
@@@ -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 } })),});});nnew file mode 100644@@@ -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();++})();
@@ -14,6 +14,34 @@ const views = require('../views');
@@ -14,6 +14,34 @@ const views = require('../views');
const PUBLIC_CACHE = 'public, max-age=60';
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) {
function createPageRoutes(ctx) {
const { config, bench } = ctx;
const { config, bench } = ctx;
const r = asyncRouter();
const r = asyncRouter();
@@ -26,7 +54,7 @@ function createPageRoutes(ctx) {
@@ -26,7 +54,7 @@ function createPageRoutes(ctx) {
index: true, cache: PUBLIC_CACHE,
index: true, cache: PUBLIC_CACHE,
title: null,
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.',
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 } })),
body: raw(views.home({ kinds, counts: { kinds: kinds.length } })),
});
});
});
});
new file mode 100644
new file mode 100644
@@ -0,0 +1,49 @@
@@ -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();
+})();