diff options
| author | jules@lens <julescarbon@gmail.com> | 2019-04-02 20:36:51 +0200 |
|---|---|---|
| committer | jules@lens <julescarbon@gmail.com> | 2019-04-02 20:36:51 +0200 |
| commit | 1d238346b5609e9454a4917c75631a550b5b43d1 (patch) | |
| tree | 8a936e721e78c7b5948b303e6a1686c96b882d51 /client | |
| parent | b4b58f2279fb01fa0240006c460c0b5ec95c1126 (diff) | |
| parent | f58d41731fc07d94d594d5582aef203564f990ec (diff) | |
Merge branch 'master' of asdf.us:megapixels_dev
Diffstat (limited to 'client')
| -rw-r--r-- | client/applet.js | 7 | ||||
| -rw-r--r-- | client/chart/constants.js | 4 | ||||
| -rw-r--r-- | client/chart/countriesByYear.chart.js | 11 | ||||
| -rw-r--r-- | client/chart/pie.charts.js | 5 | ||||
| -rw-r--r-- | client/index.js | 45 | ||||
| -rw-r--r-- | client/map/index.js | 81 | ||||
| -rw-r--r-- | client/modalImage/index.js | 7 | ||||
| -rw-r--r-- | client/modalImage/modal.css | 69 | ||||
| -rw-r--r-- | client/modalImage/modalImage.container.js | 85 | ||||
| -rw-r--r-- | client/table/citations.table.js | 130 | ||||
| -rw-r--r-- | client/table/file.table.js | 82 | ||||
| -rw-r--r-- | client/table/index.js | 11 | ||||
| -rw-r--r-- | client/table/tabulator.css | 69 | ||||
| -rw-r--r-- | client/tables.js | 96 | ||||
| -rw-r--r-- | client/util/index.js | 9 |
15 files changed, 574 insertions, 137 deletions
diff --git a/client/applet.js b/client/applet.js index 21e1e4fa..db95168a 100644 --- a/client/applet.js +++ b/client/applet.js @@ -4,11 +4,12 @@ import { Container as FaceSearchContainer } from './faceSearch' import { Container as FaceAnalysisContainer } from './faceAnalysis' import { Container as NameSearchContainer } from './nameSearch' import { Container as DatasetListContainer } from './datasetList' +import { CitationsTable, FileTable } from './table' import { CountriesByYear, PieCharts } from './chart' export default class Applet extends Component { render() { - // console.log(this.props) + // console.log(this.props.payload.cmd) switch (this.props.payload.cmd) { case 'face_analysis': return <FaceAnalysisContainer {...this.props} /> @@ -22,6 +23,10 @@ export default class Applet extends Component { return <CountriesByYear {...this.props} /> case 'piechart': return <PieCharts {...this.props} /> + case 'citations': + return <CitationsTable {...this.props} /> + case 'load_file': + return <FileTable {...this.props} /> default: return <pre style={{ color: '#0f0' }}>{'Megapixels'}</pre> } diff --git a/client/chart/constants.js b/client/chart/constants.js index 70375ba3..b916cbd2 100644 --- a/client/chart/constants.js +++ b/client/chart/constants.js @@ -59,6 +59,6 @@ export const institutionOrder = { export const institutionLabels = { 'edu': 'Academic', 'company': 'Commercial', - 'gov': 'Government / Military', - 'mil': 'Government / Military', + 'gov': 'Military / Government', + 'mil': 'Military / Government', }
\ No newline at end of file diff --git a/client/chart/countriesByYear.chart.js b/client/chart/countriesByYear.chart.js index 4257748c..df7a4530 100644 --- a/client/chart/countriesByYear.chart.js +++ b/client/chart/countriesByYear.chart.js @@ -68,11 +68,13 @@ class CountriesByYearChart extends Component { ) ) - citationCountsByYear.push( - [otherCountriesLabel].concat( - yearList.map(year => otherCountries.reduce((a,b) => (a + years[year][b]), 0)) + if (otherCountries.length) { + citationCountsByYear.push( + [otherCountriesLabel].concat( + yearList.map(year => otherCountries.reduce((a,b) => (a + years[year][b]), 0)) + ) ) - ) + } let maxCitationsInAYear = 0 let currentSum = 0 @@ -158,6 +160,7 @@ class CountriesByYearChart extends Component { } }} /> + <div className='caption'>{paper.name}{' dataset citations per country per year. We verified ' + citations.length + ' papers that used the dataset.'}</div> </div> ) } diff --git a/client/chart/pie.charts.js b/client/chart/pie.charts.js index 6e579537..84e85c3a 100644 --- a/client/chart/pie.charts.js +++ b/client/chart/pie.charts.js @@ -17,6 +17,7 @@ class PieCharts extends Component { render() { const { payload } = this.props const { paper, citations } = payload.data + console.log(this.props) if (!citations.length) return null const countries = {} @@ -83,9 +84,10 @@ class PieCharts extends Component { } }} size={{ - height: 336, + height: countryRows.length < 4 ? 316 : 336, }} /> + <span className='chartCaption'>{paper.name}{' dataset citations by country'}</span> </div> <div> <C3Chart @@ -105,6 +107,7 @@ class PieCharts extends Component { height: 316, }} /> + <span className='chartCaption'>{paper.name}{' dataset citations by organization type'}</span> </div> </div> ) diff --git a/client/index.js b/client/index.js index 5c8bc880..10ed8563 100644 --- a/client/index.js +++ b/client/index.js @@ -2,12 +2,13 @@ import React from 'react' import ReactDOM from 'react-dom' import { AppContainer } from 'react-hot-loader' import { Provider } from 'react-redux' +import 'waypoints/lib/noframework.waypoints.min.js'; import { toArray } from './util' import Applet from './applet' import { store } from './store' -import appendTable from './tables' import appendMap from './map' +import { ModalImage } from './modalImage' function appendReactApplet(el, payload) { ReactDOM.render( @@ -19,6 +20,13 @@ function appendReactApplet(el, payload) { ) } +function appendModalImage() { + const el = document.createElement('div') + document.body.appendChild(el) + ReactDOM.render(<ModalImage />, el) + console.log(el) +} + function fetchDataset(payload) { if (payload.command === 'face_analysis') return new Promise(resolve => resolve()) if (payload.dataset === 'info') return new Promise(resolve => resolve()) @@ -33,7 +41,8 @@ function appendApplets(applets) { case 'citations': case 'load_file': el.parentNode.classList.add('wide') - appendTable(el, payload) + appendReactApplet(el, payload) + el.classList.add('loaded') break case 'map': el.parentNode.classList.add('wide') @@ -65,14 +74,17 @@ function runApplets() { let opt = null payload.cmd = cmd payload.partz = cmdPartz + if (payload.cmd === 'load_file') { + payload.url = 'https://nyc3.digitaloceanspaces.com/megapixels/v1' + cmdPartz.shift() + return [el, payload] + } + if (payload.partz.length) { opt = payload.partz.shift().trim() if (opt.indexOf('http') === 0) { dataset = null url = opt } else if (opt.indexOf('assets') === 0) { - let pathname = window.location.pathname.replace('index.html', '') - url = 'https://nyc3.digitaloceanspaces.com/megapixels/v1' + pathname + opt dataset = null // console.log(url) } else { @@ -87,14 +99,15 @@ function runApplets() { if (dataset === 'index.html') { dataset = path.pop() } - console.log('dataset from path:', dataset) + // console.log('dataset from path:', dataset) } else { - console.log('not on a dataset page') + // console.log('not on a dataset page') return [el, payload] } } payload.dataset = dataset payload.url = url + console.log(payload) return [el, payload] }).filter(a => !!a) const withDataset = applets.map(a => a[1].dataset ? a[1] : null).filter(a => !!a) @@ -108,6 +121,24 @@ function runApplets() { } } +function buildWaypoints() { + const element = document.querySelector('.content section:nth-child(2)') + if (element) { + var waypoint = new Waypoint({ + element, + handler: function(direction) { + if (direction === 'down') { + document.body.classList.add('scrolled') + } else { + document.body.classList.remove('scrolled') + } + // console.log(direction) + // console.log('Scrolled to waypoint!') + } + }) + } +} + function main() { const paras = document.querySelectorAll('section p') // if (paras.length) { @@ -119,6 +150,8 @@ function main() { } }) runApplets() + buildWaypoints() + appendModalImage() } main() diff --git a/client/map/index.js b/client/map/index.js index b35ffddb..475ba3c6 100644 --- a/client/map/index.js +++ b/client/map/index.js @@ -37,25 +37,31 @@ const redDot = L.icon({ popupAnchor: [0, -5] // point from which the popup should open relative to the iconAnchor }) -function addMarker(map, latlng, title, addresses, year, pdf) { +function addMarker(map, latlng, citations) { const marker = L.marker(latlng, { icon: redDot }).addTo(map) - let message = [ - "<b>", title, "</b>", - ] - if (pdf && pdf.length) { - message.unshift("<a href='" + pdf[0] + "' target='_blank'>") - message.push("</a>") - } + let message = citations.map(citation => { + const { title, addresses, year, pdf, doi } = citation + let rec = [ + "<b>", title, "</b>", + ] + if (pdf && pdf.length) { + rec.unshift("<a href='" + pdf[0] + "' target='_blank'>") + rec.push("</a>") + } + else if (doi && doi.length) { + rec.unshift("<a href='" + doi[0] + "' target='_blank'>") + rec.push("</a>") + } + if (year) { + rec.push(" (" + year + ")") + } + const addressString = addresses.map(addr => addr.name).join('<br/>') + rec.push("<br>") + rec.push(addressString) + return rec.join("") + }) - const addressString = addresses.map(addr => addr.name).join('<br/>') - message = message.concat([ - "<br>", - addressString, - ]) - if (year) { - message.push(" (" + year + ")") - } - marker.bindPopup(message.join('')) + marker.bindPopup(message.join('<br><br>')) return marker } @@ -73,7 +79,7 @@ function addArc(map, src, dest, arcStyle) { export default function append(el, payload) { const { data } = payload if (!data) return - let { paper, addresses, citations } = data + let { paper, citations } = data let source = [0, 0] let map = L.map(el).setView([25, 0], 2) @@ -87,30 +93,51 @@ export default function append(el, payload) { accessToken: 'pk.eyJ1IjoiZmFuc2FsY3kiLCJhIjoiY2pvN3I1czJwMHF5NDNrbWRoMWpteHlrdCJ9.kMpM5syQUhVjKkn1iVx9fg' }).addTo(map) - if (addresses && addresses.length) { - source = [address[0].lat, address[0].lng].map(n => (parseFloat(n) || 0)) + if (paper.addresses && paper.addresses.length) { + source = [paper.addresses[0].lat, paper.addresses[0].lng].map(n => (parseFloat(n) || 0)) } else { console.error("No address found for root paper") // console.log(data) } // group papers by address + let citationsByAddress = {} citations.forEach(citation => { - console.log(citation) if (!citation.addresses) { - console.log(citation) + // console.log(citation) return } - const citationAddress = citation.addresses[0] + // console.log(citation) + citation.addresses.forEach(address => { + if (!(address.name in citationsByAddress)) { + citationsByAddress[address.name] = { address, citations: []} + } + citationsByAddress[address.name].citations.push(citation) + }) + }) + + Object.keys(citationsByAddress).map(name => { + const { citations: citationList, address: citationAddress } = citationsByAddress[name] + // console.log(name, citationsByAddress[name]) + // console.log(citation) const latlng = [citationAddress.lat, citationAddress.lng].map(n => parseFloat(n)) if (Number.isNaN(latlng[0]) || Number.isNaN(latlng[1])) return - addMarker(map, latlng, citation.title, citation.addresses, citation.year, citation.pdf) - addArc(map, source, latlng, arcStyles[citationAddress.type]) + addMarker(map, latlng, citationList) + const style = { ...arcStyles[citationAddress.type] } + let weight = Math.min(citationList.length, 5) + let opacity = 0.5 + Math.min(citationList.length / 5, 0.5) + if (citationAddress.type !== 'edu') { + weight += 1 + opacity = 1 + } + style.weight = String(weight) + style.opacity = opacity + addArc(map, source, latlng, style) }) - console.log(paper) + // console.log(paper) - const rootMarker = addMarker(map, source, paper.title, addresses, paper.year) + const rootMarker = addMarker(map, source, [paper]) rootMarker.openPopup() // a transparent div to cover the map, so normal scroll events will not be eaten by leaflet diff --git a/client/modalImage/index.js b/client/modalImage/index.js new file mode 100644 index 00000000..ebb3bb72 --- /dev/null +++ b/client/modalImage/index.js @@ -0,0 +1,7 @@ +import ModalImage from './modalImage.container.js' + +import './modal.css' + +export { + ModalImage +}
\ No newline at end of file diff --git a/client/modalImage/modal.css b/client/modalImage/modal.css new file mode 100644 index 00000000..d9180125 --- /dev/null +++ b/client/modalImage/modal.css @@ -0,0 +1,69 @@ +.modal { + position: fixed; + top: 0; left: 0; width: 100%; height: 100%; + background: rgba(0,0,0,0.8); + color: white; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + pointer-events: none; + z-index: -9999999; + transition: opacity 0.2s cubic-bezier(0,0,1,1); +} +.modal.visible { + opacity: 1; + pointer-events: auto; + z-index: 999999999; +} +.modal .inner { + position: absolute; + top: 0; left: 0; + width: 100%; height: 100%; + display: flex; + justify-content: center; + align-items: center; +} +.modal img { + max-width: 80vw; + max-height: 80vh; +} +.modal .caption { + display: block; + text-align: center; +} +.modal .prev { + position: absolute; + top: 0; left: 0; + width: 10%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + color: white; + font-size: 40px; + cursor: pointer; +} +.modal .next { + position: absolute; + top: 0; right: 0; + width: 10%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + color: white; + font-size: 40px; + cursor: pointer; +} +.modal .close { + position: absolute; + top: 0; right: 0; + width: 10vw; height: 10vw; + display: flex; + justify-content: center; + align-items: center; + color: white; + font-size: 40px; + cursor: pointer; +}
\ No newline at end of file diff --git a/client/modalImage/modalImage.container.js b/client/modalImage/modalImage.container.js new file mode 100644 index 00000000..a637deb6 --- /dev/null +++ b/client/modalImage/modalImage.container.js @@ -0,0 +1,85 @@ +import React, { Component } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { ReactTabulator } from 'react-tabulator' + +import { toArray, toTuples, domainFromUrl } from '../util' +import { Loader } from '../common' + +import csv from 'parse-csv' + +class ModalImage extends Component { + state = { + visible: true, + images: [], + index: 0, + } + + componentDidMount() { + const images = toArray(document.querySelectorAll('.image img')) + // console.log(images) + images.forEach((img, i) => { + img.addEventListener('click', () => this.loadImage(i)) + }) + this.setState({ images }) + document.body.addEventListener('keydown', e => { + if (document.activeElement && document.activeElement !== document.body) { + return null + } + // console.log(e.keyCode) + switch (e.keyCode) { + case 37: // left + this.prev() + break + case 39: // right + this.next() + break + default: + break + } + }) + } + + loadImage(index) { + this.setState({ visible: true, index }) + } + + prev() { + const { index, images } = this.state + this.setState({ index: (images.length + index - 1) % images.length }) + } + + next() { + const { index, images } = this.state + this.setState({ index: (index + 1) % images.length }) + } + + close() { + this.setState({ visible: false }) + } + + render() { + const { images, index, visible } = this.state + if (!images.length) return null + const img = images[index] + let caption = null + const sib = img.nextSibling + if (sib && sib.classList.contains('caption')) { + caption = sib.innerText + } + return ( + <div className={visible ? 'modal visible' : 'modal'}> + <div className='inner'> + <div className='centered'> + <img src={img.src} /> + {caption && <div class='caption'>{caption}</div>} + </div> + </div> + <div onClick={() => this.prev()}className='prev'>{'<'}</div> + <div onClick={() => this.next()} className='next'>{'>'}</div> + <div onClick={() => this.close()} className='close'>{'x'}</div> + </div> + ) + } +} +export default ModalImage diff --git a/client/table/citations.table.js b/client/table/citations.table.js new file mode 100644 index 00000000..178cc65b --- /dev/null +++ b/client/table/citations.table.js @@ -0,0 +1,130 @@ +import React, { Component } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { ReactTabulator } from 'react-tabulator' +import { saveAs } from 'file-saver' + +import { Loader } from '../common' +import { toArray, toTuples, domainFromUrl } from '../util' + +export const citationsColumns = [ + { title: 'Title', field: 'title', sorter: 'string' }, + { title: 'Institution', field: 'institution', sorter: 'string', }, + { title: 'Country', field: 'country', sorter: 'string', width: 140, }, + { title: 'Year', field: 'year', sorter: 'number', width: 70 }, + { title: 'PDF', field: 'pdf_text', formatter: 'link', + formatterParams: { target: "_blank", urlField: 'pdf_link', }, + sorter: 'string', width: 100 }, +] + +class CitationsTable extends Component { + state = { + q: '', + formattedCitations: [], + filteredCitations: [], + } + + componentDidMount(){ + this.updateCitations() + } + componentDidUpdate(oldProps){ + if (this.props.payload.data.citations !== oldProps.payload.data.citations) { + this.updateCitations() + } + } + updateCitations(){ + const { paper, citations } = this.props.payload.data + if (!citations.length) this.setState({ formattedCitations: [] }) + console.log(citations.filter(a => a.title.match('Coarse'))) + const formattedCitations = citations.sort((a,b) => a.title.localeCompare(b.title)).map(citation => { + const pdf_link = (citation.pdf && citation.pdf.length) + ? citation.pdf[0] + : (citation.doi && citation.doi.length) + ? citation.doi[0] + : 'https://www.semanticscholar.org/paper/' + citation.id + let pdf_text = domainFromUrl(pdf_link) + return { + title: citation.title, + institution: citation.addresses.map(a => a.name).sort().join('; '), + country: Array.from(new Set(citation.addresses.map(a => a.country))).sort().join('; '), + year: citation.year, + pdf_link, pdf_text, + } + }) + this.setState({ + formattedCitations, + filteredCitations: formattedCitations, + }) + } + + updateFilter(q) { + const { formattedCitations } = this.state + if (!q.length) { + this.setState({ q, filteredCitations: formattedCitations }) + } else { + let q_re = new RegExp('(' + q.replace(/\s+/g, ' ').trim().replace(' ', '|') + ')', 'gi') + let filteredCitations = formattedCitations.filter(citation => ( + citation.title.match(q_re) || + citation.institution.match(q_re) || + citation.country.match(q_re) + )) + this.setState({ q, filteredCitations }) + } + } + + download() { + const { formattedCitations } = this.state + const fn = this.props.payload.data.paper.key + '.csv' + const titles = citationsColumns.map(c => c.title) + const fields = citationsColumns.map(c => c.formatterParams ? c.formatterParams.urlField : c.field) + const rows = formattedCitations.map(citation => { + const row = fields.map(field => citation[field]).map(data => { + switch (typeof data) { + case 'number': + return String(data) + default: + return '\"' + String(data) + '\"' + } + }) + return row.join(",") + }) + + const blob = new Blob([ + [ + titles.join(','), + ...rows, + ].join('\n') + ], {type: "text/csv;charset=utf-8"}); + saveAs(blob, fn); + } + + render() { + const { formattedCitations, filteredCitations } = this.state + if (!formattedCitations.length) return <Loader /> + return ( + <div className='citationBrowser'> + <div className='citationHeader'> + <input + type="text" + value={this.state.q} + onChange={e => this.updateFilter(e.target.value)} + className='q' + placeholder='Enter text to search citations...' + /> + <span className='download' onClick={() => this.download()}>Download CSV</span> + </div> + <ReactTabulator + columns={citationsColumns} + data={filteredCitations} + options={{ + height: Math.max(104, Math.min(37 * formattedCitations.length + 29, 311)), + layout: 'fitColumns', + placeholder: formattedCitations.length ? '' : 'Nothing matches your query', + }} + /> + </div> + ) + } +} + +export default CitationsTable diff --git a/client/table/file.table.js b/client/table/file.table.js new file mode 100644 index 00000000..db53243a --- /dev/null +++ b/client/table/file.table.js @@ -0,0 +1,82 @@ +import React, { Component } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { ReactTabulator } from 'react-tabulator' + +import { toArray, toTuples, domainFromUrl } from '../util' +import { Loader } from '../common' + +import csv from 'parse-csv' + +class FileTable extends Component { + state = { + keys: [], + data: [], + columns: [], + } + + componentDidMount() { + const { payload } = this.props + console.log(payload.url) + fetch(payload.url, { mode: 'cors' }) + .then(r => r.text()) + .then(text => { + try { + const keys = text.split('\n')[0].split(',').map(s => s.trim().replace(/\"/,'')) + const data = csv.toJSON(text, { headers: { included: true } }) + // console.log(data) + const columns = this.getColumns(keys, data, payload.fields) + this.setState({ keys, data, columns }) + } catch (e) { + console.error("error making json:", payload.url) + console.error(e) + } + }) + } + + getColumns(keys, data, fields) { + let titles = fields.length ? fields[0].split(', ') : keys + let numberFields = [] + let columns = keys.map((field, i) => { + const title = titles[i] || field + if (field.match('url')) { + let textField = field.replace('url', 'label') + data.forEach(el => el[textField] = domainFromUrl(el[field])) + return { + title, + field: textField, + formatter: 'link', + formatterParams: { target: "_blank", urlField: field, }, + sorter: 'string' + } + } + switch (field) { + case 'images': + case 'year': + return { title, field: field.toLowerCase(), sorter: 'number' } + default: + return { title, field: field.toLowerCase(), sorter: 'string' } + } + }) + return columns + } + + render() { + const { payload } = this.props + if (!this.state.data.length) { + return <Loader /> + } + return ( + <ReactTabulator + columns={this.state.columns} + data={this.state.data} + options={{ + height: Math.min(37 * this.state.data.length + 29, 311), + layout: 'fitColumns', + placeholder: 'No Data Set', + }} + /> + ) + } +} +export default FileTable diff --git a/client/table/index.js b/client/table/index.js new file mode 100644 index 00000000..c741f33e --- /dev/null +++ b/client/table/index.js @@ -0,0 +1,11 @@ +import 'react-tabulator/lib/styles.css' +import 'react-tabulator/lib/css/tabulator_midnight.css' +import './tabulator.css' + +import CitationsTable from './citations.table' +import FileTable from './file.table' + +export { + CitationsTable, + FileTable, +}
\ No newline at end of file diff --git a/client/table/tabulator.css b/client/table/tabulator.css new file mode 100644 index 00000000..95768976 --- /dev/null +++ b/client/table/tabulator.css @@ -0,0 +1,69 @@ +.tabulator { + border-left: 1px solid #333; + border-bottom: 1px solid #333; +} +.tabulator-row.tabulator-row-odd { + background-color: #222; +} +.tabulator-row.tabulator-row-even { + background-color: #333; +} +.desktop .tabulator-row.tabulator-selectable.tabulator-row-even:hover { + cursor: arrow; + background-color: #333; +} +.desktop .tabulator-row.tabulator-selectable.tabulator-row-odd:hover { + cursor: arrow; + background-color: #222; +} +.tabulator-row .tabulator-cell { + border-right: 1px solid #444; + padding: 8px; +} +.tabulator .tabulator-header { + border-bottom: 0; +} +.tabulator .tabulator-header .tabulator-col { + border-right: 1px solid #444; +} +.tabulator .tabulator-tableHolder .tabulator-table { + background-color: #333; +} +.multi-value-formatter-content span { + border: 0; + padding: 0 5px 0 0; +} + +.citationBrowser { +} +.citationBrowser .q { + max-width: 400px; + margin-bottom: 10px; + background-image: url(/assets/img/icon-search.png); + background-position: 380px center; + background-repeat: no-repeat; + box-shadow: 0px 2px 4px rgba(0,0,0,0.2); + border: 0; +} + +.citationHeader { + width: 100%; + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; +} +span.download { + display: block; + font-size: 13px; + color: #ddd; + cursor: pointer; + background: #333; + padding: 5px 8px; + border-radius: 5px; + transition: all 0.2s; +} +.desktop span.download:hover { + color: #fff; + background: #666; +}
\ No newline at end of file diff --git a/client/tables.js b/client/tables.js deleted file mode 100644 index 3b53b5db..00000000 --- a/client/tables.js +++ /dev/null @@ -1,96 +0,0 @@ -import Tabulator from 'tabulator-tables' -import csv from 'parse-csv' - -const datasetColumns = [ - { title: 'Title', field: 'title', sorter: 'string' }, - { title: 'Images', field: 'images', sorter: 'number' }, - { title: 'People', field: 'people', sorter: 'number' }, - { title: 'Year', field: 'year', sorter: 'number' }, - { title: 'Citations', field: 'citations', sorter: 'number' }, - { title: 'Influenced', field: 'influenced', sorter: 'number' }, - // { title: 'Origin', field: 'origin', sorter: 'string' }, -] -const citationsColumns = [ - { title: 'Title', field: 'title', sorter: 'string' }, - { title: 'Institution', field: 'institution', sorter: 'string' }, - { title: 'Country', field: 'country', sorter: 'string', width: 140 }, - { title: 'Year', field: 'year', sorter: 'number', width: 70 }, - { title: 'PDF', field: 'pdf', formatter: 'link', - formatterParams: { target: "_blank", urlField: 'pdf', }, - sorter: 'string', width: 100 }, -] - -function getColumns(payload) { - let { cmd, url, fields } = payload - if (cmd === 'citations') { - return citationsColumns - } - if (url && url.match('datasets.csv')) { - return datasetColumns - } - return ((fields && fields.length) ? fields[0] : '').split(', ').map(field => { - switch (field) { - default: - return { title: field, field: field.toLowerCase(), sorter: 'string' } - } - }) -} - -function getCitations(dataset) { - // console.log(dataset.citations) - // console.log(dataset.citations.map(d => [d.pdf, d.doi])) - return dataset.citations.map(citation => ({ - title: citation.title, - institution: citation.addresses[0].name, - country: citation.addresses[0].country, - year: citation.year, - pdf: (citation.pdf && citation.pdf.length) - ? citation.pdf[0] - : (citation.doi && citation.doi.length) - ? citation.doi[0] - : "", - })) -} - -export default function append(el, payload) { - const columns = getColumns(payload) - // console.log(columns) - const table = new Tabulator(el, { - height: '311px', - layout: 'fitColumns', - placeholder: 'No Data Set', - columns, - }) - // let path = payload.opt - // console.log(path, columns) - - if (payload.cmd === 'citations') { - let { data } = payload - if (!data) return null - const citations = getCitations(data) - // console.log(citations) - table.setData(citations) - el.classList.add('loaded') - } else { - fetch(payload.url, { mode: 'cors' }) - .then(r => r.text()) - .then(text => { - try { - // console.log(text) - const data = csv.toJSON(text, { headers: { included: true } }) - // console.log(data) - table.setData(data) - el.classList.add('loaded') - } catch (e) { - - console.error("error making json:", payload.url) - console.error(e) - // console.log(text) - } - }) - } - - // if (fields && fields.length > 1 && fields[1].indexOf('filter')) { - // const filter = fields[1].split(' ') - // } -} diff --git a/client/util/index.js b/client/util/index.js index 87d32ebb..e90e5466 100644 --- a/client/util/index.js +++ b/client/util/index.js @@ -63,6 +63,15 @@ export const percent = n => (n * 100).toFixed(1) + '%' export const px = (n, w) => Math.round(n * w) + 'px' export const clamp = (n, a, b) => n < a ? a : n < b ? n : b +export const domainFromUrl = url => { + const partz = url.split('/')[2].split('.') + if (partz.length > 2 && partz[partz.length - 2].length == 2) { + return partz.slice(-3).join('.') + } else { + return partz.slice(-2).join('.') + } +} + /* URLs */ export const preloadImage = opt => { |
