Skip to content

Commit 410cd61

Browse files
authored
Merge pull request #29 from nbonfils/url-copus
Allow download of corpus from OpenAlex API URL
2 parents ff579f6 + 3065fa5 commit 410cd61

File tree

3 files changed

+224
-153
lines changed

3 files changed

+224
-153
lines changed

src/index.html

+181-151
Original file line numberDiff line numberDiff line change
@@ -66,166 +66,196 @@ <h1 class="center" >Bibliograph 2</h1>
6666
min="1"
6767
max="10000"
6868
x-model="maxWorks" />
69-
<span>most cited works</span>
69+
<span>works.</span>
7070
</div>
7171
<br />
72-
<form @submit.prevent="
73-
loading('corpus', 'Fetching corpus from OpenAlex API');
74-
({count, works} = await fetchWorks(params, maxWorks));
75-
done();" >
76-
<div class="card">
77-
<template x-for="(param, index) in params">
78-
<div>
79-
<label :for="`type-${index}`" >
80-
Type:
81-
</label>
82-
<select :id="`type-${index}`" x-model="param.type">
83-
<option value="date" >Filter by date range</option>
84-
<option value="title" >Search in title</option>
85-
<option value="titleabs" >Search in title and abstract</option>
86-
<option value="titleabsfull" >Search in title, abstract and full text</option>
87-
<option value="concept" >Filter by concepts</option>
88-
<option value="topic" >Filter by topics</option>
89-
</select>
90-
<button type="button" @click.prevent="params.splice(index, 1)">X</button>
91-
92-
<template x-if="param.type === 'date'">
93-
<div>
94-
<span>From</span>
95-
<input
96-
:id="`from-${index}`"
97-
class="card"
98-
placeholder="Enter a year. E.g. 2008"
99-
type="number"
100-
x-model="param.fromYear" />
101-
<span>to</span>
102-
<input
103-
:id="`to-${index}`"
104-
class="card"
105-
placeholder="Enter a year. E.g. 2008"
106-
type="number"
107-
x-model="param.toYear" />
108-
</div>
109-
</template>
110-
<template x-if="param.type === 'title'">
111-
<div>
112-
<input :id="`value-${index}`"
113-
class="card w-100"
114-
placeholder="Search for words in works' title"
115-
type="search"
116-
x-model="param.value" />
117-
<a href="https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/search-entities#boolean-searches">Note that you can use boolean search in this field</a>
118-
</div>
119-
</template>
120-
<template x-if="param.type === 'titleabs'">
121-
<div>
122-
<input :id="`value-${index}`"
123-
class="card w-100"
124-
placeholder="Search for words in works' title and abstract"
125-
type="search"
126-
x-model="param.value" />
127-
<a href="https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/search-entities#boolean-searches">Note that you can use boolean search in this field</a>
128-
</div>
129-
</template>
130-
<template x-if="param.type === 'titleabsfull'">
72+
<div x-data="{tab: 'search'}" >
73+
<button class="btn" :class="tab === 'search' && 'primary'" @click="tab = 'search'">Search Interface</button>
74+
<button class="btn" :class="tab === 'url' && 'primary'" @click="tab = 'url'">OpenAlex API URL</button>
75+
<template x-if="tab === 'url'">
76+
<form @submit.prevent="
77+
loading('corpus', 'Fetching corpus from OpenAlex API');
78+
works = await fetchWorksFromUrl(apiUrl, maxWorks);
79+
done();
80+
loading('filters', 'Processing corpus data');
81+
setTimeout(() => {
82+
data = processWorks(works);
83+
filters = getFilters(data);
84+
done();
85+
}, 10);" >
86+
<textarea id="apiurl"
87+
class="card w-100"
88+
placeholder="Paste the OpenAlex API URL here..."
89+
type="text"
90+
x-model="apiUrl"
91+
@input="checkApiUrl(apiUrl) ? $el.setCustomValidity('') : $el.setCustomValidity('URL is invalid, it needs to look like: https://api.openalex.org/works?...');"
92+
required ></textarea>
93+
<button class="btn center-btn primary"
94+
style="margin-top: 0.5em;">
95+
Fetch Corpus
96+
</button>
97+
</form>
98+
</template>
99+
<template x-if="tab === 'search'">
100+
<form @submit.prevent="
101+
loading('corpus', 'Fetching corpus from OpenAlex API');
102+
({count, works} = await fetchWorks(params, maxWorks));
103+
done();" >
104+
<div class="card">
105+
<template x-for="(param, index) in params">
131106
<div>
132-
<input :id="`value-${index}`"
133-
class="card w-100"
134-
placeholder="Search for words in works' title, abstract and full-text"
135-
type="search"
136-
x-model="param.value" />
137-
<a href="https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/search-entities#boolean-searches">Note that you can use boolean search in this field</a>
138-
</div>
139-
</template>
140-
<template x-if="param.type === 'concept'">
141-
<div x-data="{ ac: null, str: '' }"
142-
x-init="param.concepts = param.concepts || [];
143-
param.op = param.op || 'or';
144-
$nextTick(() => ac = new autoComplete({selector: `#search-${index}`, ...autoCompleteConceptConfig}))">
145-
<input :id="`search-${index}`"
146-
class="card w-100"
147-
type="search"
148-
x-model="str"
149-
@selection="param.concepts.push($event.detail.selection.value); str='';" />
150-
<input :id="`radio-or-${index}`"
151-
:name="`radio-${index}`"
152-
class="concept-radio-or"
153-
type="radio"
154-
value="or"
155-
x-model="param.op" />
156-
<label :for="`radio-or-${index}`" >OR</label>
157-
<input :id="`radio-and-${index}`"
158-
:name="`radio-${index}`"
159-
class="concept-radio-and"
160-
type="radio"
161-
value="and"
162-
x-model="param.op" />
163-
<label :for="`radio-and-${index}`" >AND</label>
107+
<label :for="`type-${index}`" >
108+
Type:
109+
</label>
110+
<select :id="`type-${index}`" x-model="param.type">
111+
<option value="date" >Filter by date range</option>
112+
<option value="title" >Search in title</option>
113+
<option value="titleabs" >Search in title and abstract</option>
114+
<option value="titleabsfull" >Search in title, abstract and full text</option>
115+
<option value="concept" >Filter by concepts</option>
116+
<option value="topic" >Filter by topics</option>
117+
</select>
118+
<button type="button" @click.prevent="params.splice(index, 1)">X</button>
164119

