Skip to content

Commit

Permalink
Add first implementation of Network of Terms provider (#63)
Browse files Browse the repository at this point in the history
Requires some more work, but is already functional. See also: https://coli-conc.gbv.de/publications/2023-09-12-SWIB23-Lightning-Talk.pdf
  • Loading branch information
stefandesu committed Sep 13, 2023
1 parent 3394b19 commit 5782cca
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/providers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import LocApiProvider from "./loc-api-provider.js"
import SkohubProvider from "./skohub-provider.js"
import LobidApiProvider from "./lobid-api-provider.js"
import MyCoReProvider from "./mycore-provider.js"
import NoTApiProvider from "./not-api-provider.js"

export {
BaseProvider,
Expand All @@ -24,4 +25,5 @@ export {
SkohubProvider,
LobidApiProvider,
MyCoReProvider,
NoTApiProvider,
}
182 changes: 182 additions & 0 deletions src/providers/not-api-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import BaseProvider from "./base-provider.js"
import * as errors from "../errors/index.js"
import { listOfCapabilities } from "../utils/index.js"
import axios from "axios"
import jskos from "jskos-tools"

/**
* TODOs:
* - [ ] Clean up conversion to JSKOS
* - [ ] Notations? (might be possible if NoT provided URI namespace)
* - [ ] Source languages (see https://github.com/netwerk-digitaal-erfgoed/network-of-terms/issues/1105)
* - [ ] Clean up GraphQL query strings
* - [ ] Better error handling
* - [ ] Implement getTop (if possible)
* - [ ] Implement getNarrower and getAncestors (already returned by getConcepts, but methods should be implemented nonetheless)
* - [ ] Decide on providerType URI
* - [ ] More testing required
*/

/**
* Add this entry to registries:
{
"provider": "NoTApi",
"uri": "http://coli-conc.gbv.de/registry/not-api",
"api": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/graphql",
"notation": [
"NoT"
]
}
*/

const cache = {
schemes: [],
}

export default class NoTApiProvider extends BaseProvider {

_prepare() {
this.has.schemes = true
this.has.top = false
this.has.data = true
this.has.concepts = true
this.has.narrower = false
this.has.ancestors = false
this.has.suggest = true
this.has.search = true
// Explicitly set other capabilities to false
listOfCapabilities.filter(c => !this.has[c]).forEach(c => {
this.has[c] = false
})
}

/**
* Used by `registryForScheme` (see src/lib/CocodaSDK.js) to determine a provider config for a concept schceme.
*
* @param {Object} options
* @param {Object} options.url API URL for server
* @returns {Object} provider configuration
*/
static _registryConfigForBartocApiConfig({ url } = {}) {
if (!url) {
return null
}
return {
api: url,
}
}

async getSchemes() {
if (!cache.schemes.length) {
const result = await axios.post(this._api.api, {
query: "query sources { sources { name uri description alternateName } }",
operationName: "sources",
})
const schemes = result?.data?.data?.sources || []
if (schemes.length) {
cache.schemes = schemes.map(scheme => {
const jskos = {
uri: scheme.uri,
prefLabel: { und: scheme.name },
}
if (scheme.desciption) {
jskos.description = { und: [scheme.description] }
}
if (scheme.alternateName) {
jskos.notation = [scheme.alternateName]
}
return jskos
})
} else {
return []
}
}
return cache.schemes
}

// async getTop() {
// }

async getConcepts({ concepts }) {
if (!concepts) {
throw new errors.InvalidOrMissingParameterError({ parameter: "concepts" })
}
if (!Array.isArray(concepts)) {
concepts = [concepts]
}
const result = await axios.post(this._api.api, {
query: `query { lookup( uris: [${concepts.map(c => `"${c.uri}"`)}], ) { uri source { ... on Source { uri } } result { ... on Term { uri prefLabel scopeNote altLabel broader { uri } narrower { uri } } } } }`,
})
return (result.data?.data?.lookup || []).map(entry => {
const concept = {
uri: entry.uri,
inScheme: [cache.schemes.find(scheme => jskos.compare(scheme, { uri: entry.source.uri }))],
}
if (entry.result?.prefLabel?.[0]) {
concept.prefLabel = { und: entry.result.prefLabel[0] }
}
if (entry.result?.altLabel?.[0]) {
concept.altLabel = { und: entry.result.altLabel }
}
if (entry.result?.scopeNote?.[0]) {
concept.scopeNote = { und: entry.result.scopeNote }
}
if (entry.result?.broader?.length) {
concept.broader = entry.result.broader
}
if (entry.result?.narrower?.length) {
concept.narrower = entry.result.narrower
}
return concept
})
}

// async getNarrower({ concept }) {
// }

// async getAncestors({ concept }) {
// }

async suggest(config) {
const search = config.search
const results = await this.search(config)
return [
search,
results.map(r => jskos.prefLabel(r, { fallbackToUri: false })),
[],
results.map(r => r.uri),
]
}

async search({ scheme, search }) {
if (!search) {
throw new errors.InvalidOrMissingParameterError({ parameter: "search" })
}
if (!scheme || !jskos.isContainedIn(scheme, cache.schemes)) {
throw new errors.InvalidOrMissingParameterError({ parameter: "scheme" })
}
const result = await axios.post(this._api.api, {
query: `query { terms( sources: ["${scheme.uri}"] query: "${search}" ) { source { uri } result { ... on Terms { terms { uri prefLabel scopeNote } } } }}`,
})
return (result.data?.data?.terms?.[0]?.result?.terms || []).map(concept => {
const jskos = {
uri: concept.uri,
inScheme: [scheme],
}
if (concept.prefLabel?.[0]) {
jskos.prefLabel = { und: concept.prefLabel[0] }
}
if (concept.altLabel?.[0]) {
jskos.altLabel = { und: concept.altLabel }
}
if (concept.scopeNote?.[0]) {
jskos.scopeNote = { und: concept.scopeNote[0] }
}
return jskos
})
}

}

NoTApiProvider.providerName = "NoTApi"
// NoTApiProvider.providerType = "http://bartoc.org/api-type/not"

0 comments on commit 5782cca

Please sign in to comment.