Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Taloth Saldono | a19776553e |
|
@ -230,6 +230,7 @@
|
|||
<Compile Include="Series\SeasonStatisticsResource.cs" />
|
||||
<Compile Include="System\Backup\BackupModule.cs" />
|
||||
<Compile Include="System\Backup\BackupResource.cs" />
|
||||
<Compile Include="System\Statistics\StatisticsModule.cs" />
|
||||
<Compile Include="System\Tasks\TaskModule.cs" />
|
||||
<Compile Include="System\Tasks\TaskResource.cs" />
|
||||
<Compile Include="System\SystemModule.cs" />
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using Nancy.Routing;
|
||||
using NLog;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Statistics;
|
||||
|
||||
namespace NzbDrone.Api.System
|
||||
{
|
||||
public class StatisticsModule : NzbDroneApiModule
|
||||
{
|
||||
private readonly IStatisticsService _statisticsService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public StatisticsModule(IStatisticsService statisticsService, Logger logger)
|
||||
: base("system/statistics")
|
||||
{
|
||||
_statisticsService = statisticsService;
|
||||
_logger = logger;
|
||||
|
||||
Get["/"] = x => GetGlobalStatistics();
|
||||
Get["/indexer"] = x => GetIndexerStatistics();
|
||||
}
|
||||
|
||||
private Response GetGlobalStatistics()
|
||||
{
|
||||
return new
|
||||
{
|
||||
Generated = DateTime.UtcNow,
|
||||
Uptime = GetUpTime(),
|
||||
History = _statisticsService.GetGlobalStatistics()
|
||||
}.AsResponse();
|
||||
}
|
||||
|
||||
private Response GetIndexerStatistics()
|
||||
{
|
||||
var stats = _statisticsService.GetIndexerStatistics();
|
||||
|
||||
return stats.AsResponse();
|
||||
}
|
||||
|
||||
private TimeSpan? GetUpTime()
|
||||
{
|
||||
try
|
||||
{
|
||||
return DateTime.Now - global::System.Diagnostics.Process.GetCurrentProcess().StartTime;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.DebugException("Failed to get uptime", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -954,6 +954,9 @@
|
|||
<Compile Include="SeriesStats\SeriesStatistics.cs" />
|
||||
<Compile Include="SeriesStats\SeriesStatisticsRepository.cs" />
|
||||
<Compile Include="SeriesStats\SeriesStatisticsService.cs" />
|
||||
<Compile Include="Statistics\HistoryStatistics.cs" />
|
||||
<Compile Include="Statistics\StatisticsGrouping.cs" />
|
||||
<Compile Include="Statistics\StatisticsService.cs" />
|
||||
<Compile Include="Tags\Tag.cs" />
|
||||
<Compile Include="Tags\TagRepository.cs" />
|
||||
<Compile Include="Tags\TagService.cs" />
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.Statistics
|
||||
{
|
||||
public class HistoryStatistics
|
||||
{
|
||||
public int Grabs { get; set; }
|
||||
public int Replaced { get; set; }
|
||||
public int Failed { get; set; }
|
||||
public int Imported { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.Statistics
|
||||
{
|
||||
public class StatisticsGrouping<T> where T : new()
|
||||
{
|
||||
public T LastWeek { get; set; }
|
||||
public T LastMonth { get; set; }
|
||||
public T AllTime { get; set; }
|
||||
|
||||
public StatisticsGrouping()
|
||||
{
|
||||
LastWeek = new T();
|
||||
LastMonth = new T();
|
||||
AllTime = new T();
|
||||
}
|
||||
|
||||
public void Apply(DateTime date, Action<T> applyAction)
|
||||
{
|
||||
var elapsed = DateTime.UtcNow - date.ToUniversalTime();
|
||||
|
||||
if (elapsed < TimeSpan.FromDays(7))
|
||||
{
|
||||
applyAction(LastWeek);
|
||||
}
|
||||
|
||||
if (elapsed < TimeSpan.FromDays(7 * 4))
|
||||
{
|
||||
applyAction(LastMonth);
|
||||
}
|
||||
|
||||
applyAction(AllTime);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Statistics
|
||||
{
|
||||
public interface IStatisticsService
|
||||
{
|
||||
StatisticsGrouping<HistoryStatistics> GetGlobalStatistics();
|
||||
Dictionary<string, StatisticsGrouping<HistoryStatistics>> GetIndexerStatistics();
|
||||
}
|
||||
|
||||
public class StatisticsService : IStatisticsService
|
||||
{
|
||||
private readonly IHistoryRepository _historyRepository;
|
||||
|
||||
public StatisticsService(IHistoryRepository historyRepository)
|
||||
{
|
||||
_historyRepository = historyRepository;
|
||||
}
|
||||
|
||||
public StatisticsGrouping<HistoryStatistics> GetGlobalStatistics()
|
||||
{
|
||||
var stats = new StatisticsGrouping<HistoryStatistics>();
|
||||
|
||||
foreach (var group in GetIndexerStatistics().Values)
|
||||
{
|
||||
stats.LastWeek.Grabs += group.LastWeek.Grabs;
|
||||
stats.LastWeek.Replaced += group.LastWeek.Replaced;
|
||||
stats.LastWeek.Failed += group.LastWeek.Failed;
|
||||
stats.LastWeek.Imported += group.LastWeek.Imported;
|
||||
|
||||
stats.LastMonth.Grabs += group.LastMonth.Grabs;
|
||||
stats.LastMonth.Replaced += group.LastMonth.Replaced;
|
||||
stats.LastMonth.Failed += group.LastMonth.Failed;
|
||||
stats.LastMonth.Imported += group.LastMonth.Imported;
|
||||
|
||||
stats.AllTime.Grabs += group.AllTime.Grabs;
|
||||
stats.AllTime.Replaced += group.AllTime.Replaced;
|
||||
stats.AllTime.Failed += group.AllTime.Failed;
|
||||
stats.AllTime.Imported += group.AllTime.Imported;
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
public Dictionary<string, StatisticsGrouping<HistoryStatistics>> GetIndexerStatistics()
|
||||
{
|
||||
var all = _historyRepository.All().ToArray();
|
||||
|
||||
var stats = new Dictionary<string, StatisticsGrouping<HistoryStatistics>>();
|
||||
stats[string.Empty] = new StatisticsGrouping<HistoryStatistics>();
|
||||
|
||||
var groupedByEpisode = all.GroupBy(v => v.EpisodeId).ToArray();
|
||||
|
||||
foreach (var episode in groupedByEpisode)
|
||||
{
|
||||
var sortedEvents = episode.OrderBy(v => v.DownloadId)
|
||||
.ThenBy(v => v.Date)
|
||||
.ThenBy(v => v.Id)
|
||||
.ToArray();
|
||||
|
||||
var lastEvent = HistoryEventType.Unknown;
|
||||
string grabIndexer = null;
|
||||
string importIndexer = null;
|
||||
|
||||
foreach (var historyEvent in sortedEvents)
|
||||
{
|
||||
switch (historyEvent.EventType)
|
||||
{
|
||||
// Episode got grabbed from a specific indexer. Attribute anything that happens to that indexer.
|
||||
case History.HistoryEventType.Grabbed:
|
||||
grabIndexer = historyEvent.Data.GetValueOrDefault("indexer") ?? string.Empty;
|
||||
Apply(stats, grabIndexer, historyEvent.Date, s => s.Grabs++);
|
||||
lastEvent = HistoryEventType.Grabbed;
|
||||
break;
|
||||
|
||||
// Episodes got imported, only attribute the import if we grabbed it from an indexer.
|
||||
// Try attribute the deletion/replacement to the previous indexer.
|
||||
case History.HistoryEventType.SeriesFolderImported:
|
||||
case History.HistoryEventType.DownloadFolderImported:
|
||||
if (lastEvent == HistoryEventType.Grabbed)
|
||||
{
|
||||
if (importIndexer != null)
|
||||
{
|
||||
Apply(stats, importIndexer, historyEvent.Date, s => s.Replaced++);
|
||||
}
|
||||
importIndexer = grabIndexer;
|
||||
grabIndexer = null;
|
||||
Apply(stats, importIndexer, historyEvent.Date, s => s.Imported++);
|
||||
lastEvent = HistoryEventType.DownloadFolderImported;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastEvent = HistoryEventType.Unknown;
|
||||
}
|
||||
break;
|
||||
|
||||
// Attribute the failure to the indexer if we haven't imported yet.
|
||||
case History.HistoryEventType.DownloadFailed:
|
||||
if (lastEvent == HistoryEventType.Grabbed)
|
||||
{
|
||||
Apply(stats, grabIndexer, historyEvent.Date, s => s.Failed++);
|
||||
grabIndexer = null;
|
||||
lastEvent = HistoryEventType.Unknown;
|
||||
}
|
||||
break;
|
||||
|
||||
case History.HistoryEventType.EpisodeFileDeleted:
|
||||
lastEvent = HistoryEventType.Unknown;
|
||||
break;
|
||||
|
||||
case History.HistoryEventType.Unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
private void Apply<T>(Dictionary<string, StatisticsGrouping<T>> stats, string key, DateTime date, Action<T> applyAction) where T : new()
|
||||
{
|
||||
StatisticsGrouping<T> group;
|
||||
|
||||
if (!stats.TryGetValue(key, out group))
|
||||
{
|
||||
stats[key] = group = new StatisticsGrouping<T>();
|
||||
}
|
||||
|
||||
group.Apply(date, applyAction);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue