diff options
| -rw-r--r-- | scraper/client/actions.js | 11 | ||||
| -rw-r--r-- | scraper/client/app.js | 2 | ||||
| -rw-r--r-- | scraper/client/common/autocomplete.component.js | 49 | ||||
| -rw-r--r-- | scraper/client/paper/citationList.component.js | 96 | ||||
| -rw-r--r-- | scraper/client/paper/paper.citations.js | 55 | ||||
| -rw-r--r-- | scraper/client/paper/paper.css | 73 | ||||
| -rw-r--r-- | scraper/client/paper/paper.info.js | 4 | ||||
| -rw-r--r-- | scraper/client/paper/paper.random.js | 2 | ||||
| -rw-r--r-- | scraper/client/paper/paper.unknown.js | 48 | ||||
| -rw-r--r-- | scraper/client/paper/paper.verify.js | 286 | ||||
| -rw-r--r-- | scraper/client/store.js | 2 | ||||
| -rw-r--r-- | scraper/client/types.js | 7 | ||||
| -rw-r--r-- | scraper/s2-geocode-server.py | 30 | ||||
| -rw-r--r-- | scraper/util.py | 15 |
14 files changed, 519 insertions, 161 deletions
diff --git a/scraper/client/actions.js b/scraper/client/actions.js index 6c4e16b2..1b5152ea 100644 --- a/scraper/client/actions.js +++ b/scraper/client/actions.js @@ -1,9 +1,10 @@ import { get, post } from './util' import * as types from './types' -export const api = (dispatch, method, tag, url, params) => { +export const api = (dispatch, method, tag, url, params, after) => { dispatch({ type: types.api.loading, tag }) return method(url, params).then(data => { + if (after) data = after(data) dispatch({ type: types.api.loaded, tag, data }) return data }).catch(err => { @@ -32,6 +33,14 @@ export const postAddress = data => dispatch => { api(dispatch, post, 'address', '/api/address/add', data) } +export const getVerifications = () => dispatch => ( + api(dispatch, get, 'verifications', '/api/verifications', {}) +) + +export const getVerificationsDataset = dataset => dispatch => ( + api(dispatch, get, 'verifications', '/api/verifications/' + dataset, {}) +) + export const getVerification = sha256 => dispatch => ( api(dispatch, get, 'verify', '/api/verify/' + sha256, {}) ) diff --git a/scraper/client/app.js b/scraper/client/app.js index dc5c6fe6..b449d0d0 100644 --- a/scraper/client/app.js +++ b/scraper/client/app.js @@ -15,7 +15,7 @@ export default class App extends Component { <div className='body'> <Route path="/paper/:key/" component={Paper.Manager} /> <Switch> - <Route exact path="/paper/:key/" component={Paper.Random} /> + <Route exact path="/paper/:key/" component={Paper.Info} /> <Route exact path="/paper/:key/citations/" component={Paper.Citations} /> <Route exact path="/paper/:key/unknown/" component={Paper.UnknownCitations} /> <Route exact path="/paper/:key/info/" component={Paper.Info} /> diff --git a/scraper/client/common/autocomplete.component.js b/scraper/client/common/autocomplete.component.js index e2908cd1..67dc78c9 100644 --- a/scraper/client/common/autocomplete.component.js +++ b/scraper/client/common/autocomplete.component.js @@ -49,6 +49,10 @@ class Autocomplete extends Component { componentDidMount() { // build index based on what's in the hierarchy + if (!this.props.institutions.loading) this.buildIndex() + } + + buildIndex() { const { entities, lookup } = this.props.institutions let index = [] this.index = index @@ -69,6 +73,9 @@ class Autocomplete extends Component { } componentDidUpdate(oldProps) { + if (oldProps.institutions.loading && !this.props.institutions.loading) { + this.buildIndex() + } if (this.props.vetting !== oldProps.vetting) { this.handleChange({ target: { value: this.props.vetting } }) } else if (this.props.value !== oldProps.value) { @@ -77,30 +84,37 @@ class Autocomplete extends Component { } handleKeyDown(e) { - let id + let name + console.log(e.keyCode) switch (e.keyCode) { case 27: // escape e.preventDefault() this.handleCancel() break - case 37: // left case 38: // up e.preventDefault() this.setState({ selected: (this.state.matches.length + this.state.selected - 1) % this.state.matches.length }) return false - case 39: // right case 40: // down e.preventDefault() this.setState({ selected: (this.state.selected + 1) % this.state.matches.length }) return false - case 13: // enter - id = this.state.matches[this.state.selected] + case 13: // enter - select from the list + name = this.state.matches[this.state.selected] e.preventDefault() - this.handleSelect(id) + this.handleSelect(name, true) + return false + case 9: // tab - keep the unverified text + name = this.state.matches[this.state.selected] + if (name === this.state.q) { + this.handleSelect(this.state.q, true) + } else { + this.handleSelect(this.state.q, false) + } return false default: break @@ -121,7 +135,6 @@ class Autocomplete extends Component { return } let seen = {} - let matches = [] value.split(' ').forEach(word => { const re = new RegExp(word) this.index.forEach(([synonym, term]) => { @@ -139,14 +152,14 @@ class Autocomplete extends Component { } } }) - matches = Object.keys(seen) - .map(term => [seen[term], term]) - .sort((a, b) => { - return b[0] - a[0] - }) - .slice(0, 100) - .map(pair => pair[1]) }) + let matches = Object.keys(seen) + .map(term => [seen[term], term]) + .sort((a, b) => { + return b[0] - a[0] + }) + .slice(0, 100) + .map(pair => pair[1]) this.setState({ q, selected: 0, @@ -154,9 +167,9 @@ class Autocomplete extends Component { }) } - handleSelect(name) { - console.log('select', name) - if (this.props.onSelect) this.props.onSelect(name) + handleSelect(name, valid) { + console.log('select', name, valid) + if (this.props.onSelect) this.props.onSelect(name, valid) this.setState({ q: name, selected: 0, matches: [] }) } @@ -173,7 +186,7 @@ class Autocomplete extends Component { <div key={i} className={selected === i ? 'selected' : ''} - onClick={() => this.handleSelect(match)} + onClick={() => this.handleSelect(match, true)} onMouseEnter={() => this.setState({ selected: i })} > {label} diff --git a/scraper/client/paper/citationList.component.js b/scraper/client/paper/citationList.component.js new file mode 100644 index 00000000..09060375 --- /dev/null +++ b/scraper/client/paper/citationList.component.js @@ -0,0 +1,96 @@ +import React, { Component } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { Link } from 'react-router-dom' + +import * as actions from '../actions' + +import { TableObject, Loader } from '../common' +import { USES_DATASET } from '../types' + +class CitationList extends Component { + render() { + const { citations, title, api } = this.props + const { paperInfo, unknownCitations, verifications } = api + const { dataset } = paperInfo + if (!dataset || !citations || !verifications[dataset.key]) return <Loader /> + let verifiedLookup = verifications[dataset.key] || {} + // console.log(verifications) + + return ( + <div className='citations'> + <h2>{title}</h2> + <ul> + {citations + .map(citation => [citation.title, verifiedLookup[citation.id] ? verifiedLookup[citation.id].uses_dataset : USES_DATASET.NO_DATA, citation]) + .sort((a,b) => (b[1] - a[1] || (a[0].localeCompare(b[0])))) + .map((pair, i) => { + const [ title, uses_dataset, citation ] = pair + let cite = { ...citation } + cite.id = { + _raw: true, + value: <Link to={'/paper/' + dataset.key + '/verify/' + citation.id}>{citation.id}</Link> + } + cite.pdf = { + _raw: true, + value: (cite.pdf && cite.pdf.length) + ? cite.pdf.map((pdf, i) => <a key={'pdf_' + i} href={pdf} rel='noopener noreferrer' target="_blank">[pdf]</a>) + : "no pdf" + } + cite.s2 = { + _raw: true, + value: <a + href={'https://www.semanticscholar.org/paper/' + citation.id} + target="_blank" + rel="noopener noreferrer" + className={'pdfLink'} + >{'[semantic scholar]'}</a> + } + cite.addresses = { + _raw: true, + value: (cite.addresses || []).map((address, j) => ( + <div key={j}>{address.name}{', '}<span className='type'>{address.type}</span></div> + )) + } + if (citation.id in verifiedLookup) { + const verification = verifiedLookup[citation.id] + cite.verified = { + _raw: true, + value: verification.uses_dataset === USES_DATASET.YES + ? <span className='verified'>uses dataset</span> + : verification.uses_dataset === USES_DATASET.NO + ? <span className='unverified'>{"doesn't use dataset"}</span> + : <span className='not_enough_info'>{"not enough information"}</span> + } + } + else { + cite.verified = { + _raw: true, + value: <span className='unknown'>unknown</span> + } + } + return ( + <li key={i}> + <TableObject + summary + object={cite} + tag={cite.title} + order={['id', 'pdf', 's2', 'year', 'addresses', 'verified']} + /> + </li> + ) + })} + </ul> + </div> + ) + } +} + +const mapStateToProps = state => ({ + api: state.api +}) +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators({ ...actions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(CitationList) diff --git a/scraper/client/paper/paper.citations.js b/scraper/client/paper/paper.citations.js index 41ddd55e..f0e9ea26 100644 --- a/scraper/client/paper/paper.citations.js +++ b/scraper/client/paper/paper.citations.js @@ -5,47 +5,28 @@ import { Link } from 'react-router-dom' import * as actions from '../actions' -import { TableObject } from '../common' +import { Loader } from '../common' +import { USES_DATASET } from '../types' + +import CitationList from './citationList.component' class PaperCitations extends Component { + componentDidUpdate(prevProps) { + if (this.props.api.paperInfo.dataset !== prevProps.api.paperInfo.dataset) { + this.props.actions.getVerificationsDataset(this.props.api.paperInfo.dataset.key) + } + } + render() { - const { dataset, citations } = this.props.api.paperInfo - if (!dataset || !citations) return null - console.log('rendering citations...') - console.log(citations) + const { paperInfo, unknownCitations, verifications } = this.props.api + const { dataset, citations } = paperInfo + if (!dataset || !citations || !verifications[dataset.key]) return <Loader /> + return ( - <div className='citations'> - <h2>{dataset.name_full}: Citations</h2> - <ul> - {citations.map((citation, i) => { - let cite = { ...citation } - cite.id = { - _raw: true, - value: <Link to={'/paper/' + dataset.key + '/verify/' + citation.id}>{citation.id}</Link> - } - cite.pdf = { - _raw: true, - value: (cite.pdf && cite.pdf.length) ? <a href={cite.pdf[0]} rel='noopener noreferrer' target="_blank">[pdf]</a> : "no pdf" - } - cite.addresses = { - _raw: true, - value: cite.addresses.map((address, j) => ( - <div key={j}>{address.name}{', '}<span className='type'>{address.type}</span></div> - )) - } - return ( - <li key={i}> - <TableObject - summary - object={cite} - tag={cite.title} - order={['id', 'pdf', 'year', 'addresses']} - /> - </li> - ) - })} - </ul> - </div> + <CitationList + title={dataset.name_full + ': Citations'} + citations={citations.concat(unknownCitations.citations)} + /> ) } } diff --git a/scraper/client/paper/paper.css b/scraper/client/paper/paper.css index 4815bd30..302eceb0 100644 --- a/scraper/client/paper/paper.css +++ b/scraper/client/paper/paper.css @@ -1,8 +1,20 @@ +.row { + display: flex; + flex-direction: row; + padding: 4px; +} +.rowHeading { + display: block; + width: 194px; +} .form, .paperInfo { padding: 10px; width: 100%; } +.citations { + padding:40px; +} .citations ul { list-style-type: none; margin: 0; @@ -12,7 +24,13 @@ .citations li { list-style-type: none; margin: 0; - padding-bottom: 40px; + padding-bottom: 20px; + border:1px solid #ccc; +} +.citations h3{ + font-size:20px; + margin:10px 0 15px 0; + font-weight: bold; } .type { @@ -26,6 +44,41 @@ cursor: pointer; color: #11f; } +.verified { + font-weight: bold; + color: white; + font-weight: bold; + display: inline-block; + background: #00b200; + padding:4px; + font-size:12px; +} +.unverified { + color: white; + font-weight: bold; + display: inline-block; + background: #ff0000; + padding:4px; + font-size:12px; +} +.not_enough_info { + font-weight: bold; + color: white; + font-weight: bold; + display: inline-block; + background: #e0c200; + padding:4px; + font-size:12px; +} +.unknown { + font-weight: bold; + color: white; + font-weight: bold; + display: inline-block; + background: #999; + padding:4px; + font-size:12px; +} .param { display: flex; @@ -39,9 +92,16 @@ input.notes { .param .btn { margin-top: 5px; } -.vetting { - width: 250px; - margin-right: 10px; +.row.disabled { opacity: 0.5; pointer-events: none;} + +.vettingRow label { + display: flex; + justify-content: center; + align-items: center; + padding: 0 10px 0 0; +} +.vettingRow input { + margin-right: 6px; } .param label { display: block; @@ -51,7 +111,11 @@ input.notes { .param input[type=checkbox] { margin: 6px 0; } +input[type=text] { + margin-bottom: 2px; +} textarea { + margin-bottom: 2px; padding: 4px; font-size: 14px; } @@ -68,6 +132,7 @@ iframe.pdfViewer { header select { margin-right: 10px; width: 100px; + min-width: 200px; } header a { color: white; diff --git a/scraper/client/paper/paper.info.js b/scraper/client/paper/paper.info.js index a8553c9b..35234617 100644 --- a/scraper/client/paper/paper.info.js +++ b/scraper/client/paper/paper.info.js @@ -9,7 +9,7 @@ import { TableObject } from '../common' class PaperInfo extends Component { render() { const { paperInfo, unknownCitations } = this.props.api - const { dataset, statistics, address } = paperInfo + const { dataset, address } = paperInfo if (!dataset) return null return ( <div className='paperInfo'> @@ -21,7 +21,7 @@ class PaperInfo extends Component { /> <TableObject summary tag="Statistics" - object={statistics} + object={dataset} order={['year_published', 'purpose_short', 'wild', 'indoor', 'outdoor', 'cyberspace', 'names', 'downloaded', diff --git a/scraper/client/paper/paper.random.js b/scraper/client/paper/paper.random.js index aab22172..c7476332 100644 --- a/scraper/client/paper/paper.random.js +++ b/scraper/client/paper/paper.random.js @@ -15,7 +15,7 @@ class PaperRandom extends Component { const citation = choice(citations) console.log(citation) if (citation.id) { - history.push('/paper/' + this.props.match.params.key + '/address/' + citation.id) + history.push('/paper/' + this.props.match.params.key + '/verify/' + citation.id) } } diff --git a/scraper/client/paper/paper.unknown.js b/scraper/client/paper/paper.unknown.js index 0cb7d2da..876ac144 100644 --- a/scraper/client/paper/paper.unknown.js +++ b/scraper/client/paper/paper.unknown.js @@ -5,40 +5,28 @@ import { Link } from 'react-router-dom' import * as actions from '../actions' -import { TableObject } from '../common' +import { Loader } from '../common' +import { USES_DATASET } from '../types' + +import CitationList from './citationList.component' class PaperUnknown extends Component { + componentDidUpdate(prevProps) { + if (this.props.api.paperInfo.dataset !== prevProps.api.paperInfo.dataset) { + this.props.actions.getVerificationsDataset(this.props.api.paperInfo.dataset.key) + } + } + render() { - const { dataset } = this.props.api.paperInfo - const { citations } = this.props.api.unknownCitations - if (!dataset || !citations) return null - console.log('rendering unknown citations...') + const { paperInfo, unknownCitations, verifications } = this.props.api + const { dataset, citations } = paperInfo + if (!dataset || !citations || !verifications[dataset.key]) return <Loader /> + return ( - <div className='citations'> - <h2>{dataset.name_full}: Unknown Citations</h2> - <ul> - {citations.map((citation, i) => { - let cite = { ...citation } - cite.id = { - _raw: true, - value: <Link to={'/paper/' + dataset.key + '/address/' + citation.id}>{citation.id}</Link> - } - cite.pdf = { - _raw: true, - value: cite.pdf ? <a href={cite.pdf} rel='noopener noreferrer' target="_blank">[pdf]</a> : "no pdf" - } - return ( - <li key={i}> - <TableObject - summary - object={cite} - tag={cite.title} - /> - </li> - ) - })} - </ul> - </div> + <CitationList + title={dataset.name_full + ': Unknown Citations'} + citations={unknownCitations.citations} + /> ) } } diff --git a/scraper/client/paper/paper.verify.js b/scraper/client/paper/paper.verify.js index 5f85a551..61faa24a 100644 --- a/scraper/client/paper/paper.verify.js +++ b/scraper/client/paper/paper.verify.js @@ -3,20 +3,28 @@ import { bindActionCreators } from 'redux' import { connect } from 'react-redux' import * as actions from '../actions' - import { history } from '../store' - import { Loader, Autocomplete } from '../common' +import { USES_DATASET } from '../types' const initialState = { citation: null, - uses_dataset: false, - doesnt_use_dataset: true, - images_in_paper: false, + address: {}, + uses_dataset: USES_DATASET.UNKNOWN, + images_in_paper: "FALSE", + used_as_model: "FALSE", verified_by: localStorage.getItem('verify.username') || '', reference: '', notes: '', + location: '', pdf_index: 0, + isUnknown: false, + institution_1: '', + institution_2: '', + institution_3: '', + validate_1: false, + validate_2: false, + validate_3: false, } class PaperVerify extends Component { @@ -27,6 +35,7 @@ class PaperVerify extends Component { componentDidMount() { const { sha256 } = this.props.match.params this.props.actions.getInstitutions() + this.props.actions.getAddress(sha256) this.props.actions.getVerification(sha256) const citationState = this.getCitationState(sha256) console.log('DID MOUNT') @@ -35,53 +44,67 @@ class PaperVerify extends Component { componentDidUpdate(oldProps) { const { sha256 } = this.props.match.params - const { verify } = this.props.api + const { address, verify } = this.props.api const { sha256: oldSha256 } = oldProps.match.params - const { verify: oldVerify } = oldProps.api + const { verify: oldVerify, address: oldAddress } = oldProps.api const oldPaper = oldVerify ? oldVerify.paper : null const paper = verify ? verify.paper : null + let newState = {} if (oldSha256 && sha256 !== oldSha256) { console.log('update verification') + this.props.actions.getAddress(sha256) this.props.actions.getVerification(sha256) const citationState = this.getCitationState(sha256) - this.setState({ + newState = { ...initialState, ...citationState, - }) + ...address.paper, + } + this.setState(newState) } else if (verify && !verify.loading && verify.paper && (!oldPaper || oldPaper !== verify.paper)) { if (paper.error) { console.log('USING PAPER VERIFICATION') const citationState = this.getCitationState(sha256) - this.setState({ + newState = { ...initialState, ...citationState, - }) + ...address.paper, + } + this.setState(newState) } else { // console.log(paper) console.log('GOT CUSTOM CITATION STATE', paper) const citationState = this.getCitationState(sha256) - this.setState({ + newState = { ...citationState, - uses_dataset: paper.uses_dataset === "TRUE", - doesnt_use_dataset: paper.doesnt_use_dataset === "TRUE", - images_in_paper: paper.images_in_paper === "TRUE", + ...address.paper, + uses_dataset: paper.uses_dataset, + images_in_paper: paper.images_in_paper, verified_by: paper.verified_by, reference: paper.reference, notes: paper.notes, - }) + } + this.setState(newState) } } else if (oldProps.api.unknownCitations !== this.props.api.unknownCitations) { + console.log('loaded unknown citations') const citationState = this.getCitationState(sha256) - this.setState(citationState) + newState = citationState + this.setState(newState) } } getCitationState(sha256) { const { paperInfo, unknownCitations } = this.props.api let citation = (unknownCitations.citations || []).find(f => f.id === sha256) - if (!citation) { + if (citation) { + citation.isUnknown = true + } else { citation = (paperInfo.citations || []).find(f => f.id === sha256) + if (citation) { + citation.isUnknown = false + } } // console.log(sha256, citation) let state = { citation } @@ -103,12 +126,19 @@ class PaperVerify extends Component { title: this.state.citation.title, dataset: this.props.api.paperInfo.dataset.key, uses_dataset: this.state.uses_dataset, - doesnt_use_dataset: this.state.doesnt_use_dataset, images_in_paper: this.state.images_in_paper, + used_as_model: this.state.used_as_model, verified_by: this.state.verified_by, reference: this.state.reference, notes: this.state.notes, date: dateString, + isUnknown: this.state.citation.isUnknown, + institution_1: this.state.institution_1, + validate_1: this.state.validate_1, + institution_2: this.state.institution_2, + validate_2: this.state.validate_2, + institution_3: this.state.institution_3, + validate_3: this.state.validate_3, }) this.next(false) } @@ -150,7 +180,7 @@ class PaperVerify extends Component { } render() { - let { paperInfo } = this.props.api + const { paperInfo, unknownCitations } = this.props.api if (paperInfo.loading) return <Loader /> // console.log(this.state) const { citation } = this.state @@ -169,6 +199,7 @@ class PaperVerify extends Component { return ( <a key={i} + href={pdf} onClick={() => this.setState({ pdf_index: i })} className={i === this.state.pdf_index ? 'selected pdfLink' : 'pdfLink'} > @@ -186,45 +217,117 @@ class PaperVerify extends Component { </a> </div> - <div className='param'> - <label>Uses the dataset?</label> - <input - className='vetting' - type='radio' - name='uses_dataset' - checked={!!this.state.uses_dataset} - onChange={e => this.setState({ - uses_dataset: e.target.checked, - doesnt_use_dataset: !e.target.checked, - })} - /> + {this.renderLocationForm()} + + <div className='row vettingRow'> + <div className='rowHeading'> + {'Uses dataset'} + </div> + <label> + <input + className='vetting' + type='radio' + name='uses_dataset' + checked={String(this.state.uses_dataset) === USES_DATASET.YES} + onChange={e => this.setState({ + uses_dataset: USES_DATASET.YES, + })} + /> + <div>{"Yes"}</div> + </label> + + <label> + <input + className='vetting' + type='radio' + name='uses_dataset' + checked={String(this.state.uses_dataset) === USES_DATASET.NO} + onChange={e => this.setState({ + uses_dataset: USES_DATASET.NO, + })} + /> + <div>{"No"}</div> + </label> + + <label> + <input + className='vetting' + type='radio' + name='uses_dataset' + checked={String(this.state.uses_dataset) !== USES_DATASET.YES && String(this.state.uses_dataset) !== USES_DATASET.NO} + onChange={e => this.setState({ + uses_dataset: USES_DATASET.UNKNOWN, + })} + /> + <div>{"Not enough information"}</div> + </label> </div> - <div className='param'> - <label>{"Doesn't use dataset"}</label> - <input - className='vetting' - type='radio' - name='uses_dataset' - checked={!!this.state.doesnt_use_dataset} - onChange={e => this.setState({ - uses_dataset: !e.target.checked, - doesnt_use_dataset: e.target.checked - })} - /> + <div + className={String(this.state.uses_dataset) === USES_DATASET.YES ? 'row vettingRow' : 'row vettingRow disabled'} + disabled={String(this.state.uses_dataset) === USES_DATASET.YES ? false : true} + > + <div className='rowHeading'> + {'Paper shows images'} + </div> + + <label> + <input + className='vetting' + type='radio' + name='images_in_paper' + checked={this.state.images_in_paper === "TRUE"} + onChange={e => this.setState({ + images_in_paper: "TRUE", + })} + /> + <div>{"Yes"}</div> + </label> + + <label> + <input + className='vetting' + type='radio' + name='images_in_paper' + checked={this.state.images_in_paper === "FALSE"} + onChange={e => this.setState({ + images_in_paper: "FALSE", + })} + /> + <div>{"No"}</div> + </label> </div> - <div className='param'> - <label>{"Uses images from dataset"}</label> - <input - className='vetting' - type='checkbox' - name='images_in_paper' - checked={!!this.state.images_in_paper} - onChange={e => this.setState({ - images_in_paper: e.target.checked, - })} - /> + <div className='row vettingRow'> + <div className='rowHeading'> + {'Used as model'} + </div> + + <label> + <input + className='vetting' + type='radio' + name='used_as_model' + checked={this.state.used_as_model === "TRUE"} + onChange={e => this.setState({ + used_as_model: "TRUE", + })} + /> + <div>{"Yes"}</div> + </label> + + <label> + <input + className='vetting' + type='radio' + name='used_as_model' + checked={this.state.used_as_model === "FALSE"} + onChange={e => this.setState({ + used_as_model: "FALSE", + })} + /> + <div>{"No"}</div> + </label> </div> <div className='param'> @@ -244,10 +347,10 @@ class PaperVerify extends Component { rows={3} cols={50} type='text' - className='notes' - value={this.state.notes} - placeholder='Notes' - onChange={e => this.setState({ notes: e.target.value })} + className='reference' + value={this.state.reference} + placeholder='Reference' + onChange={e => this.setState({ reference: e.target.value })} /> </div> @@ -274,7 +377,7 @@ class PaperVerify extends Component { <button className='btn' onClick={this.prev.bind(this)} - >{'Prev >'}</button> + >{'< Prev'}</button> <button className='btn' @@ -286,8 +389,71 @@ class PaperVerify extends Component { </div> ) } + renderLocationForm() { + if (!this.state.citation.isUnknown) return + return ( + <div> + <div className='param'> + <label>Institution #1</label> + <Autocomplete + placeholder='Institution #1' + value={this.state.institution_1} + onSelect={(institution_1, validate_1) => { + this.setState({ institution_1, validate_1 }) + }} + onCancel={value => { + this.setState({ institution_1: '', validate_1: false }) + }} + /> + </div> + + <div className='param'> + <label>Institution #2</label> + <Autocomplete + placeholder='Institution #2' + value={this.state.institution_2} + onSelect={(institution_2, validate_2) => { + this.setState({ institution_2, validate_2 }) + }} + onCancel={value => { + this.setState({ institution_2: '', validate_2: false }) + }} + /> + </div> + + <div className='param'> + <label>Institution #3</label> + <Autocomplete + placeholder='Institution #3' + value={this.state.institution_3} + onSelect={(institution_3, validate_3) => { + this.setState({ institution_3, validate_3 }) + }} + onCancel={value => { + this.setState({ institution_3: '', validate_3: false }) + }} + /> + </div> + </div> + ) + } } +/* + <div className='param'> + <label>{"Verifiable"}</label> + <input + className='vetting' + type='checkbox' + name='verifiable' + checked={!!this.state.verifiable} + onChange={e => this.setState({ + verifiable: e.target.checked, + })} + /> + </div> +*/ + const mapStateToProps = state => ({ api: state.api, }) diff --git a/scraper/client/store.js b/scraper/client/store.js index 8649d472..b199ae29 100644 --- a/scraper/client/store.js +++ b/scraper/client/store.js @@ -13,6 +13,8 @@ const initialState = () => ({ address: {}, paperInfo: {}, unknownCitations: {}, + verify: {}, + verifications: {}, options: {} }) diff --git a/scraper/client/types.js b/scraper/client/types.js index 53c0cff0..e6b4142c 100644 --- a/scraper/client/types.js +++ b/scraper/client/types.js @@ -11,3 +11,10 @@ export const api = tagAsType('api', [ ]) export const init = '@@INIT' + +export const USES_DATASET = { + YES: "1", + NO: "-1", + UNKNOWN: "0", + NO_DATA: "-2", +} diff --git a/scraper/s2-geocode-server.py b/scraper/s2-geocode-server.py index 9d824443..3aeda881 100644 --- a/scraper/s2-geocode-server.py +++ b/scraper/s2-geocode-server.py @@ -13,9 +13,6 @@ load_dotenv() from util import * -locations_worksheet = fetch_worksheet('paper_locations') -verifications_worksheet = fetch_worksheet('verifications') -paper_lookup = fetch_google_lookup('citation_lookup') addresses = AddressBook() app = Flask(__name__, static_url_path="/reports", static_folder=os.path.abspath("reports")) @@ -41,6 +38,7 @@ def list_locations(): @app.route('/api/papers', methods=['GET']) def list_papers(): + paper_lookup = fetch_google_lookup('citation_lookup') return jsonify({ 'papers': paper_lookup, }) @@ -73,6 +71,7 @@ def add_address(): form = request.get_json() print(form) # id, title, institution_1, institution_2, institution_3, institution_4, notes + locations_worksheet = fetch_worksheet('paper_locations') locations_worksheet.append_row([ form['paper_id'], form['title'], @@ -90,6 +89,23 @@ def add_address(): 'status': 'ok' }) +@app.route('/api/verifications', methods=['GET']) +def list_verifications(): + return jsonify({ + 'verifications': fetch_google_lookup('verifications', item_key='paper_id'), + }) + +@app.route('/api/verifications/<dataset>', methods=['GET']) +def list_dataset_verifications(dataset): + rows = fetch_google_sheet_objects('verifications') + verifications = {} + for row in rows: + if row['dataset'] == dataset: + verifications[row['paper_id']] = row + return jsonify({ + dataset: verifications, + }) + @app.route('/api/verify/<sha256>', methods=['GET']) def find_verification(sha256): worksheet = fetch_worksheet('verifications') @@ -114,12 +130,12 @@ def find_verification(sha256): }) @app.route('/api/verify/add', methods=['POST']) -def add_verifications(): +def add_verification(): form = request.get_json() print(form) - keys = verifications_worksheet.row_values(1) - row = [ form[key] if key in form else '' for key in keys ] - verifications_worksheet.append_row(row) + update_or_append_worksheet('verifications', form) + if form['isUnknown']: + update_or_append_worksheet('paper_locations', form) return jsonify({ 'status': 'ok' }) diff --git a/scraper/util.py b/scraper/util.py index 1ee2ad52..96ced430 100644 --- a/scraper/util.py +++ b/scraper/util.py @@ -452,6 +452,21 @@ def fetch_google_lookup(name, item_key='key'): lookup[rec[item_key]] = rec return lookup +def update_or_append_worksheet(name, form): + worksheet = fetch_worksheet(name) + keys = worksheet.row_values(1) + row = [ form[key] if key in form else '' for key in keys ] + try: + cell = worksheet.find(form['paper_id']) + except: + cell = None + + if cell: + for i, item in enumerate(row): + worksheet.update_cell(cell.row, i+1, item) + else: + worksheet.append_row(row) + def load_countries(): countries = read_json('countries.json') lookup = {} |
