diff --git a/frontend/src/Components/Chart/BarChart.js b/frontend/src/Components/Chart/BarChart.js index c95e5f2a5..ca225952b 100644 --- a/frontend/src/Components/Chart/BarChart.js +++ b/frontend/src/Components/Chart/BarChart.js @@ -1,4 +1,4 @@ -import Chart from 'chart.js'; +import Chart from 'chart.js/auto'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import colors from 'Styles/Variables/colors'; @@ -11,9 +11,19 @@ class BarChart extends Component { componentDidMount() { this.myChart = new Chart(this.canvasRef.current, { - type: this.props.horizontal ? 'horizontalBar' : 'bar', + type: 'bar', options: { - maintainAspectRatio: false + indexAxis: this.props.horizontal ? 'y' : 'x', + maintainAspectRatio: false, + plugins: { + title: { + display: true, + text: this.props.title + }, + legend: { + display: this.props.legend + } + } }, data: { labels: this.props.data.map((d) => d.label), @@ -42,12 +52,14 @@ class BarChart extends Component { BarChart.propTypes = { data: PropTypes.arrayOf(PropTypes.object).isRequired, horizontal: PropTypes.bool, + legend: PropTypes.bool, title: PropTypes.string.isRequired }; BarChart.defaultProps = { data: [], horizontal: false, + legend: false, title: '' }; diff --git a/frontend/src/Components/Chart/DoughnutChart.js b/frontend/src/Components/Chart/DoughnutChart.js index 722ee695a..5f38991c6 100644 --- a/frontend/src/Components/Chart/DoughnutChart.js +++ b/frontend/src/Components/Chart/DoughnutChart.js @@ -1,4 +1,4 @@ -import Chart from 'chart.js'; +import Chart from 'chart.js/auto'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import colors from 'Styles/Variables/colors'; diff --git a/frontend/src/Components/Chart/LineChart.js b/frontend/src/Components/Chart/LineChart.js index 3d7d1ebac..8e61ea5e3 100644 --- a/frontend/src/Components/Chart/LineChart.js +++ b/frontend/src/Components/Chart/LineChart.js @@ -1,4 +1,4 @@ -import Chart from 'chart.js'; +import Chart from 'chart.js/auto'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; diff --git a/frontend/src/Components/Chart/StackedBarChart.js b/frontend/src/Components/Chart/StackedBarChart.js new file mode 100644 index 000000000..dbfc88733 --- /dev/null +++ b/frontend/src/Components/Chart/StackedBarChart.js @@ -0,0 +1,67 @@ +import Chart from 'chart.js/auto'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import colors from 'Styles/Variables/colors'; + +class StackedBarChart extends Component { + constructor(props) { + super(props); + this.canvasRef = React.createRef(); + } + + componentDidMount() { + this.myChart = new Chart(this.canvasRef.current, { + type: 'bar', + options: { + maintainAspectRatio: false, + scales: { + x: { + stacked: true + }, + y: { + stacked: true + } + }, + plugins: { + title: { + display: true, + text: this.props.title + } + } + }, + data: { + labels: this.props.data.labels, + datasets: this.props.data.datasets.map((d, index) => { + return { + label: d.label, + data: d.data, + backgroundColor: colors.chartColors[index] + }; + }) + } + }); + } + + componentDidUpdate() { + this.myChart.data.labels = this.props.data.labels; + this.myChart.data.datasets = this.props.data.datasets; + this.myChart.update(); + } + + render() { + return ( + + ); + } +} + +StackedBarChart.propTypes = { + data: PropTypes.object.isRequired, + title: PropTypes.string.isRequired +}; + +StackedBarChart.defaultProps = { + title: '' +}; + +export default StackedBarChart; diff --git a/frontend/src/Indexer/Stats/Stats.js b/frontend/src/Indexer/Stats/Stats.js index 777078e82..c3984f60e 100644 --- a/frontend/src/Indexer/Stats/Stats.js +++ b/frontend/src/Indexer/Stats/Stats.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import BarChart from 'Components/Chart/BarChart'; -import DoughnutChart from 'Components/Chart/DoughnutChart'; +import StackedBarChart from 'Components/Chart/StackedBarChart'; import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import PageContent from 'Components/Page/PageContent'; import PageContentBody from 'Components/Page/PageContentBody'; @@ -21,12 +21,25 @@ function getAverageResponseTimeData(indexerStats) { } function getTotalRequestsData(indexerStats) { - const data = indexerStats.map((indexer) => { - return { - label: indexer.indexerName, - value: indexer.numberOfQueries - }; - }); + const data = { + labels: indexerStats.map((indexer) => indexer.indexerName), + datasets: [ + { + label: 'Search Queries', + data: indexerStats.map((indexer) => indexer.numberOfQueries) + }, + { + label: 'Rss Queries', + data: indexerStats.map((indexer) => indexer.numberOfRssQueries) + }, + { + label: 'Auth Queries', + data: indexerStats.map((indexer) => indexer.numberOfAuthQueries) + } + ] + }; + + console.log(data); return data; } @@ -112,7 +125,7 @@ function Stats(props) { />
- diff --git a/package.json b/package.json index 05ff9bbba..062b07b1b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@microsoft/signalr": "5.0.5", "@sentry/browser": "6.3.1", "@sentry/integrations": "6.3.1", - "chart.js": "2.9.4", + "chart.js": "3.2.0", "classnames": "2.3.1", "clipboard": "2.0.8", "connected-react-router": "6.9.1", diff --git a/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs b/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs index 481123e2d..a8b65af07 100644 --- a/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs +++ b/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs @@ -9,6 +9,8 @@ namespace NzbDrone.Core.IndexerStats public int AverageResponseTime { get; set; } public int NumberOfQueries { get; set; } public int NumberOfGrabs { get; set; } + public int NumberOfRssQueries { get; set; } + public int NumberOfAuthQueries { get; set; } } public class UserAgentStatistics : ResultSet @@ -16,6 +18,7 @@ namespace NzbDrone.Core.IndexerStats public string UserAgent { get; set; } public int NumberOfQueries { get; set; } public int NumberOfGrabs { get; set; } + public int NumberOfRssQueries { get; set; } } public class HostStatistics : ResultSet @@ -23,5 +26,6 @@ namespace NzbDrone.Core.IndexerStats public string Host { get; set; } public int NumberOfQueries { get; set; } public int NumberOfGrabs { get; set; } + public int NumberOfRssQueries { get; set; } } } diff --git a/src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs index 7bb2b73db..35aa6b658 100644 --- a/src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs +++ b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs @@ -60,6 +60,8 @@ namespace NzbDrone.Core.IndexerStats .Select(@"Indexers.Id AS IndexerId, Indexers.Name AS IndexerName, SUM(CASE WHEN EventType == 2 then 1 else 0 end) AS NumberOfQueries, + SUM(CASE WHEN EventType == 3 then 1 else 0 end) AS NumberOfRssQueries, + SUM(CASE WHEN EventType == 4 then 1 else 0 end) AS NumberOfAuthQueries, SUM(CASE WHEN EventType == 1 then 1 else 0 end) AS NumberOfGrabs, AVG(json_extract(History.Data,'$.elapsedTime')) AS AverageResponseTime") .Join((t, r) => t.IndexerId == r.Id) @@ -68,7 +70,8 @@ namespace NzbDrone.Core.IndexerStats private SqlBuilder UserAgentBuilder() => new SqlBuilder() .Select(@"json_extract(History.Data,'$.source') AS UserAgent, SUM(CASE WHEN EventType == 2 then 1 else 0 end) AS NumberOfQueries, - SUM(CASE WHEN EventType == 1 then 1 else 0 end) AS NumberOfGrabs") + SUM(CASE WHEN EventType == 1 then 1 else 0 end) AS NumberOfGrabs, + SUM(CASE WHEN EventType == 3 then 1 else 0 end) AS NumberOfRssQueries") .GroupBy("UserAgent"); } } diff --git a/yarn.lock b/yarn.lock index 8a4441d92..7f1bbac06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1998,28 +1998,10 @@ character-reference-invalid@^1.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== -chart.js@2.9.4: - version "2.9.4" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684" - integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A== - dependencies: - chartjs-color "^2.1.0" - moment "^2.10.2" - -chartjs-color-string@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71" - integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A== - dependencies: - color-name "^1.0.0" - -chartjs-color@^2.1.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0" - integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w== - dependencies: - chartjs-color-string "^0.6.0" - color-convert "^1.9.3" +chart.js@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.2.0.tgz#3d0a4b62b6bee428f8547db6aa68c792b130c260" + integrity sha512-Ml3R47TvOPW6gQ6T8mg/uPvyOASPpPVVF6xb7ZyHkek1c6kJIT5ScT559afXoDf6uwtpDR2BpCommkA5KT8ODg== chrome-trace-event@^1.0.2: version "1.0.3" @@ -2100,7 +2082,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.3.0, color-convert@^1.9.0, color-convert@^1.9.3: +color-convert@^1.3.0, color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -4391,7 +4373,7 @@ mobile-detect@1.4.5: resolved "https://registry.yarnpkg.com/mobile-detect/-/mobile-detect-1.4.5.tgz#da393c3c413ca1a9bcdd9ced653c38281c0fb6ad" integrity sha512-yc0LhH6tItlvfLBugVUEtgawwFU2sIe+cSdmRJJCTMZ5GEJyLxNyC/NIOAOGk67Fa8GNpOttO3Xz/1bHpXFD/g== -moment@2.29.1, moment@^2.10.2: +moment@2.29.1: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==