Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>paper-hero</title> | |
| <link rel="stylesheet" href="https://unpkg.com/boltcss/bolt.min.css"> | |
| <script type="importmap"> | |
| { | |
| "imports": { | |
| "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js" | |
| } | |
| } | |
| </script> | |
| <style> | |
| body { | |
| max-width: 800px; | |
| margin: 40px auto; | |
| padding: 0 20px; | |
| } | |
| .form-group { | |
| display: flex; | |
| flex-direction: row; | |
| justify-content: flex-start; | |
| align-items: center; | |
| } | |
| label { | |
| margin-right: 1rem; | |
| } | |
| button { | |
| margin: 0.2rem 0.2rem; | |
| } | |
| button:hover { | |
| background-color: #dbdbdb; | |
| } | |
| footer { | |
| text-align: center; | |
| margin-top: 2rem; | |
| } | |
| .button-group { | |
| margin-top: 3rem; | |
| } | |
| .search-button { | |
| background-color: #ffc83d; | |
| color: #d67d00; | |
| font-weight: bold; | |
| } | |
| .search-button:hover { | |
| background-color: #ffc83dc0; | |
| } | |
| .download-button { | |
| background-color: #98ca56; | |
| color: white; | |
| font-weight: bold; | |
| } | |
| .download-button:hover { | |
| background-color: #98ca56d1; | |
| } | |
| .output-title { | |
| margin-top: 2rem; | |
| margin-bottom: 0; | |
| display: block; | |
| background-color: #98ca56; | |
| color: white; | |
| font-weight: bold; | |
| font-size: large; | |
| padding: 6px 15px; | |
| border-top-left-radius: 6px; | |
| border-top-right-radius: 6px; | |
| } | |
| .output-box { | |
| margin-top: 0; | |
| padding: 6px 15px; | |
| background-color: white; | |
| border: 2px solid #98ca56; | |
| border-bottom-left-radius: 6px; | |
| border-bottom-right-radius: 6px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>💪 Paper Hero</h1> | |
| <p> | |
| Paper Hero is a toolkit to help search for papers from aclanthology, arXiv and dblp. | |
| </p> | |
| <p>GitHub Address: <a href="https://github.com/Spico197/paper-hero" target="_blank">Spico197/paper-hero</a></p> | |
| </header> | |
| <main> | |
| <div id="app"> | |
| <div class="form-group"> | |
| <label for="method"><strong>Source</strong></label> | |
| <select id="method" v-model="method"> | |
| <option value="" disabled>Please select a source</option> | |
| <option value="aclanthology">ACL Anthology</option> | |
| <option value="arxiv">ArXiv</option> | |
| <option value="dblp">DBLP</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="max-res"><strong>Max Results</strong></label> | |
| <input id="max-res" type="number" v-model="maxResults"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="add-field"><strong>New Field</strong></label> | |
| <select id="add-field" v-model="addField"> | |
| <option value="" disabled>Please select a field</option> | |
| <option :value="field" v-for="field in restFields">{{ field }}</option> | |
| </select> | |
| <button @click.prevent="addNewField">Add</button> | |
| </div> | |
| <hr> | |
| <div> | |
| <p><strong>Fields</strong></p> | |
| <p> | |
| Add <code>&&</code> to represent <code>AND</code> logic, e.g. <code>span-based && event extraction</code> | |
| means <em>span-based</em> and <em>event extraction</em> both appear in a field. | |
| </p> | |
| <p> | |
| For <code>year</code> and <code>month</code> fields, the query should | |
| follow the <code>start && end</code> format, | |
| e.g. year <code>2006 && 2013</code> means searching for papers published | |
| between <code>2006</code> and <code>2013</code>. | |
| </p> | |
| <div v-for="(groups, field) in query"> | |
| <label :for="field"><strong>{{ field }}</strong></label> | |
| <div v-for="(group, index) in groups"> | |
| <input class="field-input" type="text" v-model="query[field][index]" placeholder="text1 && text2 && text3" | |
| size="50"> | |
| <button @click.prevent="rmAnd(field, index)">X</button> | |
| </div> | |
| <button @click.prevent="addOr(field)">OR</button> | |
| </div> | |
| </div> | |
| <div v-if="timerHandler"> | |
| <p>⏱️ {{ searchSecondsTwoDecimal }}</p> | |
| </div> | |
| <div v-if="output"> | |
| <p class="output-title">Output Info</p> | |
| <p class="output-box"> | |
| {{ output }} | |
| <br> | |
| You are ready to download the results by clicking the download button below. | |
| <br> | |
| Like this tool? ⭐ me on <a href="https://github.com/Spico197/paper-hero" target="_blank">GitHub</a> ! | |
| </p> | |
| </div> | |
| <div class="button-group"> | |
| <button @click.prevent="resetQuery">Reset</button> | |
| <button class="search-button" @click.prevent="search">Search</button> | |
| <a :href="downloadHref" :download="`${method}.json`" v-if="downloadHref"> | |
| <button class="search-button download-button">Download</button> | |
| </a> | |
| </div> | |
| </div> | |
| </main> | |
| <footer> | |
| <hr> | |
| Made by <a href="https://spico197.github.io" target="_blank">Tong Zhu</a> w/ 💖 | |
| </footer> | |
| <script type="module"> | |
| import { createApp, ref, computed, toRaw, watch } from 'vue' | |
| createApp( | |
| { | |
| setup() { | |
| const method = ref("aclanthology") | |
| const query = ref({ title: [[]] }) | |
| const maxResults = ref(2000) | |
| const addField = ref("") | |
| const allFields = ["title", "author", "abstract", "venue", "year", "month"] | |
| const downloadUrl = ref('') | |
| const downloadToken = ref('') | |
| const downloadHref = ref('') | |
| const output = ref('') | |
| const timerHandler = ref(0) | |
| const searchSeconds = ref(0.0) | |
| const searchSecondsTwoDecimal = computed(() => { | |
| return `${searchSeconds.value.toFixed(1)}s` | |
| }) | |
| const restFields = computed(() => { | |
| let rest = [] | |
| for (const field of allFields) { | |
| if (!(field in query.value)) { | |
| rest.push(field) | |
| } | |
| } | |
| return rest | |
| }) | |
| function addNewField() { | |
| if (addField.value) { | |
| query.value[addField.value] = [[]] | |
| addField.value = "" | |
| } | |
| } | |
| function rmAnd(field, index) { | |
| if (query.value[field].length == 1) { | |
| delete query.value[field] | |
| } else { | |
| query.value[field].splice(index, 1) | |
| } | |
| } | |
| function addOr(field) { | |
| query.value[field].push([]) | |
| } | |
| function resetOutput() { | |
| output.value = "" | |
| downloadUrl.value = "" | |
| downloadToken.value = "" | |
| URL.revokeObjectURL(downloadHref.value) | |
| downloadHref.value = "" | |
| searchSeconds.value = 0.0 | |
| timerHandler.value = 0 | |
| searchSecondsTwoDecimal.value = "" | |
| } | |
| function resetQuery() { | |
| query.value = { title: [[]] } | |
| resetOutput() | |
| } | |
| function startTimer() { | |
| console.log("start") | |
| timerHandler.value = setInterval(() => { | |
| searchSeconds.value += 0.1 | |
| }, 100) | |
| } | |
| function endTimer() { | |
| console.log("end") | |
| if (timerHandler.value > 0) { | |
| console.log("endi") | |
| clearInterval(timerHandler.value) | |
| } | |
| } | |
| function search() { | |
| resetOutput() | |
| startTimer() | |
| let q = {} | |
| for (const prop in query.value) { | |
| q[prop] = [] | |
| for (let i = 0; i < query.value[prop].length; i++) { | |
| if (query.value[prop][i].length > 0) { | |
| let andString = toRaw(query.value[prop][i]) | |
| let andStrings = andString.split('&&') | |
| for (let j = 0; j < andStrings.length; j++) { | |
| andStrings[j] = andStrings[j].trim() | |
| } | |
| q[prop].push(andStrings) | |
| } | |
| } | |
| if (q[prop].length < 1) { | |
| delete q[prop] | |
| } | |
| } | |
| const postData = JSON.stringify({ | |
| "method": method.value, | |
| "query": q, | |
| "max_results": maxResults.value, | |
| "return_content": false, | |
| }) | |
| fetch( | |
| "/api/", | |
| { | |
| method: "POST", | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: postData, | |
| } | |
| ) | |
| .then((response) => response.json()) | |
| .then((json) => { | |
| if (json["ok"] === false) { | |
| alert(json["msg"]) | |
| } else { | |
| downloadUrl.value = json["url"] | |
| downloadToken.value = json["token"] | |
| output.value = `${json["msg"]}, #matched paper: ${json["paper"]}` | |
| } | |
| }) | |
| .catch((err) => { alert(err) }) | |
| .finally(() => endTimer()) | |
| } | |
| watch(downloadUrl, (newUrl, oldUrl) => { | |
| if (downloadToken.value) { | |
| fetch( | |
| `/download/?u=${downloadUrl.value}&t=${downloadToken.value}`, | |
| { | |
| method: "GET", | |
| } | |
| ) | |
| .then((response) => response.blob()) | |
| .then((data) => { | |
| downloadHref.value = URL.createObjectURL(data) | |
| }) | |
| } | |
| }) | |
| return { | |
| method, | |
| query, | |
| restFields, | |
| addField, | |
| addNewField, | |
| search, | |
| rmAnd, | |
| addOr, | |
| resetQuery, | |
| maxResults, | |
| output, | |
| downloadUrl, | |
| downloadHref, | |
| searchSeconds, | |
| timerHandler, | |
| searchSecondsTwoDecimal, | |
| } | |
| } | |
| } | |
| ).mount("#app") | |
| </script> | |
| </body> | |
| </html> |