diff --git a/ras/api/urls.py b/ras/api/urls.py index 42cb452..c9f821f 100644 --- a/ras/api/urls.py +++ b/ras/api/urls.py @@ -2,7 +2,9 @@ from django.urls import path from .views import * urlpatterns = [ + path('books/years', getYears), + path('books/stats', getStats), path('books/genres', books_per_genre_per_month), path('books/genres/count', countGenres), - path('ratings', avg_ratings_per_month) + 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 89b18a2..4898d05 100644 --- a/ras/api/views.py +++ b/ras/api/views.py @@ -17,22 +17,23 @@ def getBooksData(): return df +def filterData(df, datayear = None): + df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d') + df['readed'] = df['readed'].dt.strftime('%m-%Y') + + # Filter data on year + if datayear and datayear is not None: + df = df.where(df['readed'].str.contains(datayear)) + + return df + + @api_view(['GET']) def books_per_genre_per_month(request): - - datayear = request.META.get('HTTP_YEAR') - - if datayear: + if request.META.get('HTTP_YEAR'): data = [] - - df = getBooksData() - - df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d') - df['readed'] = df['readed'].dt.strftime('%m-%Y') - - # Filter data on year - df = df.where(df['readed'].str.contains(datayear)) + df = filterData(getBooksData(), request.META.get('HTTP_YEAR')) # Filter array on genre and date booksPerMonth = df.groupby(['genre','readed'])['genre'].count().reset_index(name="count") @@ -49,49 +50,13 @@ def books_per_genre_per_month(request): else: return Response("No year header included") -@api_view(['GET']) -def avg_ratings_per_month(request): - datayear = request.META.get('HTTP_YEAR') - - if datayear: - data = [] - - # Get CSV file with book data - df = getBooksData() - - df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d') - df['readed'] = df['readed'].dt.strftime('%m-%Y') - - # Filter data on year - df = df.where(df['readed'].str.contains(datayear)) - - avgratingspermonth = df.groupby('readed')['rating'].mean().reset_index(name="rating") - - for index, row in avgratingspermonth.iterrows(): - - data.append({ - "date": row['readed'], - "rating": int(row['rating']) - }) - - return Response(data) - else: - return Response("No year header included") - @api_view(['GET']) def countGenres(request): - datayear = request.META.get('HTTP_YEAR') - - if datayear: + if request.META.get('HTTP_YEAR'): + data = [] - - # Get CSV file with book data - df = getBooksData() - - df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d') - df['readed'] = df['readed'].dt.strftime('%m-%Y') - - df = df.where(df['readed'].str.contains(datayear)) + df = filterData(getBooksData(), request.META.get('HTTP_YEAR')) + genres = df.groupby('genre')['genre'].count().reset_index(name="count") genres = genres.sort_values(by='count', ascending=False) @@ -106,3 +71,60 @@ def countGenres(request): else: return Response("No year header included") +@api_view(['GET']) +def books_per_country(request): + if request.META.get('HTTP_YEAR'): + data = [] + df = filterData(getBooksData(), request.META.get('HTTP_YEAR')) + + countries = df.groupby('country')['country'].count().reset_index(name="count") + countries = countries.sort_values(by='count', ascending=False) + + for index, row in countries.iterrows(): + + data.append({ + "country": row['country'], + "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'): + data = [] + df = filterData(getBooksData(), request.META.get('HTTP_YEAR')) + df = df.dropna() + + statsTotalBooks = df['name'].count() + statsTotalPages = df['pages'].astype(int).sum() + statsTotalWriters = df['author'].nunique() + statsTotalCountries = df['country'].nunique() + statsTotalGenres = df['genre'].nunique() + + + data.append({ + 'totalbooks': statsTotalBooks, + 'totalpages': statsTotalPages, + 'totalauthors': statsTotalWriters, + 'totalcountries': statsTotalCountries, + 'totalgenres': statsTotalGenres + }) + + + return Response(data[0]) + else: + return Response("No year header included") + +@api_view(['GET']) +def getYears(request): + df = filterData(getBooksData()) + + df['readed'] = pd.to_datetime(df['readed'], errors='coerce') + df['year']= df['readed'].dt.year + + years = df.groupby('year')['year'].count().reset_index(name="count") + + return Response(years['year']) \ No newline at end of file diff --git a/ras/frontend/src/App.js b/ras/frontend/src/App.js index 8b77db6..1a928d2 100644 --- a/ras/frontend/src/App.js +++ b/ras/frontend/src/App.js @@ -5,15 +5,95 @@ export default class App extends Component { super(props); this.state = { year: new Date().getFullYear(), + readingYears: [], + totalbooks: 0, + totalpages: 0, + totalauthors: 0, + totalcountries: 0, + totalgenres: 0 } this.yearsArray = []; } changeYear(event) { + this.setState({ year: event.target.value }) + + fetch('/api/books/stats', { + "method": "GET", + "headers": { + "year": event.target.value + } + }) + .then(response => response.json()) + .then(data => { + this.setState({ + totalbooks: data.totalbooks, + totalpages: data.totalpages, + totalauthors: data.totalauthors, + totalcountries: data.totalcountries, + totalgenres: data.totalgenres + }) + }) + } + + initHorBar(data){ + + var countries = []; + var counts = []; + + data.forEach((count) => { + if (!countries.includes(count.country)) { + countries.push(count.country) + } + + counts.push(count.count) + }) + + $("canvas#countryChart").remove(); + $("div.books-per-country").append(''); + + var ctx = document.getElementById("countryChart"); + new Chart(ctx, { + type: 'bar', + options: { + indexAxis: 'y', + plugins: { + legend: { + display: false + }, + }, + scales: { + x: { + ticks: { + beginAtZero: true, + color: "white", + }, + stacked: true, + }, + y: { + ticks: { + beginAtZero: true, + stepSize: 1, + color: "white", + }, + stacked: true + } + }, + }, + data: { + labels: countries, + datasets: [ + { + label: "Boeken", + data: counts, + backgroundColor: '#696ffc' + }] + } + }); } initDoughnut(data) { @@ -50,12 +130,11 @@ export default class App extends Component { options: { //cutoutPercentage: 40, responsive: true, - } }); } - initChart(data, ratings, year) { + initChart(data, year) { /* ---------------------------------- @@ -110,39 +189,6 @@ export default class App extends Component { }) } - /* - ---------------------------------- - Avarage ratings per month - ---------------------------------- - */ - - var avgRatings = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - for (var j = 0; j < 12; j++) { - - if (j < 9) { - var month = "0" + (j + 1) - } else { - month = (j + 1) - } - - for (var i = 0; i < ratings.length; i++) { - if (ratings[i].date == month + '-' + year) { - avgRatings[j] = ratings[i].rating; - } - } - } - - dataSet.push({ - label: 'Gemiddelde beoordeling', - data: avgRatings, - backgroundColor: '#ffa500', - borderColor: '#ffa500', - tension: 0.4, - type: 'line', - order: 1 - }) - /* ---------------------------------- Stacked bar chart @@ -196,6 +242,8 @@ export default class App extends Component { } componentDidUpdate() { + var $this = this; + fetch('/api/books/genres', { "method": "GET", "headers": { @@ -204,17 +252,7 @@ export default class App extends Component { }) .then(response => response.json()) .then(books => { - fetch('/api/ratings', { - "method": "GET", - "headers": { - "year": this.state.year - } - }) - .then(response => response.json()) - .then(ratings => { - this.initChart(books, ratings, this.state.year); - }) - + this.initChart(books, this.state.year); }) fetch('/api/books/genres/count', { @@ -227,10 +265,26 @@ export default class App extends Component { .then(data => { this.initDoughnut(data); }) + + fetch('/api/books/countries', { + "method": "GET", + "headers": { + "year": this.state.year + } + }) + .then(response => response.json()) + .then(data => { + this.initHorBar(data); + }) + + + } componentDidMount() { + var $this = this; + var currentyear = this.state.year ? this.state.year : new Date().getFullYear() fetch('/api/books/genres', { @@ -241,17 +295,7 @@ export default class App extends Component { }) .then(response => response.json()) .then(books => { - fetch('/api/ratings', { - "method": "GET", - "headers": { - "year": currentyear - } - }) - .then(response => response.json()) - .then(ratings => { - this.initChart(books, ratings, currentyear); - }) - + this.initChart(books, currentyear); }) fetch('/api/books/genres/count', { @@ -264,60 +308,106 @@ export default class App extends Component { .then(data => { this.initDoughnut(data); }) + + fetch('/api/books/countries', { + "method": "GET", + "headers": { + "year": this.state.year + } + }) + .then(response => response.json()) + .then(data => { + this.initHorBar(data); + }) + + fetch('/api/books/stats', { + "method": "GET", + "headers": { + "year": this.state.year + } + }) + .then(response => response.json()) + .then(data => { + $this.setState({ + totalbooks: data.totalbooks, + totalpages: data.totalpages, + totalauthors: data.totalauthors, + totalcountries: data.totalcountries, + totalgenres: data.totalgenres + }) + }) + + fetch('/api/books/years', { + "method": "GET", + }) + .then(response => response.json()) + .then(data => { + this.setState({ + readingYears: data + }) + }) } render() { return ( -
- -
-
-
-
-
- Jaar: - -
- -
- -
-
-
-
-
-
+
- Boeken - 17 - -20% + + + +
-
+ +
- Bladzijdes - 512 - +5% + + {this.state.totalbooks} + Boeken +
+
+
+
+ + {this.state.totalpages} + Bladzijdes +
+
+
+
+ + {this.state.totalauthors} + Schrijvers +
+
+
+
+ + {this.state.totalgenres} + Genres +
+
+
+
+ + {this.state.totalcountries} + Landen
@@ -334,6 +424,17 @@ export default class App extends Component {
+ +
+
+
+
+
+
+ +
+
+
) diff --git a/ras/frontend/static/js/main.js b/ras/frontend/static/js/main.js index 48f4a95..ff44f6e 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 };\n this.yearsArray = [];\n }\n\n changeYear(event) {\n this.setState({\n year: event.target.value\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 $("canvas#chartGenres").remove();\n $("div.genresPercent").append(\'\');\n var ctx = document.getElementById("chartGenres");\n var myChart = new Chart(ctx, {\n type: \'doughnut\',\n data: {\n labels: labels,\n datasets: [{\n label: \'# of Tomatoes\',\n data: counts,\n backgroundColor: [\'#696ffc\', \'#7596fa\', \'#92adfe\', \'#abc0ff\'],\n borderWidth: 3,\n borderColor: \'#1f2940\'\n }]\n },\n options: {\n //cutoutPercentage: 40,\n responsive: true\n }\n });\n }\n\n initChart(data, ratings, 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 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 Avarage ratings per month\r\n ----------------------------------\r\n */\n\n\n var avgRatings = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n\n for (var j = 0; j < 12; j++) {\n if (j < 9) {\n var month = "0" + (j + 1);\n } else {\n month = j + 1;\n }\n\n for (var i = 0; i < ratings.length; i++) {\n if (ratings[i].date == month + \'-\' + year) {\n avgRatings[j] = ratings[i].rating;\n }\n }\n }\n\n dataSet.push({\n label: \'Gemiddelde beoordeling\',\n data: avgRatings,\n backgroundColor: \'#ffa500\',\n borderColor: \'#ffa500\',\n tension: 0.4,\n type: \'line\',\n order: 1\n });\n /*\r\n ----------------------------------\r\n Stacked bar chart\r\n ----------------------------------\r\n */\n\n $("canvas#chart").remove();\n $("div.books-per-month").append(\'\');\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 responsive: true,\n showTooltips: true,\n legend: {\n display: true\n },\n interaction: {\n mode: \'index\'\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 plugins: {\n legend: {\n labels: {\n color: "white"\n }\n }\n }\n }\n });\n }\n\n componentDidUpdate() {\n fetch(\'/api/books/genres\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(books => {\n fetch(\'/api/ratings\', {\n "method": "GET",\n "headers": {\n "year": this.state.year\n }\n }).then(response => response.json()).then(ratings => {\n this.initChart(books, ratings, this.state.year);\n });\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 }\n\n componentDidMount() {\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 fetch(\'/api/ratings\', {\n "method": "GET",\n "headers": {\n "year": currentyear\n }\n }).then(response => response.json()).then(ratings => {\n this.initChart(books, ratings, currentyear);\n });\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 }\n\n render() {\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: "sidebar"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("ul", {\n className: "sidebar_menu"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", {\n className: "menu-item"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "menu-item-label"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "menu-item-label-name"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n className: "fa fa-chart-bar"\n }), " Dashboard"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "menu-item-label"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "menu-item-label-name"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {\n class: "fa fa-book-open"\n }), " Boeken"))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "content"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "filter"\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-4"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {\n style: {\n color: \'#ffffff\',\n display: \'inline-block\',\n width: \'auto\'\n }\n }, "Jaar: "), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", {\n style: {\n display: \'inline-block\',\n width: \'auto\'\n },\n defaultValue: this.state.year,\n onChange: event => this.changeYear(event)\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", {\n value: "2020"\n }, "2020"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", {\n value: "2021"\n }, "2021"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", {\n value: "2022"\n }, "2022"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-4"\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-4"\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-4"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Boeken"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "17"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "-20%"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-4"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "stat-block"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Bladzijdes"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "512"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "+5%")))))), /*#__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-8"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "books-per-month"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("canvas", {\n id: "chart"\n }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "col-md-4"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {\n className: "genresPercent"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("canvas", {\n id: "chartGenres"\n })))))));\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