165-
<ul :class="param.op === 'or' ? 'concept-list-or' : 'concept-list-and'" class="concept-list">
166-
<template x-for="(concept, i) in param.concepts">
167-
<li>
168-
<span class="card">
169-
<span x-text="concept.display_name"></span>
170-
|
171-
<a href="#" @click.prevent='param.concepts.splice(i, 1)'>x</a>
172-
</span>
173-
</li>
174-
</template>
175-
</ul>
176-
</div>
177-
</template>
178-
<template x-if="param.type === 'topic'">
179-
<div x-data="{ ac: null, str: '' }"
180-
x-init="param.concepts = param.concepts || [];
181-
param.op = param.op || 'or';
182-
$nextTick(() => ac = new autoComplete({selector: `#search-${index}`, ...autoCompleteTopicConfig}))">
183-
<input :id="`search-${index}`"
184-
class="card w-100"
185-
type="search"
186-
x-model="str"
187-
@selection="param.concepts.push($event.detail.selection.value); str='';" />
188-
<input :id="`radio-or-${index}`"
189-
:name="`radio-${index}`"
190-
class="concept-radio-or"
191-
type="radio"
192-
value="or"
193-
x-model="param.op" />
194-
<label :for="`radio-or-${index}`" >OR</label>
195-
<input :id="`radio-and-${index}`"
196-
:name="`radio-${index}`"
197-
class="concept-radio-and"
198-
type="radio"
199-
value="and"
200-
x-model="param.op" />
201-
<label :for="`radio-and-${index}`" >AND</label>
120+
<template x-if="param.type === 'date'">
121+
<div>
122+
<span>From</span>
123+
<input
124+
:id="`from-${index}`"
125+
class="card"
126+
placeholder="Enter a year. E.g. 2008"
127+
type="number"
128+
x-model="param.fromYear" />
129+
<span>to</span>
130+
<input
131+
:id="`to-${index}`"
132+
class="card"
133+
placeholder="Enter a year. E.g. 2008"
134+
type="number"
135+
x-model="param.toYear" />
136+
</div>
137+
</template>
138+
<template x-if="param.type === 'title'">
139+
<div>
140+
<input :id="`value-${index}`"
141+
class="card w-100"
142+
placeholder="Search for words in works' title"
143+
type="search"
144+
x-model="param.value" />
145+
<a href="https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/search-entities#boolean-searches">Note that you can use boolean search in this field</a>
146+
</div>
147+
</template>
148+
<template x-if="param.type === 'titleabs'">
149+
<div>
150+
<input :id="`value-${index}`"
151+
class="card w-100"
152+
placeholder="Search for words in works' title and abstract"
153+
type="search"
154+
x-model="param.value" />
155+
<a href="https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/search-entities#boolean-searches">Note that you can use boolean search in this field</a>
156+
</div>
157+
</template>
158+
<template x-if="param.type === 'titleabsfull'">
159+
<div>
160+
<input :id="`value-${index}`"
161+
class="card w-100"
162+
placeholder="Search for words in works' title, abstract and full-text"
163+
type="search"
164+
x-model="param.value" />
165+
<a href="https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/search-entities#boolean-searches">Note that you can use boolean search in this field</a>
166+
</div>
167+
</template>
168+
<template x-if="param.type === 'concept'">
169+
<div x-data="{ ac: null, str: '' }"
170+
x-init="param.concepts = param.concepts || [];
171+
param.op = param.op || 'or';
172+
$nextTick(() => ac = new autoComplete({selector: `#search-${index}`, ...autoCompleteConceptConfig}))">
173+
<input :id="`search-${index}`"
174+
class="card w-100"
175+
type="search"
176+
x-model="str"
177+
@selection="param.concepts.push($event.detail.selection.value); str='';" />
178+
<input :id="`radio-or-${index}`"
179+
:name="`radio-${index}`"
180+
class="concept-radio-or"
181+
type="radio"
182+
value="or"
183+
x-model="param.op" />
184+
<label :for="`radio-or-${index}`" >OR</label>
185+
<input :id="`radio-and-${index}`"
186+
:name="`radio-${index}`"
187+
class="concept-radio-and"
188+
type="radio"
189+
value="and"
190+
x-model="param.op" />
191+
<label :for="`radio-and-${index}`" >AND</label>
192+
193+
<ul :class="param.op === 'or' ? 'concept-list-or' : 'concept-list-and'" class="concept-list">
194+
<template x-for="(concept, i) in param.concepts">
195+
<li>
196+
<span class="card">
197+
<span x-text="concept.display_name"></span>
198+
|
199+
<a href="#" @click.prevent='param.concepts.splice(i, 1)'>x</a>
200+
</span>
201+
</li>
202+
</template>
203+
</ul>
204+
</div>
205+
</template>
206+
<template x-if="param.type === 'topic'">
207+
<div x-data="{ ac: null, str: '' }"
208+
x-init="param.concepts = param.concepts || [];
209+
param.op = param.op || 'or';
210+
$nextTick(() => ac = new autoComplete({selector: `#search-${index}`, ...autoCompleteTopicConfig}))">
211+
<input :id="`search-${index}`"
212+
class="card w-100"
213+
type="search"
214+
x-model="str"
215+
@selection="param.concepts.push($event.detail.selection.value); str='';" />
216+
<input :id="`radio-or-${index}`"
217+
:name="`radio-${index}`"
218+
class="concept-radio-or"
219+
type="radio"
220+
value="or"
221+
x-model="param.op" />
222+
<label :for="`radio-or-${index}`" >OR</label>
223+
<input :id="`radio-and-${index}`"
224+
:name="`radio-${index}`"
225+
class="concept-radio-and"
226+
type="radio"
227+
value="and"
228+
x-model="param.op" />
229+
<label :for="`radio-and-${index}`" >AND</label>
202230

