From 5db4ee5e5be18d08dd9da863fc54fb4593b19068 Mon Sep 17 00:00:00 2001 From: Brendan Sudol Date: Tue, 27 Jun 2017 13:12:13 -0400 Subject: [PATCH] fix trend dl, refactor trend cntnr (#1075) --- content/crimes/violent-crime.yml | 1 - src/components/AboutTheData.js | 2 +- src/components/TrendChart.js | 89 +++++++++--------------- src/containers/TrendContainer.js | 106 ++++++++++++++--------------- src/util/content.js | 6 +- test/components/TrendChart.test.js | 2 +- 6 files changed, 88 insertions(+), 118 deletions(-) diff --git a/content/crimes/violent-crime.yml b/content/crimes/violent-crime.yml index 404347ca..9733787b 100644 --- a/content/crimes/violent-crime.yml +++ b/content/crimes/violent-crime.yml @@ -23,7 +23,6 @@ caveats: text: | In 2013, the FBI started collecting rape data under a revised definition and removed “forcible” from the offense name. All reported rape incidents—whether collected under the revised definition or the legacy definition—are presented here. Since the rape definition changed, some state and local law enforcement agencies have continued to report incidents with the legacy definition, because they haven’t been able to change their records management systems to accommodate the change. - links: - url: https://ucr.fbi.gov/ucr-publications text: "FBI: Uniform Crime Reporting Publications" diff --git a/src/components/AboutTheData.js b/src/components/AboutTheData.js index 8ddcc631..8ae9d2a0 100644 --- a/src/components/AboutTheData.js +++ b/src/components/AboutTheData.js @@ -61,7 +61,7 @@ class AboutTheData extends React.Component {

Further reading

-
    +
      {links.map((l, i) =>
    • {l.text} diff --git a/src/components/TrendChart.js b/src/components/TrendChart.js index bc07576a..69461e58 100644 --- a/src/components/TrendChart.js +++ b/src/components/TrendChart.js @@ -35,19 +35,21 @@ class TrendChart extends React.Component { } } - createSeries = ({ crimes, data, places }) => { + createSeries = (crimes, data, places) => { const [dates, rates] = [[], []] const series = places - .map(p => - crimes.map(c => { + .map(place => + crimes.map(crime => { const gaps = [] const segments = [[]] - const values = data.filter(d => d[p][c] && d[p][c].count).map(d => ({ - date: d.date, - year: d.year, - population: d[p].population, - ...d[p][c], - })) + const values = data + .filter(d => d[place][crime] && d[place][crime].count) + .map(d => ({ + date: d.date, + year: d.year, + population: d[place].population, + ...d[place][crime], + })) values.forEach(d => { if (d.count && d.count !== 0) { @@ -60,13 +62,7 @@ class TrendChart extends React.Component { rates.push(d.rate) }) - return { - crime: c, - gaps, - place: p, - segments, - values, - } + return { crime, gaps, place, segments, values } }), ) .reduce((a, n) => a.concat(n), []) @@ -74,10 +70,6 @@ class TrendChart extends React.Component { return { dates, rates, series } } - forgetValue = () => { - this.setState({ hover: null }) - } - updateYear = year => { this.setState({ yearSelected: year }) } @@ -92,67 +84,59 @@ class TrendChart extends React.Component { } render() { - const { crime, colors, data, showMarkers, since, size, until } = this.props + const { crime, colors, data, places, since, size, until } = this.props const { hover, svgParentWidth, yearSelected } = this.state + const { margin } = size const color = scaleOrdinal(colors) const svgWidth = svgParentWidth || size.width const svgHeight = svgWidth / 2.25 const width = svgWidth - margin.left - margin.right const height = svgHeight - margin.top - margin.bottom - const xPadding = svgWidth < 500 ? 15 : 30 + const xPad = svgWidth < 500 ? 15 : 30 const parse = timeParse('%Y') - const places = Object.keys(data[0]).filter( - k => k !== 'year' && k !== 'date', - ) const isRape = crime === 'rape' const crimes = [isRape ? 'rape-legacy' : crime] if (isRape) crimes.push('rape-revised') const dataByYear = data.map(d => ({ ...d, date: parse(d.year) })) - const { dates, rates, series } = this.createSeries({ - crimes, - data: dataByYear, - places, - }) - - const x = scaleTime() - .domain(extent(dates)) - .range([xPadding, width - xPadding]) + const newSeries = this.createSeries(crimes, dataByYear, places) + const { dates, rates, series } = newSeries + const x = scaleTime().domain(extent(dates)).range([xPad, width - xPad]) const y = scaleLinear().domain([0, max(rates)]).range([height, 0]).nice() - let active = series.map(d => ({ - crime: d.crime, - place: d.place, + let active = series.map(({ crime: c, place, values }) => ({ + crime: c, + place, ...(yearSelected - ? d.values.find(v => v.year === yearSelected) - : d.values[d.values.length - 1]), + ? values.find(v => v.year === yearSelected) + : values[values.length - 1]), })) if (!yearSelected && hover) { const bisectDate = bisector(d => d.date).left const x0 = x.invert(hover.x * width) active = series - .map(({ crime: c, place: p, values }) => { + .map(({ crime: c, place, values }) => { if (x0.getFullYear() < 2013 && c === 'rape-revised') return null const i = bisectDate(values, x0, 1) const [d0, d1] = [values[i - 1], values[i]] - - return { - crime: c, - place: p, - ...(d0 && d1 && x0 - d0.date > d1.date - x0 ? d1 : d0), - } + const pt = d0 && d1 && x0 - d0.date > d1.date - x0 ? d1 : d0 + return { crime: c, place, ...pt } }) .filter(s => s) } const dataHover = places - .map(p => + .map(place => active.filter( - a => a.place === p && a.crime !== 'rape-revised' && a.rate && a.count, + a => + a.place === place && + a.crime !== 'rape-revised' && + a.rate && + a.count, ), ) .reduce((a, n) => a.concat(n), []) @@ -182,13 +166,7 @@ class TrendChart extends React.Component { tickCt={svgWidth < 500 ? 4 : 8} /> - + {until > 2013 && crime === 'rape' && { - let chart - const { error, loading } = summaries +const getContent = ({ crime, place, since, summaries, until }) => { + const { loading, error } = summaries + + if (loading) return + if (error) return + + const data = mungeSummaryData({ + crime, + summaries: summaries.data, + place, + since, + until, + }) + + if (!data || data.length === 0) return + + const places = Object.keys(summaries.data) + const fname = `${place}-${crime}-${since}-${until}` + const title = + `Reported ${pluralize(crime)} in ` + + `${startCase(place)}, ${since}-${until}` + + const readme = generateCrimeReadme({ crime, title }) + const dlData = data.map(d => { + const placeData = places.map(p => ({ [p]: { ...d[p][crime] } })) + return { year: d.year, ...Object.assign(...placeData) } + }) + const download = [ - { - content: generateCrimeReadme({ - crime, - title: `Reported ${pluralize(crime)} in ${startCase( - place, - )}, ${since}-${until}`, - }), - filename: 'README.md', - }, + { content: readme, filename: 'README.md' }, + { data: dlData, filename: `${fname}.csv` }, ] - if (loading) chart = - else if (error) chart = - else { - const data = mungeSummaryData({ - crime, - summaries: summaries.data, - place, - since, - until, - }) - if (!data || data.length === 0) chart = - else { - download.push({ - data, - filename: `${place}-${crime}-${since}-${until}.csv`, - }) - chart = ( -
      - - -
      - ) - } - } + return ( +
      + + +
      + ) +} + +const TrendContainer = props => { + const { crime, place, placeType, since, summaries, until } = props + const isReady = !summaries.loading return (

      - {startCase(crime)} rate in {startCase(place)},{' '} - {since}–{until} + {startCase(crime)} rate in {startCase(place)}, {since}-{until}

      - {chart} + {getContent(props)}
      - {!loading && + {isReady && { const crimeJson = content.crimes[crime] const caveats = crimeJson.caveats - .map(c => `### ${c.heading}\n${c.text}\n`) - .join('\n') + .map(c => `### ${c.heading}\n\n${c.text}`) + .join('\n\n') const links = crimeJson.links.map(l => `* [${l.text}](${l.url})`).join('\n') - return `# ${title}\n## Caveats\n${caveats}\n## Links\n${links}` + return `# ${title}\n\n## Caveats\n\n${caveats}\n\n## Links\n\n${links}` } export default content diff --git a/test/components/TrendChart.test.js b/test/components/TrendChart.test.js index fc4d7250..123db18f 100644 --- a/test/components/TrendChart.test.js +++ b/test/components/TrendChart.test.js @@ -19,7 +19,7 @@ describe('TrendChart', () => { since: 2012, until: 2014, crime: 'violent-crime', - place: 'united-states', + places: ['united-states'], } let chart