Download api.mjs from Hugdrigo/scholarlens-research-intelligence: direct link, hf CLI and curl.
- Browser
- Download file 3.38 kB
-
https://huggingface.co/spaces/Hugdrigo/scholarlens-research-intelligence/resolve/main/api.mjs
- Command line
-
hf download hf://spaces/Hugdrigo/scholarlens-research-intelligence/api.mjs
-
curl -L -o api.mjs https://huggingface.co/spaces/Hugdrigo/scholarlens-research-intelligence/resolve/main/api.mjs
3.38 kB
| /* External academic metadata adapters. Public metadata is not paper full text. */ | |
| import {cleanText,normalizeDoi,safeURL,dedupePapers,isValidYearRange} from './core.mjs'; | |
| const arr=v=>Array.isArray(v)?v:[]; | |
| const yearFromCrossref=w=>{for(const k of ['published','issued','created']){const y=w?.[k]?.['date-parts']?.[0]?.[0];if(Number.isInteger(y))return y}return null}; | |
| export function fromCrossref(w){const doi=normalizeDoi(w.DOI);return {title:cleanText(arr(w.title)[0]||w.title||''),year:yearFromCrossref(w),authors:arr(w.author).map(a=>cleanText([a.given,a.family].filter(Boolean).join(' '),150)).filter(Boolean).slice(0,15),venue:cleanText(arr(w['container-title'])[0]||'',160),doi,citedBy:Number.isFinite(w['is-referenced-by-count'])?w['is-referenced-by-count']:null,source:'Crossref',url:safeURL(doi?`https://doi.org/${doi}`:w.URL),notes:'',keywords:'',type:cleanText(w.type||'',80)} } | |
| export function fromOpenAlex(w){const doi=normalizeDoi(w.doi),venue=cleanText(w.primary_location?.source?.display_name||'',160);return {title:cleanText(w.display_name||w.title||''),year:Number(w.publication_year)||null,authors:arr(w.authorships).map(a=>cleanText(a.author?.display_name||'',150)).filter(Boolean).slice(0,15),venue,doi,citedBy:Number.isFinite(w.cited_by_count)?w.cited_by_count:null,source:'OpenAlex',url:safeURL(doi?`https://doi.org/${doi}`:w.id),notes:'',keywords:arr(w.keywords).map(k=>k.display_name).slice(0,5).join(', '),type:cleanText(w.type||'',80)} } | |
| export function buildSearchURL(provider,query,start,end,limit=20){if(!cleanText(query,300))throw new Error('Enter a research topic first.');if(!isValidYearRange(start,end))throw new Error('Check the publication years.');const n=Math.max(1,Math.min(40,Number(limit)||20));if(provider==='OpenAlex'){const u=new URL('https://api.openalex.org/works');u.searchParams.set('search',query.trim());u.searchParams.set('per_page',String(n));u.searchParams.set('filter',`from_publication_date:${start}-01-01,to_publication_date:${end}-12-31`);return u.href}if(provider==='Crossref'){const u=new URL('https://api.crossref.org/works');u.searchParams.set('query.bibliographic',query.trim());u.searchParams.set('rows',String(n));u.searchParams.set('filter',`from-pub-date:${start}-01-01,until-pub-date:${end}-12-31`);return u.href}throw new Error('Unsupported provider');} | |
| export async function searchPapers(provider,query,start,end,limit=20,fetcher=fetch){const url=buildSearchURL(provider,query,start,end,limit);const controller=new AbortController();const timer=setTimeout(()=>controller.abort(),16000);try{const resp=await fetcher(url,{headers:{Accept:'application/json'},signal:controller.signal});if(!resp.ok)throw new Error(`${provider} returned HTTP ${resp.status}`);const raw=await resp.json();let data=provider==='OpenAlex'?arr(raw.results).map(fromOpenAlex):arr(raw.message?.items).map(fromCrossref);return {papers:dedupePapers(data.filter(p=>p.title)),url,provider,metadataOnly:true}}finally{clearTimeout(timer)}} | |
| export async function lookupDOI(doi,fetcher=fetch){const val=normalizeDoi(doi);if(!val)throw new Error('Enter a valid DOI, for example 10.1038/nature12373');const url='https://api.crossref.org/works/'+encodeURIComponent(val);const r=await fetcher(url,{headers:{Accept:'application/json'}});if(!r.ok)throw new Error('DOI lookup returned '+r.status);const j=await r.json();return fromCrossref(j.message)} | |