203-
<ul :class="param.op === 'or' ? 'concept-list-or' : 'concept-list-and'" class="concept-list">
204-
<template x-for="(concept, i) in param.concepts">
205-
<li>
206-
<span class="card">
207-
<span x-text="concept.display_name"></span>
208-
|
209-
<a href="#" @click.prevent='param.concepts.splice(i, 1)'>x</a>
210-
</span>
211-
</li>
212-
</template>
213-
</ul>
231+
<ul :class="param.op === 'or' ? 'concept-list-or' : 'concept-list-and'" class="concept-list">
232+
<template x-for="(concept, i) in param.concepts">
233+
<li>
234+
<span class="card">
235+
<span x-text="concept.display_name"></span>
236+
|
237+
<a href="#" @click.prevent='param.concepts.splice(i, 1)'>x</a>
238+
</span>
239+
</li>
240+
</template>
241+
</ul>
242+
</div>
243+
</template>
214244
</div>
215245
</template>
216-
</div>
217-
</template>
218246

219-
<button type="button" class="btn center-btn"
220-
@click.prevent="params.push({type: 'date', fromYear: 1900, toYear: 2100})">
221-
Add query param
222-
</button>
223-
</div>
247+
<button type="button" class="btn center-btn"
248+
@click.prevent="params.push({type: 'date', fromYear: 1900, toYear: 2100})">
249+
Add query param
250+
</button>
251+
</div>
224252

