From 77634fb009e740ac3453ca0a0ae9ebcef99e4949 Mon Sep 17 00:00:00 2001 From: Chris Wilkinson Date: Fri, 28 Jun 2024 12:24:27 +0100 Subject: [PATCH] Allow the page to be filtered by OpenAlex Domain Refs #13 --- src/data/openalex-domains.json.ts | 35 +++++++++++++++++++++++++++++++ src/requests.md | 25 +++++++++++++++++----- 2 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 src/data/openalex-domains.json.ts diff --git a/src/data/openalex-domains.json.ts b/src/data/openalex-domains.json.ts new file mode 100644 index 0000000..0f4f01a --- /dev/null +++ b/src/data/openalex-domains.json.ts @@ -0,0 +1,35 @@ +import { HttpClient, Terminal } from '@effect/platform' +import { NodeTerminal } from '@effect/platform-node' +import { Schema } from '@effect/schema' +import { Effect, Record } from 'effect' +import { DomainIdFromUrlSchema, DomainIdSchema } from '../lib/OpenAlex.js' +import { UrlFromStringSchema } from '../lib/Url.js' + +const Domains = Schema.Struct({ + results: Schema.Array( + Schema.Struct({ + id: Schema.compose(UrlFromStringSchema, DomainIdFromUrlSchema), + display_name: Schema.String, + }), + ), +}) + +const DomainNames = Schema.Record(DomainIdSchema, Schema.String) + +const program = Effect.gen(function* () { + const terminal = yield* Terminal.Terminal + + const request = HttpClient.request.get('https://api.openalex.org/domains?per-page=200') + + const data = yield* HttpClient.client + .fetchOk(request) + .pipe(Effect.andThen(HttpClient.response.schemaBodyJson(Domains)), Effect.scoped) + + const transformedData = Record.fromIterableWith(data.results, domain => [domain.id, domain.display_name]) + + const encoded = yield* Schema.encode(Schema.parseJson(DomainNames))(transformedData) + + yield* terminal.display(encoded) +}) + +await Effect.runPromise(program.pipe(Effect.provide(NodeTerminal.layer))) diff --git a/src/requests.md b/src/requests.md index 7ae8d58..34b85e9 100644 --- a/src/requests.md +++ b/src/requests.md @@ -15,6 +15,7 @@ const requests = FileAttachment('./data/requests.json') .json() .then(data => data.map(request => ({ ...request, timestamp: parseTimestamp(request.timestamp) }))) +const openAlexDomains = FileAttachment('./data/openalex-domains.json').json() const openAlexFields = FileAttachment('./data/openalex-fields.json').json() ``` @@ -23,6 +24,19 @@ const now = new Date() const firstRequest = d3.min(requests, request => request.timestamp) ``` +```js +const chosenDomain = view( + Inputs.select([null, ...Object.keys(openAlexDomains)], { + label: 'Domain', + format: domain => (domain ? openAlexDomains[domain] : 'All domains'), + }), +) +``` + +```js +const requestsSelected = chosenDomain ? requests.filter(d => d.domains.includes(chosenDomain)) : requests +``` + ```js const languageColor = Plot.scale({ color: { @@ -41,15 +55,16 @@ const languageColor = Plot.scale({ }, }) -const requestsByField = requests.flatMap(({ fields, ...request }) => - fields.map(field => ({ ...request, field: openAlexFields[field].name })), -) +const requestsByField = requestsSelected + .flatMap(({ fields, ...request }) => fields.map(field => ({ ...request, field }))) + .filter(request => (chosenDomain ? openAlexFields[request.field].domain === chosenDomain : true)) + .map(({ field, ...request }) => ({ ...request, field: openAlexFields[field].name })) ```

Requests

- ${requests.length.toLocaleString("en-US")} + ${requestsSelected.length.toLocaleString("en-US")}
@@ -68,7 +83,7 @@ function requestsByLanguageTimeline({ width } = {}) { x: { label: '', domain: [d3.utcSunday.floor(firstRequest), d3.utcSunday.ceil(now)] }, marks: [ Plot.rectY( - requests, + requestsSelected, Plot.binX( { y: 'count' }, {