summaryrefslogtreecommitdiff
path: root/scraper
diff options
context:
space:
mode:
Diffstat (limited to 'scraper')
-rw-r--r--scraper/client/common/activeLink.component.js16
-rw-r--r--scraper/client/common/autocomplete.component.js208
-rw-r--r--scraper/client/common/index.js4
-rw-r--r--scraper/client/paper/paper.address.js85
-rw-r--r--scraper/client/paper/paper.citations.js3
-rw-r--r--scraper/client/paper/paper.css12
-rw-r--r--scraper/client/paper/paper.manager.js4
-rw-r--r--scraper/client/paper/paper.unknown.js4
8 files changed, 305 insertions, 31 deletions
diff --git a/scraper/client/common/activeLink.component.js b/scraper/client/common/activeLink.component.js
deleted file mode 100644
index 59f63881..00000000
--- a/scraper/client/common/activeLink.component.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react'
-import { NavLink } from 'react-router-dom'
-
-export default function ActiveLink({
- to,
- className = 'navlink',
- children
-}) {
- return (
- <span className={className}>
- <NavLink to={to}>
- {children}
- </NavLink>
- </span>
- )
-}
diff --git a/scraper/client/common/autocomplete.component.js b/scraper/client/common/autocomplete.component.js
new file mode 100644
index 00000000..12419cf1
--- /dev/null
+++ b/scraper/client/common/autocomplete.component.js
@@ -0,0 +1,208 @@
+import React, { Component } from 'react'
+// import PropTypes from 'prop-types'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+
+function formatLabel(label, value) {
+ if (!value) {
+ return label
+ }
+ let len = 0
+ return (
+ <span>
+ {
+ label.split(new RegExp(value.replace(/[-\[\]\(\)\+\*\\\^\$\{\}\.\?\&\|\<\>]/g, ''), "i")) // eslint-disable-line
+ .reduce((prev, current, i) => {
+ if (!i) {
+ len += current.length
+ return [current]
+ }
+ const ret = prev.concat(<b key={i}>{label.substr(len, value.length)}</b>, current)
+ len += value.length + current.length
+ return ret
+ }, [])
+ }
+ </span>
+ )
+}
+
+function sanitizeForAutocomplete(s) {
+ return (s || "")
+ .toLowerCase()
+ .replace(/[^a-zA-Z0-9 ]/g, '')
+ .trim()
+ .replace(/\\/g, '')
+}
+
+class Autocomplete extends Component {
+ constructor(props) {
+ super()
+ this.state = {
+ q: props.q || "",
+ selected: 0,
+ matches: []
+ }
+ this.handleKeyDown = this.handleKeyDown.bind(this)
+ this.handleChange = this.handleChange.bind(this)
+ this.handleCancel = this.handleCancel.bind(this)
+ }
+
+ componentWillMount() {
+ // build index based on what's in the hierarchy
+ const { nodes } = this.props.hierarchy
+ let index = []
+ this.index = index
+ Object.keys(nodes).forEach(key => {
+ const node = nodes[key]
+ if (!key || !node || !node.name || !node.parent) return
+ let { name } = node
+ let prefixName = name
+ if (node.is_attribute) {
+ const parent = nodes[node.parent]
+ if (parent) {
+ prefixName = parent.name + " (" + name + ")"
+ }
+ }
+ index.push([sanitizeForAutocomplete(prefixName), name, node.id])
+ node.synonyms
+ .split("\n")
+ .map(word => word = word.trim())
+ .filter(word => !!word)
+ .forEach(word => index.push([prefixName, name, node.id]))
+ })
+ }
+
+ handleKeyDown(e) {
+ let id
+ 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]
+ e.preventDefault()
+ this.handleSelect(id)
+ return false
+ default:
+ break
+ }
+ }
+
+ handleChange(e) {
+ // search for the given string in our index
+ const q = e.target.value
+ let value = sanitizeForAutocomplete(q)
+ if (!value.length) {
+ this.setState({
+ q,
+ selected: 0,
+ matches: [],
+ })
+ return
+ }
+ const re = new RegExp(value)
+ let matches = []
+ let seen = {}
+ this.index.forEach(pair => {
+ if (seen[pair[2]]) return
+ if (pair[0].match(re)) {
+ seen[pair[2]] = true
+ if (pair[1].indexOf(value) === 0) {
+ matches.unshift(pair[2])
+ } else {
+ matches.push(pair[2])
+ }
+ }
+ })
+ this.setState({
+ q,
+ selected: 0,
+ matches: matches.slice(0, 10),
+ })
+ }
+
+ handleSelect(id) {
+ const { nodes } = this.props.hierarchy
+ const node = nodes[id]
+ if (this.props.onSelect) this.props.onSelect(node)
+ this.setState({ q: "", selected: 0, matches: [] })
+ }
+
+ handleCancel() {
+ if (this.props.onCancel) this.props.onCancel()
+ this.setState({ q: "", selected: 0, matches: [] })
+ }
+
+ render() {
+ // const suggestions = this.state.suggestions.map((suggestion))
+ const { nodes } = this.props.hierarchy
+ const { q, selected } = this.state
+ const matches = this.state.matches.map((match, i) => {
+ const node = nodes[match]
+ const parent = nodes[node.parent]
+ let label
+ if (node.is_attribute) {
+ label = (
+ <span>
+ {formatLabel(parent.name, q)}
+ {' '}<small>{'('}{formatLabel(node.name, q)}{')'}</small>
+ </span>
+ )
+ } else {
+ label = formatLabel(node.name, q)
+ }
+ return (
+ <div
+ key={i}
+ className={selected === i ? 'selected' : ''}
+ onClick={() => this.handleSelect(node.id)}
+ onMouseEnter={() => this.setState({ selected: i })}
+ >
+ {label}
+ </div>
+ )
+ })
+ return (
+ <div className="autocomplete">
+ <input
+ type="text"
+ name="q"
+ value={this.state.q}
+ onKeyDown={this.handleKeyDown}
+ onChange={this.handleChange}
+ autoFocus
+ autoCapitalize="off"
+ autoComplete="off"
+ placeholder="Start typing a name"
+ />
+ <div className="matches">
+ {matches}
+ </div>
+ </div>
+ )
+ }
+}
+
+const mapStateToProps = (state, ownProps) => ({
+ onSelect: ownProps.onSelect,
+})
+
+const mapDispatchToProps = (dispatch) => ({
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(Autocomplete)
diff --git a/scraper/client/common/index.js b/scraper/client/common/index.js
index c5b4af5b..8ced56b3 100644
--- a/scraper/client/common/index.js
+++ b/scraper/client/common/index.js
@@ -1,8 +1,8 @@
-import ActiveLink from './activeLink.component'
import Header from './header.component'
import Footer from './footer.component'
import Loader from './loader.component'
import Gate from './gate.component'
+import Autocomplete from './autocomplete.component'
import { TableObject, TableArray, TableTuples, TableRow, TableCell } from './table.component'
import './common.css'
@@ -16,5 +16,5 @@ export {
TableTuples,
TableRow,
TableCell,
- ActiveLink,
+ Autocomplete,
}
diff --git a/scraper/client/paper/paper.address.js b/scraper/client/paper/paper.address.js
index 2240388f..6d02c3db 100644
--- a/scraper/client/paper/paper.address.js
+++ b/scraper/client/paper/paper.address.js
@@ -4,27 +4,98 @@ import { connect } from 'react-redux'
import * as actions from '../actions'
-// import { Loader } from '../common'
+import { Loader, Autocomplete } from '../common'
+const initialState = {
+ citation: null,
+ institution_1: '',
+ institution_2: '',
+ institution_3: '',
+ institution_4: '',
+}
class PaperAddress extends Component {
+ state = {
+ ...initialState
+ }
+
componentDidMount() {
const { sha256 } = this.props.match.params
this.props.actions.getAddress(sha256)
+ const citation = this.getCitation(sha256)
+ this.setState({ citation })
}
- componentDidUpdate(newProps) {
- const { sha256: oldSha256 } = this.props.match.params
- const { sha256 } = newProps.match.params
- if (sha256 !== oldSha256) {
- this.props.actions.getPaperInfo(this.props.match.params.key)
+ componentDidUpdate(oldProps) {
+ const { sha256 } = this.props.match.params
+ const { address } = this.props.api
+ const { sha256: oldSha256 } = oldProps.match.params
+ const { address: oldAddress } = oldProps.api
+ const oldPaper = oldAddress ? oldAddress.paper : null
+ const paper = address ? address.paper : null
+
+ if (oldSha256 && sha256 !== oldSha256) {
+ console.log('update address')
+ this.props.actions.getAddress(sha256)
+ const citation = this.getCitation(sha256)
+ this.setState({
+ ...initialState,
+ citation
+ })
+ } else if (address && !address.loading && address.paper && (!oldPaper || oldPaper !== address.paper)) {
+ if (paper.error) {
+ const citation = this.getCitation(sha256)
+ this.setState({
+ ...initialState,
+ citation,
+ })
+ } else {
+ console.log(paper)
+ const citation = this.getCitation(sha256)
+ this.setState({
+ citation,
+ institution_1: paper['Institution #1'],
+ institution_2: paper['Institution #2'],
+ institution_3: paper['Institution #3'],
+ institution_4: paper['Institution #4'],
+ })
+ }
+ } else if (oldProps.api.unknownCitations !== this.props.api.unknownCitations) {
+ const citation = this.getCitation(sha256)
+ this.setState({ citation })
}
}
+ getCitation(sha256) {
+ const { paperInfo, unknownCitations } = this.props.api
+ let citation = (unknownCitations.citations || []).find(f => f.id === sha256)
+ if (!citation) {
+ citation = (paperInfo.citations || []).find(f => f.id === sha256)
+ }
+ console.log(sha256, citation)
+ return citation
+ }
+
render() {
// if (!this.props.match.params.key) return null
// if (this.props.api.address.loading) return <Loader />
// if (!this.props.api.paperInfo.dataset) return <div className='paperInfo'>Metadata not found</div>
- return null
+ let { paperInfo, unknownCitations } = this.props.api
+ if (paperInfo.loading || unknownCitations.loading) return <Loader />
+ console.log(this.state)
+ const { citation } = this.state
+ if (!citation) {
+ return <div>Citation not found in this paper</div>
+ }
+ return (
+ <div className='form'>
+ <h3>{citation.title}</h3>
+ <div className='gray'>{citation.id}</div>
+ {this.state.institution_1}
+ {/*
+ <iframe className='pdfViewer' src={citation.pdf} />
+ */}
+ </div>
+ )
}
}
diff --git a/scraper/client/paper/paper.citations.js b/scraper/client/paper/paper.citations.js
index de423a77..1789a815 100644
--- a/scraper/client/paper/paper.citations.js
+++ b/scraper/client/paper/paper.citations.js
@@ -1,6 +1,7 @@
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'
@@ -19,7 +20,7 @@ class PaperCitations extends Component {
let cite = { ...citation }
cite.id = {
_raw: true,
- value: <a href={'/api/address/' + dataset.key + '/' + citation.id}>{citation.id}</a>
+ value: <Link to={'/paper/' + dataset.key + '/address/' + citation.id}>{citation.id}</Link>
}
cite.pdf = {
_raw: true,
diff --git a/scraper/client/paper/paper.css b/scraper/client/paper/paper.css
index cd2f8529..0977dcb3 100644
--- a/scraper/client/paper/paper.css
+++ b/scraper/client/paper/paper.css
@@ -1,5 +1,6 @@
-.paperInfo {
+.form, .paperInfo {
padding: 10px;
+ width: 100%;
}
.citations ul {
@@ -16,4 +17,13 @@
.type {
color: #888;
+}
+.gray {
+ padding: 3px;
+}
+iframe.pdfViewer {
+ margin: 10px 0;
+ width: 100%;
+ height: 50vh;
+ border: 1px solid black;
} \ No newline at end of file
diff --git a/scraper/client/paper/paper.manager.js b/scraper/client/paper/paper.manager.js
index c52722e7..2ac03b01 100644
--- a/scraper/client/paper/paper.manager.js
+++ b/scraper/client/paper/paper.manager.js
@@ -11,8 +11,8 @@ class PaperManager extends Component {
this.props.actions.getPaperInfo(this.props.match.params.key)
}
- componentDidUpdate(newProps) {
- if (this.props.match.params.key !== newProps.match.params.key) {
+ componentDidUpdate(oldProps) {
+ if (this.props.match.params.key !== oldProps.match.params.key) {
this.props.actions.getPaperInfo(this.props.match.params.key)
}
}
diff --git a/scraper/client/paper/paper.unknown.js b/scraper/client/paper/paper.unknown.js
index 7a20f398..0cb7d2da 100644
--- a/scraper/client/paper/paper.unknown.js
+++ b/scraper/client/paper/paper.unknown.js
@@ -1,6 +1,7 @@
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'
@@ -20,7 +21,7 @@ class PaperUnknown extends Component {
let cite = { ...citation }
cite.id = {
_raw: true,
- value: <a href={'/api/address/' + dataset.key + '/' + citation.id}>{citation.id}</a>
+ value: <Link to={'/paper/' + dataset.key + '/address/' + citation.id}>{citation.id}</Link>
}
cite.pdf = {
_raw: true,
@@ -41,7 +42,6 @@ class PaperUnknown extends Component {
)
}
}
- // order={['id', 'pdf', 'year']}
const mapStateToProps = state => ({
api: state.api