From 85d044df094cf72285993765eb15cbd045d63e51 Mon Sep 17 00:00:00 2001
From: esikkala <esko.ikkala@aalto.fi>
Date: Mon, 4 Oct 2021 18:48:06 +0300
Subject: [PATCH] Prepare data handling for BarChartRace

---
 .../components/facet_results/BarChartRace.js  | 109 ++++++++++--------
 .../perspectives/sampo/Perspective1.js        |   8 +-
 src/client/reducers/sampo/perspective1.js     |   1 +
 src/server/sparql/Mappers.js                  |  29 ++++-
 src/server/sparql/SparqlApi.js                |   4 +-
 .../sparql/sampo/BackendSearchConfig.js       |  14 ++-
 .../SparqlQueriesPerspective1.js              |  16 ++-
 7 files changed, 129 insertions(+), 52 deletions(-)

diff --git a/src/client/components/facet_results/BarChartRace.js b/src/client/components/facet_results/BarChartRace.js
index 59f81767..15ff47aa 100644
--- a/src/client/components/facet_results/BarChartRace.js
+++ b/src/client/components/facet_results/BarChartRace.js
@@ -380,26 +380,46 @@ const allData = {
 
 const stepDuration = 2000
 
-let year = 2002
+let year = 1100
 
 class BarChartRace extends React.Component {
     componentDidMount = () => {
+      this.props.fetchData({
+        resultClass: this.props.resultClass,
+        facetClass: this.props.facetClass
+      })
+      this.initChart()
+    }
+
+    componentDidUpdate = prevProps => {
+      if (prevProps.resultUpdateID !== this.props.resultUpdateID) {
+        this.playAnimation()
+      }
+    }
+
+    componentWillUnmount () {
+      if (this.chart) {
+        this.chart.dispose()
+      }
+    }
+
+    initChart = () => {
       // Create root element
       // https://www.amcharts.com/docs/v5/getting-started/#Root_element
       const root = am5.Root.new('chartdiv')
 
-      root.numberFormatter.setAll({
-        numberFormat: '#a',
+      // root.numberFormatter.setAll({
+      //   numberFormat: '#a',
 
-        // Group only into M (millions), and B (billions)
-        bigNumberPrefixes: [
-          { number: 1e6, suffix: 'M' },
-          { number: 1e9, suffix: 'B' }
-        ],
+      //   // Group only into M (millions), and B (billions)
+      //   bigNumberPrefixes: [
+      //     { number: 1e6, suffix: 'M' },
+      //     { number: 1e9, suffix: 'B' }
+      //   ],
 
-        // Do not use small number prefixes at all
-        smallNumberPrefixes: []
-      })
+      //   // Do not use small number prefixes at all
+      //   smallNumberPrefixes: []
+      // })
 
       // Set themes
       // https://www.amcharts.com/docs/v5/concepts/themes/
@@ -479,7 +499,7 @@ class BarChartRace extends React.Component {
       })
 
       this.label = chart.plotContainer.children.push(am5.Label.new(root, {
-        text: '2002',
+        text: '10',
         fontSize: '8em',
         opacity: 0.2,
         x: am5.p100,
@@ -488,40 +508,12 @@ class BarChartRace extends React.Component {
         centerX: am5.p100
       }))
 
-      // update data with values each 1.5 sec
-      const interval = setInterval(() => {
-        year++
-
-        if (year > 2018) {
-          clearInterval(interval)
-          clearInterval(this.sortInterval)
-        }
-
-        this.updateData()
-      }, stepDuration)
-
-      this.setInitialData()
-      setTimeout(() => {
-        year++
-        this.updateData()
-      }, 50)
-
-      // Make stuff animate on load
-      // https://www.amcharts.com/docs/v5/concepts/animations/
-      this.series.appear(1000)
-      chart.appear(1000, 100)
-
       this.chart = chart
     }
 
-    componentWillUnmount () {
-      if (this.chart) {
-        this.chart.dispose()
-      }
-    }
-
     setInitialData = () => {
-      const d = allData[year]
+      // const d = allData[year]
+      const d = this.props.results[year]
 
       for (const n in d) {
         this.series.data.push({ network: n, value: d[n] })
@@ -531,13 +523,13 @@ class BarChartRace extends React.Component {
 
     updateData = () => {
       let itemsWithNonZero = 0
-
-      if (allData[year]) {
+      // if (allData[year]) {
+      if (this.props.results[year]) {
         this.label.set('text', year.toString())
 
         am5.array.each(this.series.dataItems, dataItem => {
           const category = dataItem.get('categoryY')
-          const value = allData[year][category]
+          const value = this.props.results[year][category]
 
           if (value > 0) {
             itemsWithNonZero++
@@ -561,6 +553,33 @@ class BarChartRace extends React.Component {
       }
     }
 
+    playAnimation = () => {
+      // update data with values each 1.5 sec
+      const interval = setInterval(() => {
+        // year++
+        year += 10
+
+        if (year > 2018) {
+          clearInterval(interval)
+          clearInterval(this.sortInterval)
+        }
+
+        this.updateData()
+      }, stepDuration)
+
+      this.setInitialData()
+      setTimeout(() => {
+        // year++
+        year += 10
+        this.updateData()
+      }, 50)
+
+      // Make stuff animate on load
+      // https://www.amcharts.com/docs/v5/concepts/animations/
+      this.series.appear(1000)
+      this.chart.appear(1000, 100)
+    }
+
     // Get series item by category
     getSeriesItem = category => {
       for (let i = 0; i < this.series.dataItems.length; i++) {
diff --git a/src/client/components/perspectives/sampo/Perspective1.js b/src/client/components/perspectives/sampo/Perspective1.js
index a46d024e..62116052 100644
--- a/src/client/components/perspectives/sampo/Perspective1.js
+++ b/src/client/components/perspectives/sampo/Perspective1.js
@@ -265,7 +265,13 @@ const Perspective1 = props => {
       <Route
         path={`${rootUrl}/${perspective.id}/faceted-search/bar_chart_race`}
         render={() =>
-          <BarChartRace />}
+          <BarChartRace
+            fetchData={props.fetchResults}
+            resultClass='productionsByDecadeAndCountry'
+            facetClass='perspective1'
+            resultUpdateID={props.perspectiveState.resultUpdateID}
+            results={props.perspectiveState.results}
+          />}
       />
       <Route
         path={`${rootUrl}/${perspective.id}/faceted-search/network`}
diff --git a/src/client/reducers/sampo/perspective1.js b/src/client/reducers/sampo/perspective1.js
index 6425548a..48b3ca7d 100644
--- a/src/client/reducers/sampo/perspective1.js
+++ b/src/client/reducers/sampo/perspective1.js
@@ -286,6 +286,7 @@ const resultClasses = new Set([
   'placesMsMigrations',
   'placesMsMigrationsDialog',
   'productionTimespanLineChart',
+  'productionsByDecadeAndCountry',
   'eventLineChart',
   'manuscriptInstancePageNetwork',
   'manuscriptFacetResultsNetwork',
diff --git a/src/server/sparql/Mappers.js b/src/server/sparql/Mappers.js
index 6e5a9426..96c6ab02 100644
--- a/src/server/sparql/Mappers.js
+++ b/src/server/sparql/Mappers.js
@@ -129,7 +129,7 @@ export const mapMultipleLineChart = ({ sparqlBindings, config }) => {
   if (config && config.fillEmptyValues) {
     //  fill the missing years with zeroes
     const valmax = Math.max(...category)
-    for (var i = Math.min(...category); i <= valmax; i++) {
+    for (let i = Math.min(...category); i <= valmax; i++) {
       for (const p in res) {
         if (p !== 'category') {
           res[p][i] = 0
@@ -149,7 +149,7 @@ export const mapMultipleLineChart = ({ sparqlBindings, config }) => {
 
   // sort by year and remove empty sequence at start and end
   for (const p in res) {
-    var arr = Object.entries(res[p])
+    const arr = Object.entries(res[p])
       .map(p => [parseFloat(p[0]), p[1]])
       .sort((a, b) => ((a[0] < b[0]) ? -1 : ((a[0] > b[0]) ? 1 : 0)))
     res[p] = trimResult(arr)
@@ -242,3 +242,28 @@ const recursiveSortAndSelectChildren = nodes => {
   })
   return nodes
 }
+
+export const toBarChartRaceFormat = ({ data }) => {
+  const object = data.reduce((obj, item) => {
+    if (Array.isArray(item.productionPlaceCountry)) {
+      const countries = item.productionPlaceCountry.reduce((obj, item) => {
+        return {
+          ...obj,
+          [item.id]: parseInt(item.manuscriptCount)
+        }
+      }, {})
+      return {
+        ...obj,
+        [item.id]: countries
+      }
+    } else {
+      return {
+        ...obj,
+        [item.id]: {
+          [item.productionPlaceCountry.id]: parseInt(item.productionPlaceCountry.manuscriptCount)
+        }
+      }
+    }
+  }, {})
+  return object
+}
diff --git a/src/server/sparql/SparqlApi.js b/src/server/sparql/SparqlApi.js
index d4a0aec9..af8ff875 100644
--- a/src/server/sparql/SparqlApi.js
+++ b/src/server/sparql/SparqlApi.js
@@ -33,11 +33,11 @@ export const runSelectQuery = async ({
       if (resultMapper) {
 
       }
-      const mappedResults = resultMapperConfig
+      let mappedResults = resultMapperConfig
         ? resultMapper({ sparqlBindings: response.data.results.bindings, config: resultMapperConfig })
         : resultMapper(response.data.results.bindings)
       if (postprocess) {
-        postprocess.func({ data: mappedResults, config: postprocess.config })
+        mappedResults = postprocess.func({ data: mappedResults, config: postprocess.config })
       }
       return {
         data: mappedResults,
diff --git a/src/server/sparql/sampo/BackendSearchConfig.js b/src/server/sparql/sampo/BackendSearchConfig.js
index b83863a7..3d1bb319 100644
--- a/src/server/sparql/sampo/BackendSearchConfig.js
+++ b/src/server/sparql/sampo/BackendSearchConfig.js
@@ -13,6 +13,7 @@ import {
   expressionProperties,
   collectionProperties,
   productionsByDecadeQuery,
+  productionsByDecadeAndCountryQuery,
   eventsByDecadeQuery,
   manuscriptInstancePageNetworkLinksQuery,
   manuscriptFacetResultsNetworkLinksQuery,
@@ -60,7 +61,8 @@ import {
   mapPlaces,
   mapLineChart,
   mapMultipleLineChart,
-  linearScale
+  linearScale,
+  toBarChartRaceFormat
 } from '../Mappers'
 
 export const backendSearchConfig = {
@@ -186,6 +188,16 @@ export const backendSearchConfig = {
       fillEmptyValues: false
     }
   },
+  productionsByDecadeAndCountry: {
+    perspectiveID: 'perspective1',
+    q: productionsByDecadeAndCountryQuery,
+    filterTarget: 'manuscript',
+    resultMapper: makeObjectList,
+    postprocess: {
+      func: toBarChartRaceFormat,
+      config: null
+    }
+  },
   eventLineChart: {
     perspectiveID: 'perspective1',
     q: eventsByDecadeQuery,
diff --git a/src/server/sparql/sampo/sparql_queries/SparqlQueriesPerspective1.js b/src/server/sparql/sampo/sparql_queries/SparqlQueriesPerspective1.js
index b6ae656a..e6fd9310 100644
--- a/src/server/sparql/sampo/sparql_queries/SparqlQueriesPerspective1.js
+++ b/src/server/sparql/sampo/sparql_queries/SparqlQueriesPerspective1.js
@@ -528,12 +528,26 @@ export const migrationsDialogQuery = `
 
 export const productionsByDecadeQuery = `
   SELECT ?category (COUNT (DISTINCT ?instance) as ?count) WHERE {
-    <FILTER>
+   
     ?instance ^crm:P108_has_produced/crm:P4_has_time-span/mmm-schema:decade ?category .
   }
   GROUP BY ?category
   ORDER BY ?category
 `
+
+export const productionsByDecadeAndCountryQuery = `
+  SELECT ?id ?productionPlaceCountry__id ?productionPlaceCountry__prefLabel (count(?manuscript) as ?productionPlaceCountry__manuscriptCount) WHERE {
+    <FILTER>
+    []  crm:P108_has_produced ?manuscript ;
+        crm:P7_took_place_at/gvp:broaderPreferred* ?productionPlaceCountry__id ; 
+        crm:P4_has_time-span/mmm-schema:decade ?id .
+    ?productionPlaceCountry__id gvp:placeTypePreferred "nations" ;
+                          skos:prefLabel ?productionPlaceCountry__prefLabel .
+  } 
+  GROUP BY ?id ?productionPlaceCountry__id ?productionPlaceCountry__prefLabel
+  # ORDER BY  ?id 
+`
+
 export const eventsByDecadeQuery = `
   SELECT DISTINCT ?category 
   (COUNT(?production) AS ?productionCount) 
-- 
GitLab