From ecbfe6f09a5f072ad6ea16e0908da967fd544490 Mon Sep 17 00:00:00 2001 From: Jordy van Zeeland Date: Tue, 11 Oct 2022 15:33:16 +0200 Subject: [PATCH] Styling changes + extra API data --- ras/api/urls.py | 5 ++ ras/api/views.py | 78 +++++++++++++++++++++ ras/frontend/src/App.js | 41 ++++------- ras/frontend/static/js/main.js | 2 +- ras/frontend/templates/index.html | 109 +++++++++++++----------------- 5 files changed, 144 insertions(+), 91 deletions(-) diff --git a/ras/api/urls.py b/ras/api/urls.py index c9f821f..329e372 100644 --- a/ras/api/urls.py +++ b/ras/api/urls.py @@ -4,7 +4,12 @@ from .views import * urlpatterns = [ path('books/years', getYears), path('books/stats', getStats), + + path('books/pages/stats', getStatsPages), + path('books/pages', pages_per_month), + path('books/genres', books_per_genre_per_month), path('books/genres/count', countGenres), + path('books/authors', books_per_author), path('books/countries', books_per_country), ] \ No newline at end of file diff --git a/ras/api/views.py b/ras/api/views.py index 7a920a4..ab369c2 100644 --- a/ras/api/views.py +++ b/ras/api/views.py @@ -92,6 +92,26 @@ def books_per_country(request): else: return Response("No year header included") +@api_view(['GET']) +def books_per_author(request): + if request.META.get('HTTP_YEAR'): + data = [] + df = filterData(getBooksData(), request.META.get('HTTP_YEAR')) + + countries = df.groupby(['author'])['author'].count().reset_index(name="count") + countries = countries.sort_values(by='count', ascending=False) + + for index, row in countries.iterrows(): + + data.append({ + "author": row['author'], + "count": int(row['count']) + }) + + return Response(data) + else: + return Response("No year header included") + @api_view(['GET']) def getStats(request): if request.META.get('HTTP_YEAR'): @@ -119,6 +139,64 @@ def getStats(request): else: return Response("No year header included") +@api_view(['GET']) +def getStatsPages(request): + data = [] + df = filterData(getBooksData(), request.META.get('HTTP_YEAR')) + df = df.dropna() + + df['pages'] = df['pages'].astype(int) + + pages = df.groupby(['pages', 'name', 'author', 'rating'])['pages'].count().reset_index(name="count") + pages = pages.sort_values(by='pages', ascending=True) + + shortestbook = pages.iloc[0] + longestbook = pages.iloc[-1] + avgPages = df["pages"].mean().astype(int) + + shortestbook = { + "name": shortestbook["name"], + "author": shortestbook['author'], + "pages": shortestbook['pages'], + "rating": shortestbook['rating'].astype(int) + } + + longestbook = { + "name": longestbook["name"], + "author": longestbook['author'], + "pages": longestbook['pages'], + "rating": longestbook['rating'].astype(int) + } + + data = { + "longestbook": longestbook, + "shortestbook": shortestbook, + "avgbook": avgPages + } + + return Response(data) + +@api_view(['GET']) +def pages_per_month(request): + if request.META.get('HTTP_YEAR'): + + data = [] + df = filterData(getBooksData(), request.META.get('HTTP_YEAR')) + + # Filter array on genre and date + booksPerMonth = df.groupby(['pages', 'readed'])['pages'].count().reset_index(name="count") + booksPerMonth = booksPerMonth.sort_values(by=['readed', 'count'], ascending=True) + + for index, row in booksPerMonth.iterrows(): + data.append({ + "pages": row['pages'], + "readed": row['readed'] + }) + + return Response(data) + else: + return Response("No year header included") + @api_view(['GET']) def getYears(request): df = filterData(getBooksData()) diff --git a/ras/frontend/src/App.js b/ras/frontend/src/App.js index 95f1f24..11a2622 100644 --- a/ras/frontend/src/App.js +++ b/ras/frontend/src/App.js @@ -199,7 +199,7 @@ export default class App extends Component { label: '# of Tomatoes', data: counts, backgroundColor: [ - '#7b71ff', '#f9939b', '#3fe7bf', '#9bcafe', '#7c59ff' + '#6f52ec', '#ff4c62', '#33d69f', '#fdb700', '#4cb7ff' ], borderWidth: 0, borderColor: '#1f2940', @@ -235,7 +235,7 @@ export default class App extends Component { padding: 20, usePointStyle: true, // This more specific font property overrides the global property - color: "white", + color: "##101010", font: { size: 14, family: 'Source Sans Pro' @@ -270,7 +270,7 @@ export default class App extends Component { var colors = [ // '#696ffc', '#7596fa', '#92adfe', '#abc0ff' - '#7b71ff', '#f9939b', '#3fe7bf', '#9bcafe', '#7c59ff' + '#6f52ec', '#ff4c62', '#33d69f', '#fdb700', '#4cb7ff' ] var dataSet = []; @@ -358,7 +358,7 @@ export default class App extends Component { x: { ticks: { beginAtZero: true, - color: "white", + color: "#101010", fontFamily: "Source Sans Pro", }, stacked: true, @@ -367,7 +367,7 @@ export default class App extends Component { ticks: { beginAtZero: true, stepSize: 1, - color: "white", + color: "#101010", fontFamily: "Source Sans Pro", }, stacked: true @@ -378,7 +378,7 @@ export default class App extends Component { position: 'top', labels: { usePointStyle: true, - color: "white", + color: "#101010", padding: 20, font: { size: 14, @@ -462,6 +462,7 @@ export default class App extends Component { console.log(this.state); return ( +
@@ -527,21 +528,12 @@ export default class App extends Component {
-
+
Boeken per maand per genre
- -
-
-
-
-
Genres
-
-
- {/*
*/} -
+
Landen @@ -569,20 +561,13 @@ export default class App extends Component {
+
Genres
-
-
- Schrijvers -
-
- -
-
- Favorieten (5 sterren) -
-
+
+ +
) diff --git a/ras/frontend/static/js/main.js b/ras/frontend/static/js/main.js index c473aca..ae96659 100644 --- a/ras/frontend/static/js/main.js +++ b/ras/frontend/static/js/main.js @@ -1,2 +1,2 @@ /*! For license information please see main.js.LICENSE.txt */ -(()=>{"use strict";var __webpack_modules__={"./src/App.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval('__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "default": () => (/* binding */ App)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "./node_modules/react/index.js");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n\nclass App extends react__WEBPACK_IMPORTED_MODULE_0__.Component {\n constructor(props) {\n super(props);\n this.state = {\n year: new Date().getFullYear(),\n readingYears: [],\n totalbooks: 0,\n totalpages: 0,\n totalauthors: 0,\n totalcountries: 0,\n totalgenres: 0,\n countries: []\n };\n this.yearsArray = [];\n }\n\n getGenres() {\n fetch(\'/api/books/genres\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(books => {\n this.initChart(books, this.state.year);\n });\n }\n\n getCountries(init) {\n fetch(\'/api/books/countries\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(data => {\n this.setState({\n countries: data\n });\n\n if (init == true) {\n $(\'#DataTable\').DataTable({\n paging: false,\n ordering: false,\n info: false,\n searching: false\n });\n }\n });\n }\n\n changeYear(event) {\n this.setState({\n year: event.target.value\n });\n fetch(\'/api/books/stats\', {\n "method": "GET",\n "headers": {\n "year": event.target.value\n }\n }).then(response => response.json()).then(data => {\n this.setState({\n totalbooks: data.totalbooks,\n totalpages: data.totalpages,\n totalauthors: data.totalauthors,\n totalcountries: data.totalcountries,\n totalgenres: data.totalgenres\n });\n });\n fetch(\'/api/books/countries\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(data => {\n this.setState({\n countries: data\n });\n this.getCountries(false);\n });\n var $this = this;\n fetch(\'/api/books/genres/count\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(data => {\n this.initDoughnut(data);\n });\n }\n\n initHorBar(data) {\n var countries = [];\n var counts = [];\n data.forEach(count => {\n if (!countries.includes(count.country)) {\n countries.push(count.country);\n }\n\n counts.push(count.count);\n });\n $("canvas#countryChart").remove();\n $("div.books-per-country").append(\'\');\n var ctx = document.getElementById("countryChart");\n new Chart(ctx, {\n type: \'bar\',\n options: {\n indexAxis: \'y\',\n plugins: {\n legend: {\n display: false\n }\n },\n scales: {\n x: {\n ticks: {\n beginAtZero: true,\n color: "white"\n },\n stacked: true\n },\n y: {\n ticks: {\n beginAtZero: true,\n stepSize: 1,\n color: "white"\n },\n stacked: true\n }\n }\n },\n data: {\n labels: countries,\n datasets: [{\n label: "Boeken",\n data: counts,\n backgroundColor: \'#696ffc\'\n }]\n }\n });\n }\n\n initDoughnut(data) {\n var labels = [];\n var counts = [];\n data.forEach(count => {\n if (!labels.includes(count.genre)) {\n labels.push(count.genre);\n }\n\n counts.push(count.count);\n });\n const legendMargin = {\n id: \'legendMargin\',\n\n beforeInit(chart, legend, options) {\n const fitValue = chart.legend.fit;\n\n chart.legend.fit = function fit() {\n fitValue.bind(chart.legend)();\n return this.height += 30;\n };\n }\n\n };\n $("canvas#chartGenres").remove();\n $("div.genresPercent").append(\'\');\n var ctx = document.getElementById("chartGenres");\n var myChart = new Chart(ctx, {\n type: \'pie\',\n data: {\n labels: labels,\n datasets: [{\n label: \'# of Tomatoes\',\n data: counts,\n backgroundColor: [\'#7b71ff\', \'#f9939b\', \'#3fe7bf\', \'#9bcafe\', \'#7c59ff\'],\n borderWidth: 0,\n borderColor: \'#1f2940\',\n tooltip: {\n callbacks: {\n label: function (context) {\n let label = context.label;\n let value = context.formattedValue;\n if (!label) label = \'Unknown\';\n let sum = 0;\n let dataArr = context.chart.data.datasets[0].data;\n dataArr.map(data => {\n sum += Number(data);\n });\n let percentage = (value * 100 / sum).toFixed(1) + \'%\';\n return label + ": " + percentage;\n }\n }\n }\n }]\n },\n options: {\n cutout: \'50%\',\n responsive: true,\n plugins: {\n legend: {\n position: \'top\',\n labels: {\n padding: 20,\n usePointStyle: true,\n // This more specific font property overrides the global property\n color: "white",\n font: {\n size: 14,\n family: \'Source Sans Pro\'\n }\n }\n }\n }\n },\n plugins: [{\n id: \'legendMargin\',\n\n beforeInit(chart, legend, options) {\n const fitValue = chart.legend.fit;\n\n chart.legend.fit = function fit() {\n fitValue.bind(chart.legend)();\n return this.height += 30;\n };\n }\n\n }]\n });\n }\n\n initChart(data, year) {\n /*\r\n ----------------------------------\r\n Books per month per genre\r\n ----------------------------------\r\n */\n var genres = [];\n var colors = [// \'#696ffc\', \'#7596fa\', \'#92adfe\', \'#abc0ff\'\n \'#7b71ff\', \'#f9939b\', \'#3fe7bf\', \'#9bcafe\', \'#7c59ff\'];\n var dataSet = [];\n data.forEach(book => {\n if (!genres.includes(book.genre)) {\n genres.push(book.genre);\n }\n });\n\n if (genres && genres.length > 0) {\n genres.forEach((genre, index) => {\n var genreData = [];\n\n for (var i = 0; i < 12; i++) {\n genreData[i] = 0;\n\n if (i + 1 < 10) {\n var month = "0" + (i + 1);\n } else {\n month = i + 1;\n }\n\n for (var j = 0; j < data.length; j++) {\n if (data && data[j] && data[j].readed == month + \'-\' + year) {\n if (data[j].genre == genre) {\n genreData[i] = data[j].count;\n }\n }\n }\n }\n\n dataSet.push({\n label: genre,\n data: genreData,\n backgroundColor: colors[index],\n order: 2\n });\n });\n }\n /*\r\n ----------------------------------\r\n Stacked bar chart\r\n ----------------------------------\r\n */\n\n\n $("canvas#chart").remove();\n $("div.books-per-month").append(\'\');\n const legendMargin = {\n id: \'legendMargin\',\n\n beforeInit(chart, legend, options) {\n const fitValue = chart.legend.fit;\n\n chart.legend.fit = function fit() {\n fitValue.bind(chart.legend)();\n return this.height += 30;\n };\n }\n\n };\n new Chart(document.getElementById(\'chart\'), {\n type: \'bar\',\n data: {\n labels: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],\n datasets: dataSet\n },\n options: {\n maintainAspectRatio: false,\n responsive: true,\n showTooltips: true,\n legend: {\n display: true,\n labels: {\n usePointStyle: true\n }\n },\n interaction: {\n mode: \'index\'\n },\n scales: {\n x: {\n ticks: {\n beginAtZero: true,\n color: "white",\n fontFamily: "Source Sans Pro"\n },\n stacked: true\n },\n y: {\n ticks: {\n beginAtZero: true,\n stepSize: 1,\n color: "white",\n fontFamily: "Source Sans Pro"\n },\n stacked: true\n }\n },\n plugins: {\n legend: {\n position: \'top\',\n labels: {\n usePointStyle: true,\n color: "white",\n padding: 20,\n font: {\n size: 14,\n family: \'Source Sans Pro\'\n }\n }\n }\n },\n tooltips: {\n bodyFont: \'Source Sans Pro\'\n }\n },\n plugins: [legendMargin]\n });\n }\n\n componentDidUpdate() {\n this.getGenres();\n }\n\n componentDidMount() {\n var $this = this;\n var currentyear = this.state.year ? this.state.year : new Date().getFullYear();\n fetch(\'/api/books/genres\', {\n "method": "GET",\n "headers": {\n "year": currentyear\n }\n }).then(response => response.json()).then(books => {\n this.initChart(books, currentyear);\n });\n fetch(\'/api/books/genres/count\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(data => {\n this.initDoughnut(data);\n });\n this.getCountries(true);\n fetch(\'/api/books/stats\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(data => {\n $this.setState({\n totalbooks: data.totalbooks,\n totalpages: data.totalpages,\n totalauthors: data.totalauthors,\n totalcountries: data.totalcountries,\n totalgenres: data.totalgenres\n });\n });\n fetch(\'/api/books/years\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n this.setState({\n readingYears: data\n });\n });\n }\n\n render() {\n console.log(this.state);\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "content"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "books-stats"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "container-fluid"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "row"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-2"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n class: "fa fa-calendar"\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-number"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", {\n className: "yearselector",\n defaultValue: this.state.year,\n onChange: event => this.changeYear(event)\n }, this.state.readingYears.map(year => {\n if (year === this.state.year) {\n var selected = \'selected\';\n } else {\n selected = \'\';\n }\n\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", {\n selected: selected,\n value: year\n }, year);\n }))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-2"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n class: "fa fa-book"\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-number"\n }, this.state.totalbooks), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-label"\n }, "Boeken"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-2"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n class: "fa fa-book-open"\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-number"\n }, this.state.totalpages), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-label"\n }, "Bladzijdes"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-2"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n class: "fa fa-pen"\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-number"\n }, this.state.totalauthors), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-label"\n }, "Schrijvers"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-2"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n class: "fa fa-book"\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-number"\n }, this.state.totalgenres), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-label"\n }, "Genres"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-2"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n class: "fa fa-globe"\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-number"\n }, this.state.totalcountries), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "stats-label"\n }, "Landen")))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "container-fluid"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "row"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-12"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "books-per-month"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "block_name"\n }, "Boeken per maand per genre"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("canvas", {\n id: "chart"\n }))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "container-fluid"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "row"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-3"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "genresPercent"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "block_name"\n }, "Genres"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("canvas", {\n id: "chartGenres"\n }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-3"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "books-per-country"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "block_name"\n }, "Landen"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("table", {\n id: "DataTable",\n class: "showHead table responsive nowrap",\n width: "100%"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("thead", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("th", null, "#"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("th", null, "Land"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("th", null, "Boeken"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tbody", null, this.state.countries.map((country, i) => {\n var code = country.code.toLowerCase();\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", null, i + 1), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {\n src: `https://flagcdn.com/32x24/${code}.png`\n }), " ", country.country), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", null, country.count)));\n }))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-3"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "books-per-country"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "block_name"\n }, "Schrijvers"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-3"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "books-per-country"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n className: "block_name"\n }, "Favorieten (5 sterren)")))))));\n }\n\n}\n\n//# sourceURL=webpack://frontend/./src/App.js?')},"./src/index.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval('__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "./node_modules/react/index.js");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-dom */ "./node_modules/react-dom/index.js");\n/* harmony import */ var _App__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./App */ "./src/App.js");\n\n\n\nreact_dom__WEBPACK_IMPORTED_MODULE_1__.render( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_App__WEBPACK_IMPORTED_MODULE_2__["default"], null), document.getElementById(\'app\'));\n\n//# sourceURL=webpack://frontend/./src/index.js?')},"./node_modules/react-dom/cjs/react-dom.development.js":(__unused_webpack_module,exports,__webpack_require__)=>{eval("/**\n * @license React\n * react-dom.development.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\n\nif (true) {\n (function() {\n\n 'use strict';\n\n/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */\nif (\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===\n 'function'\n) {\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());\n}\n var React = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\nvar Scheduler = __webpack_require__(/*! scheduler */ \"./node_modules/scheduler/index.js\");\n\nvar ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n\nvar suppressWarning = false;\nfunction setSuppressWarning(newSuppressWarning) {\n {\n suppressWarning = newSuppressWarning;\n }\n} // In DEV, calls to console.warn and console.error get replaced\n// by calls to these methods by a Babel plugin.\n//\n// In PROD (or in packages without access to React internals),\n// they are left as they are instead.\n\nfunction warn(format) {\n {\n if (!suppressWarning) {\n for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n printWarning('warn', format, args);\n }\n }\n}\nfunction error(format) {\n {\n if (!suppressWarning) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n\n printWarning('error', format, args);\n }\n }\n}\n\nfunction printWarning(level, format, args) {\n // When changing this logic, you might want to also\n // update consoleWithStackDev.www.js as well.\n {\n var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;\n var stack = ReactDebugCurrentFrame.getStackAddendum();\n\n if (stack !== '') {\n format += '%s';\n args = args.concat([stack]);\n } // eslint-disable-next-line react-internal/safe-string-coercion\n\n\n var argsWithFormat = args.map(function (item) {\n return String(item);\n }); // Careful: RN currently depends on this prefix\n\n argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it\n // breaks IE9: https://github.com/facebook/react/issues/13610\n // eslint-disable-next-line react-internal/no-production-logging\n\n Function.prototype.apply.call(console[level], console, argsWithFormat);\n }\n}\n\nvar FunctionComponent = 0;\nvar ClassComponent = 1;\nvar IndeterminateComponent = 2; // Before we know whether it is function or class\n\nvar HostRoot = 3; // Root of a host tree. Could be nested inside another node.\n\nvar HostPortal = 4; // A subtree. Could be an entry point to a different renderer.\n\nvar HostComponent = 5;\nvar HostText = 6;\nvar Fragment = 7;\nvar Mode = 8;\nvar ContextConsumer = 9;\nvar ContextProvider = 10;\nvar ForwardRef = 11;\nvar Profiler = 12;\nvar SuspenseComponent = 13;\nvar MemoComponent = 14;\nvar SimpleMemoComponent = 15;\nvar LazyComponent = 16;\nvar IncompleteClassComponent = 17;\nvar DehydratedFragment = 18;\nvar SuspenseListComponent = 19;\nvar ScopeComponent = 21;\nvar OffscreenComponent = 22;\nvar LegacyHiddenComponent = 23;\nvar CacheComponent = 24;\nvar TracingMarkerComponent = 25;\n\n// -----------------------------------------------------------------------------\n\nvar enableClientRenderFallbackOnTextMismatch = true; // TODO: Need to review this code one more time before landing\n// the react-reconciler package.\n\nvar enableNewReconciler = false; // Support legacy Primer support on internal FB www\n\nvar enableLazyContextPropagation = false; // FB-only usage. The new API has different semantics.\n\nvar enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber\n\nvar enableSuspenseAvoidThisFallback = false; // Enables unstable_avoidThisFallback feature in Fizz\n// React DOM Chopping Block\n//\n// Similar to main Chopping Block but only flags related to React DOM. These are\n// grouped because we will likely batch all of them into a single major release.\n// -----------------------------------------------------------------------------\n// Disable support for comment nodes as React DOM containers. Already disabled\n// in open source, but www codebase still relies on it. Need to remove.\n\nvar disableCommentsAsDOMContainers = true; // Disable javascript: URL strings in href for XSS protection.\n// and client rendering, mostly to allow JSX attributes to apply to the custom\n// element's object properties instead of only HTML attributes.\n// https://github.com/facebook/react/issues/11347\n\nvar enableCustomElementPropertySupport = false; // Disables children for