225-
<button class="btn primary" >
226-
Fetch Corpus
227-
</button>
228-
</form>
253+
<button class="btn primary" >
254+
Fetch Corpus
255+
</button>
256+
</form>
257+
</template>
258+
</div>
229259
</main>
230260
</template>
231261

src/index.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import Alpine from 'alpinejs';
66
import autoComplete from '@tarekraafat/autocomplete.js';
77

88
import { autoCompleteConceptConfig, autoCompleteTopicConfig } from './lib/autocomplete.js';
9-
import { fetchWorks } from './lib/fetch.js';
9+
import { checkApiUrl, fetchWorks, fetchWorksFromUrl } from './lib/fetch.js';
1010
import { processWorks, getFilters, filterData, generateJSONDataURL } from './lib/processing.js';
1111
import { generateGraph, generateGexfURL } from './lib/graph.js';
1212

1313
window.autoCompleteConceptConfig = autoCompleteConceptConfig;
1414
window.autoCompleteTopicConfig = autoCompleteTopicConfig;
15+
window.checkApiUrl = checkApiUrl;
1516
window.fetchWorks = fetchWorks;
17+
window.fetchWorksFromUrl = fetchWorksFromUrl;
1618
window.processWorks = processWorks;
1719
window.getFilters = getFilters;
1820
window.filterData = filterData;
@@ -22,7 +24,8 @@ window.generateJSONDataURL = generateJSONDataURL;
2224

2325
Alpine.data('App', () => ({
2426
params: [{type: 'titleabs', value: ''}],
25-
maxWorks: 1000,
27+
apiUrl: '',
28+
maxWorks: 10000,
2629
state: 'search',
2730
nextState: '',
2831
loadingMsg: '',

0 commit comments

Comments
 (0)