Skip to content

Commit

Permalink
Merge branch 'gh-pages' of https://github.com/LivelyKernel/lively4-core
Browse files Browse the repository at this point in the history
… into gh-pages
  • Loading branch information
onsetsu committed Mar 5, 2025
2 parents 4ec7fb1 + 0063ac4 commit f030a37
Show file tree
Hide file tree
Showing 10 changed files with 2,062 additions and 29 deletions.
112 changes: 110 additions & 2 deletions src/client/literature.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {pt} from "src/client/graphics.js"
import toTitleCase from "src/external/title-case.js"
import moment from "src/external/moment.js"

import Preferences from 'src/client/preferences.js'

function specialInspect(target, contentNode, inspector, normal) {
inspector.renderObjectdProperties(contentNode, target)
Expand Down Expand Up @@ -113,6 +114,11 @@ export class Paper {
}

static async getId(id, optionalEntity) {
if (Preferences.get("UseOpenAlex")) {
var json = await fetch("alex://data/" + id).then(r => r.json())
return new AlexPaper(json)
}

var paper = this.byId(id)
if (paper) return paper
if (optionalEntity) {
Expand Down Expand Up @@ -236,11 +242,13 @@ export class Paper {
entryTags: {
author: this.authors.map(author => author.name).join(" and "),
title: this.title,
year: this.year,
scholarid: this.scholarid,
year: this.year
},
entryType: this.bibtexType
}

if (this.scholarid) entry.entryTags.scholarid = this.scholarid
if (this.alexid) entry.entryTags.alexid = this.alexid
if (this.booktitle) { entry.entryTags.booktitle = this.booktitle }
if (this.doi) { entry.entryTags.doi = this.doi }

Expand Down Expand Up @@ -430,8 +438,95 @@ export class Paper {
}


export class AlexAuthor {

constructor(value) {
this.value = value
}

get name() {
return this.value.author.display_name // "Original author name"
}

get id() {
return this.value.author.id
}

livelyInspect(contentNode, inspector, normal) {
specialInspect(this, contentNode, inspector, normal)
}
}

export class AlexPaper extends Paper {

get alexid() {
return this.value.id.replace("https://openalex.org/","")
}


get authors() {
return (this.value.authorships || []).map(ea => new AlexAuthor(ea))
}


get year() {
return this.value.publication_year
}

get doi() {
return this.value.doi && this.value.doi.replace("https://doi.org/","")
}


get bibtexType() {
// https://docs.openalex.org/api-entities/works/work-object#type
var type = this.value.type
switch(type) {
case "article": return "article"; // TODO distinguish conference article from journal?
case "book-chapter": return "article";
case "dataset": return "misc"
case "preprint": return "misc"
case "dissertation": return "phdthesis";
case "book": return "book";
case "review": return "misc"
case "paratext":return "misc"
case "libguides":return "misc"
case "letter":return "misc"
case "other":return "misc"
case "reference-entry":return "misc"
case "report":return "misc"
case "editorial":return "misc"
case "peer-review":return "misc"
case "erratum":return "misc"
case "standard":return "misc"
case "grant":return "misc"
case "supplementary-materials":return "misc"
}
return "misc"
}

get booktitle() {
var source = this.value.primary_location.source
if (source && source.display_name) {
return source.display_name
}
return ""
}

get keywords() {
return [] // #TODO
}




}

export default class Literature {

static useOpenAlex() {
return Preferences.get("UseOpenAlex")
}

static async ensureCache() {
if (this.isLoadingCache) {
Expand Down Expand Up @@ -573,6 +668,19 @@ export default class Literature {

return db
}

static get alexdb() {
var db = new Dexie("openalex");

db.version(1).stores({
papers: 'alexid,doi,authors,year,title,key,keywords,booktitle',
}).upgrade(function () {
})


return db
}

}


Expand Down
2 changes: 2 additions & 0 deletions src/client/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export default class Preferences {
AIShadowText: {default: false, short: "complete w/ shadow text (key required)"},
AILukasExperiment: {default: false, short: "AI Lukas Experiment"},
DisableBabelCaching: {default: false, short: "Disable babel transpile caching"},
SemanticScholarAuth: {default: false, short: "use Semantic Scholar API key"},
UseOpenAlex: {default: true, short: "use OpenAlex for Literature"},
}
}

Expand Down
118 changes: 118 additions & 0 deletions src/client/protocols/alex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Scheme } from "src/client/poid.js";
import PolymorphicIdentifier from "src/client/poid.js";
import focalStorage from "src/external/focalStorage.js";

import {Author, Paper} from "src/client/literature.js"

import Preferences from 'src/client/preferences.js';


import _ from 'src/external/lodash/lodash.js';
/*MD
# Alex Scholar API
MD*/``


export default class OpenAlexScheme extends Scheme {

get scheme() {
return "alex";
}

resolve() {
return true;
}

response(content, contentType = "text/html") {
return new Response(content, {
headers: {
"content-type": contentType
},
status: 200
});
}

notfound(content, contentType = "text/html") {
return new Response(content, {
headers: {
"content-type": contentType
},
status: 303
});
}


get baseURL() {
return "https://api.openalex.org/"
}


async GET(options) {
var m = this.url.match(new RegExp(this.scheme + "\:\/\/([^/]*)/(.*)"))
var mode = m[1]
var query = m[2];
if (query.length < 2) return this.response(`{"error": "query to short"}`);

if (mode === "browse") {
if (query.match(/W.*/)) {
let id = query.replace(/.*\//,"")
return this.response(`<literature-paper alexid="${id}"><literature-paper>`);
}
}


var url = this.baseURL + query

var headers = new Headers({})
var content = await fetch(url, {
method: "GET",
headers: headers
}).then(r => r.text())

if (mode === "browse") {
var json = JSON.parse(content)
content = JSON.stringify(json, undefined, 2)
}

return this.response(content);
}

async POST(options) {
// #TODO get rid of duplication with GET
var m = this.url.match(new RegExp(this.scheme + "\:\/\/([^/]*)/(.*)"))
var mode = m[1]
var query = m[2];
if (query.length < 2) return this.response(`{"error": "query to short"}`);

var url = this.baseURL + query


var headers = new Headers({})
var content = await fetch(url, {
method: "POST",
headers: headers,
body: options.body
}).then(r => r.text())

return this.response(content);
}


async OPTIONS(options) {
var content = JSON.stringify({}, undefined, 2);
return new Response(content, {
headers: {
"content-type": "application/json"
},
status: 200
});
}

}

PolymorphicIdentifier.register(OpenAlexScheme);

// import Tracing from "src/client/tracing.js"
// Tracing.traceClass(Paper)
31 changes: 20 additions & 11 deletions src/client/protocols/scholar.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import focalStorage from "src/external/focalStorage.js";

import {Author, Paper, MicrosoftAcademicEntities} from "src/client/literature.js"

import Preferences from 'src/client/preferences.js';


import _ from 'src/external/lodash/lodash.js';
/*MD
Expand Down Expand Up @@ -113,26 +115,31 @@ export default class SemanticScholarScheme extends Scheme {
} else if (query.match("author/")) {
var authorId = query.replace(/.*author\//,"")
return this.response(`<literature-paper authorid="${authorId}"><literature-paper>`);
} else {
return this.response(`query not supported: ` + query);
}
}


}

var url = this.baseURL + query

var key = await SemanticScholarScheme.ensureSubscriptionKey() // maybe only get... ?

var headers = new Headers({})
if (key) {
headers.set("x-api-key", key)
if (Preferences.get("SemanticScholarAuth")) {
var key = await SemanticScholarScheme.ensureSubscriptionKey() // maybe only get... ?
if (key) {
headers.set("x-api-key", key)
}
}

var content = await fetch(url, {
method: "GET",
headers: headers
}).then(r => r.text())

if (mode === "browse") {
var json = JSON.parse(content)
content = JSON.stringify(json, undefined, 2)
}

return this.response(content);
}

Expand All @@ -156,11 +163,13 @@ fetch("scholar://data/paper/batch?fields=referenceCount,citationCount,title", {
if (query.length < 2) return this.response(`{"error": "query to short"}`);

var url = this.baseURL + query

var key = await SemanticScholarScheme.ensureSubscriptionKey() // maybe only get... ?

var headers = new Headers({})
if (key) {
headers.set("x-api-key", key)
if (Preferences.get("SemanticScholarAuth")) {
var key = await SemanticScholarScheme.ensureSubscriptionKey() // maybe only get... ?
if (key) {
headers.set("x-api-key", key)
}
}

var content = await fetch(url, {
Expand Down
Loading

0 comments on commit f030a37

Please sign in to comment.