From 5b22093c2998d22a706eab4e4ef6ae56fed9f68f Mon Sep 17 00:00:00 2001 From: Qstick Date: Mon, 16 Nov 2020 13:12:20 -0500 Subject: [PATCH] Basic Indexer Statistics Endpoint --- src/NzbDrone.Common/Prowlarr.Common.csproj | 1 + .../Applications/AppIndexerMapService.cs | 3 - .../Applications/ApplicationFactory.cs | 1 - .../Applications/Lidarr/LidarrField.cs | 6 -- .../Applications/Radarr/RadarrField.cs | 6 -- .../Applications/Readarr/ReadarrField.cs | 6 -- .../Applications/Sonarr/SonarrField.cs | 6 -- .../IndexerStats/IndexerStatistics.cs | 11 ++++ .../IndexerStatisticsRepository.cs | 55 +++++++++++++++++++ .../IndexerStats/IndexerStatisticsService.cs | 27 +++++++++ .../Cardigann/CardigannSettings.cs | 2 - .../IndexerDefinitionUpdateCommand.cs | 5 -- .../Definitions/Cardigann/ParseUtil.cs | 1 - src/NzbDrone.Core/Indexers/IndexerBase.cs | 1 - .../Indexers/IndexerCapabilities.cs | 2 - src/NzbDrone/Prowlarr.csproj | 1 + .../Indexers/IndexerStatsModule.cs | 27 +++++++++ .../Indexers/IndexerStatsResource.cs | 37 +++++++++++++ 18 files changed, 159 insertions(+), 39 deletions(-) create mode 100644 src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs create mode 100644 src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs create mode 100644 src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs create mode 100644 src/Prowlarr.Api.V1/Indexers/IndexerStatsModule.cs create mode 100644 src/Prowlarr.Api.V1/Indexers/IndexerStatsResource.cs diff --git a/src/NzbDrone.Common/Prowlarr.Common.csproj b/src/NzbDrone.Common/Prowlarr.Common.csproj index 01aa57003..b8d99f5ea 100644 --- a/src/NzbDrone.Common/Prowlarr.Common.csproj +++ b/src/NzbDrone.Common/Prowlarr.Common.csproj @@ -1,6 +1,7 @@  net462;netcoreapp3.1 + en diff --git a/src/NzbDrone.Core/Applications/AppIndexerMapService.cs b/src/NzbDrone.Core/Applications/AppIndexerMapService.cs index dc361931d..41c812fe1 100644 --- a/src/NzbDrone.Core/Applications/AppIndexerMapService.cs +++ b/src/NzbDrone.Core/Applications/AppIndexerMapService.cs @@ -1,7 +1,4 @@ using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; -using NzbDrone.Core.Indexers; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.ThingiProvider.Events; diff --git a/src/NzbDrone.Core/Applications/ApplicationFactory.cs b/src/NzbDrone.Core/Applications/ApplicationFactory.cs index 41b09e281..1c0d46077 100644 --- a/src/NzbDrone.Core/Applications/ApplicationFactory.cs +++ b/src/NzbDrone.Core/Applications/ApplicationFactory.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using NLog; using NzbDrone.Common.Composition; using NzbDrone.Core.Messaging.Events; diff --git a/src/NzbDrone.Core/Applications/Lidarr/LidarrField.cs b/src/NzbDrone.Core/Applications/Lidarr/LidarrField.cs index 12a2fdfeb..bfbf59dea 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/LidarrField.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/LidarrField.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace NzbDrone.Core.Applications.Lidarr { public class LidarrField diff --git a/src/NzbDrone.Core/Applications/Radarr/RadarrField.cs b/src/NzbDrone.Core/Applications/Radarr/RadarrField.cs index 93080dcdd..322b97116 100644 --- a/src/NzbDrone.Core/Applications/Radarr/RadarrField.cs +++ b/src/NzbDrone.Core/Applications/Radarr/RadarrField.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace NzbDrone.Core.Applications.Radarr { public class RadarrField diff --git a/src/NzbDrone.Core/Applications/Readarr/ReadarrField.cs b/src/NzbDrone.Core/Applications/Readarr/ReadarrField.cs index 5266342f3..c615b9938 100644 --- a/src/NzbDrone.Core/Applications/Readarr/ReadarrField.cs +++ b/src/NzbDrone.Core/Applications/Readarr/ReadarrField.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace NzbDrone.Core.Applications.Readarr { public class ReadarrField diff --git a/src/NzbDrone.Core/Applications/Sonarr/SonarrField.cs b/src/NzbDrone.Core/Applications/Sonarr/SonarrField.cs index cd9d1480a..d0350c0ce 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/SonarrField.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/SonarrField.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace NzbDrone.Core.Applications.Sonarr { public class SonarrField diff --git a/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs b/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs new file mode 100644 index 000000000..81f6ddbfe --- /dev/null +++ b/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs @@ -0,0 +1,11 @@ +using NzbDrone.Core.Datastore; + +namespace NzbDrone.Core.IndexerStats +{ + public class IndexerStatistics : ResultSet + { + public int IndexerId { get; set; } + public int AverageResponseTime { get; set; } + public int NumberOfQueries { get; set; } + } +} diff --git a/src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs new file mode 100644 index 000000000..1b9d0fa83 --- /dev/null +++ b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsRepository.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Dapper; +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Indexers; + +namespace NzbDrone.Core.IndexerStats +{ + public interface IIndexerStatisticsRepository + { + List IndexerStatistics(); + } + + public class IndexerStatisticsRepository : IIndexerStatisticsRepository + { + private const string _selectTemplate = "SELECT /**select**/ FROM History /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/"; + + private readonly IMainDatabase _database; + + public IndexerStatisticsRepository(IMainDatabase database) + { + _database = database; + } + + public List IndexerStatistics() + { + var time = DateTime.UtcNow; + return Query(Builder()); + } + + public List IndexerStatistics(int indexerId) + { + var time = DateTime.UtcNow; + return Query(Builder().Where(x => x.Id == indexerId)); + } + + private List Query(SqlBuilder builder) + { + var sql = builder.AddTemplate(_selectTemplate).LogQuery(); + + using (var conn = _database.OpenConnection()) + { + return conn.Query(sql.RawSql, sql.Parameters).ToList(); + } + } + + private SqlBuilder Builder() => new SqlBuilder() + .Select(@"Indexers.Id AS IndexerId, + COUNT(History.Id) AS NumberOfQueries, + AVG(json_extract(History.Data,'$.elapsedTime')) AS AverageResponseTime") + .Join((t, r) => t.IndexerId == r.Id) + .GroupBy(x => x.Id); + } +} diff --git a/src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs new file mode 100644 index 000000000..504d95d8e --- /dev/null +++ b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Linq; + +namespace NzbDrone.Core.IndexerStats +{ + public interface IIndexerStatisticsService + { + List IndexerStatistics(); + } + + public class IndexerStatisticsService : IIndexerStatisticsService + { + private readonly IIndexerStatisticsRepository _indexerStatisticsRepository; + + public IndexerStatisticsService(IIndexerStatisticsRepository indexerStatisticsRepository) + { + _indexerStatisticsRepository = indexerStatisticsRepository; + } + + public List IndexerStatistics() + { + var seasonStatistics = _indexerStatisticsRepository.IndexerStatistics(); + + return seasonStatistics.ToList(); + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs index 98250604c..9832fe29c 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; using FluentValidation; using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/IndexerDefinitionUpdateCommand.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/IndexerDefinitionUpdateCommand.cs index 912eb955c..0e547d9cc 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/IndexerDefinitionUpdateCommand.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/IndexerDefinitionUpdateCommand.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Indexers.Definitions.Cardigann diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/ParseUtil.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/ParseUtil.cs index 044c507c7..d97606d00 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/ParseUtil.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/ParseUtil.cs @@ -1,5 +1,4 @@ using System.Globalization; -using System.Linq; using System.Text.RegularExpressions; namespace NzbDrone.Core.Indexers.Cardigann diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index 63788a825..0059277ad 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -4,7 +4,6 @@ using System.Linq; using FluentValidation.Results; using NLog; using NzbDrone.Common.Extensions; -using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; diff --git a/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs b/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs index d9e2870b1..854cbb3e0 100644 --- a/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs +++ b/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using NzbDrone.Core.Indexers.Cardigann; -using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers { diff --git a/src/NzbDrone/Prowlarr.csproj b/src/NzbDrone/Prowlarr.csproj index 4badb0e75..3049a702a 100644 --- a/src/NzbDrone/Prowlarr.csproj +++ b/src/NzbDrone/Prowlarr.csproj @@ -2,6 +2,7 @@ WinExe net462;netcoreapp3.1 + en win-x64 true ..\NzbDrone.Host\Prowlarr.ico diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerStatsModule.cs b/src/Prowlarr.Api.V1/Indexers/IndexerStatsModule.cs new file mode 100644 index 000000000..72e5fbf2d --- /dev/null +++ b/src/Prowlarr.Api.V1/Indexers/IndexerStatsModule.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NzbDrone.Core.IndexerStats; +using Prowlarr.Http; + +namespace Prowlarr.Api.V1.Indexers +{ + public class IndexerStatsModule : ProwlarrRestModule + { + private readonly IIndexerStatisticsService _indexerStatisticsService; + + public IndexerStatsModule(IIndexerStatisticsService indexerStatisticsService) + { + _indexerStatisticsService = indexerStatisticsService; + + GetResourceAll = GetAll; + } + + private List GetAll() + { + return _indexerStatisticsService.IndexerStatistics().ToResource(); + } + } +} diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerStatsResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerStatsResource.cs new file mode 100644 index 000000000..d9f0c31cf --- /dev/null +++ b/src/Prowlarr.Api.V1/Indexers/IndexerStatsResource.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Core.IndexerStats; +using Prowlarr.Http.REST; + +namespace Prowlarr.Api.V1.Indexers +{ + public class IndexerStatsResource : RestResource + { + public int IndexerId { get; set; } + public int NumberOfQueries { get; set; } + public int AverageResponseTime { get; set; } + } + + public static class IndexerStatsResourceMapper + { + public static IndexerStatsResource ToResource(this IndexerStatistics model) + { + if (model == null) + { + return null; + } + + return new IndexerStatsResource + { + IndexerId = model.IndexerId, + NumberOfQueries = model.NumberOfQueries, + AverageResponseTime = model.AverageResponseTime + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } +}