New: Custom Filters for Stats

pull/1853/head
Qstick 9 months ago
parent c873b3ffac
commit b608e38454

@ -1,9 +1,11 @@
import { AppSectionItemState } from 'App/State/AppSectionState'; import { AppSectionItemState } from 'App/State/AppSectionState';
import { Filter } from 'App/State/AppState'; import { Filter, FilterBuilderProp } from 'App/State/AppState';
import Indexer from 'Indexer/Indexer';
import { IndexerStats } from 'typings/IndexerStats'; import { IndexerStats } from 'typings/IndexerStats';
export interface IndexerStatsAppState export interface IndexerStatsAppState
extends AppSectionItemState<IndexerStats> { extends AppSectionItemState<IndexerStats> {
filterBuilderProps: FilterBuilderProp<Indexer>[];
selectedFilterKey: string; selectedFilterKey: string;
filters: Filter[]; filters: Filter[];
} }

@ -1,20 +1,25 @@
.fullWidthChart { .fullWidthChart {
display: inline-block; display: inline-block;
padding: 15px 25px;
width: 100%; width: 100%;
height: 300px;
} }
.halfWidthChart { .halfWidthChart {
display: inline-block; display: inline-block;
padding: 15px 25px;
width: 50%; width: 50%;
}
.chartContainer {
margin: 5px;
padding: 15px 25px;
height: 300px; height: 300px;
border-radius: 10px;
background-color: var(--chartBackgroundColor);
} }
@media only screen and (max-width: $breakpointSmall) { @media only screen and (max-width: $breakpointSmall) {
.halfWidthChart { .halfWidthChart {
display: inline-block; display: inline-block;
margin: 5px;
padding: 15px 25px; padding: 15px 25px;
width: 100%; width: 100%;
height: 300px; height: 300px;

@ -1,6 +1,7 @@
// This file is automatically generated. // This file is automatically generated.
// Please do not change this file! // Please do not change this file!
interface CssExports { interface CssExports {
'chartContainer': string;
'fullWidthChart': string; 'fullWidthChart': string;
'halfWidthChart': string; 'halfWidthChart': string;
} }

@ -8,6 +8,7 @@ import BarChart from 'Components/Chart/BarChart';
import DoughnutChart from 'Components/Chart/DoughnutChart'; import DoughnutChart from 'Components/Chart/DoughnutChart';
import StackedBarChart from 'Components/Chart/StackedBarChart'; import StackedBarChart from 'Components/Chart/StackedBarChart';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody'; import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar'; import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
@ -17,6 +18,7 @@ import {
fetchIndexerStats, fetchIndexerStats,
setIndexerStatsFilter, setIndexerStatsFilter,
} from 'Store/Actions/indexerStatsActions'; } from 'Store/Actions/indexerStatsActions';
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
import { import {
IndexerStatsHost, IndexerStatsHost,
IndexerStatsIndexer, IndexerStatsIndexer,
@ -24,7 +26,7 @@ import {
} from 'typings/IndexerStats'; } from 'typings/IndexerStats';
import getErrorMessage from 'Utilities/Object/getErrorMessage'; import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import IndexerStatsFilterMenu from './IndexerStatsFilterMenu'; import IndexerStatsFilterModal from './IndexerStatsFilterModal';
import styles from './IndexerStats.css'; import styles from './IndexerStats.css';
function getAverageResponseTimeData(indexerStats: IndexerStatsIndexer[]) { function getAverageResponseTimeData(indexerStats: IndexerStatsIndexer[]) {
@ -165,15 +167,26 @@ function getHostQueryData(indexerStats: IndexerStatsHost[]) {
const indexerStatsSelector = () => { const indexerStatsSelector = () => {
return createSelector( return createSelector(
(state: AppState) => state.indexerStats, (state: AppState) => state.indexerStats,
(indexerStats: IndexerStatsAppState) => { createCustomFiltersSelector('indexerStats'),
return indexerStats; (indexerStats: IndexerStatsAppState, customFilters) => {
return {
...indexerStats,
customFilters,
};
} }
); );
}; };
function IndexerStats() { function IndexerStats() {
const { isFetching, isPopulated, item, error, filters, selectedFilterKey } = const {
useSelector(indexerStatsSelector()); isFetching,
isPopulated,
item,
error,
filters,
customFilters,
selectedFilterKey,
} = useSelector(indexerStatsSelector());
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
@ -193,10 +206,13 @@ function IndexerStats() {
<PageContent> <PageContent>
<PageToolbar> <PageToolbar>
<PageToolbarSection alignContent={align.RIGHT} collapseButtons={false}> <PageToolbarSection alignContent={align.RIGHT} collapseButtons={false}>
<IndexerStatsFilterMenu <FilterMenu
alignMenu={align.RIGHT}
selectedFilterKey={selectedFilterKey} selectedFilterKey={selectedFilterKey}
filters={filters} filters={filters}
customFilters={customFilters}
onFilterSelect={onFilterSelect} onFilterSelect={onFilterSelect}
filterModalConnectorComponent={IndexerStatsFilterModal}
isDisabled={false} isDisabled={false}
/> />
</PageToolbarSection> </PageToolbarSection>
@ -213,57 +229,73 @@ function IndexerStats() {
{isLoaded && ( {isLoaded && (
<div> <div>
<div className={styles.fullWidthChart}> <div className={styles.fullWidthChart}>
<BarChart <div className={styles.chartContainer}>
data={getAverageResponseTimeData(item.indexers)} <BarChart
title={translate('AverageResponseTimesMs')} data={getAverageResponseTimeData(item.indexers)}
/> title={translate('AverageResponseTimesMs')}
/>
</div>
</div> </div>
<div className={styles.fullWidthChart}> <div className={styles.fullWidthChart}>
<BarChart <div className={styles.chartContainer}>
data={getFailureRateData(item.indexers)} <BarChart
title={translate('IndexerFailureRate')} data={getFailureRateData(item.indexers)}
kind={kinds.WARNING} title={translate('IndexerFailureRate')}
/> kind={kinds.WARNING}
/>
</div>
</div> </div>
<div className={styles.halfWidthChart}> <div className={styles.halfWidthChart}>
<StackedBarChart <div className={styles.chartContainer}>
data={getTotalRequestsData(item.indexers)} <StackedBarChart
title={translate('TotalIndexerQueries')} data={getTotalRequestsData(item.indexers)}
/> title={translate('TotalIndexerQueries')}
/>
</div>
</div> </div>
<div className={styles.halfWidthChart}> <div className={styles.halfWidthChart}>
<BarChart <div className={styles.chartContainer}>
data={getNumberGrabsData(item.indexers)} <BarChart
title={translate('TotalIndexerSuccessfulGrabs')} data={getNumberGrabsData(item.indexers)}
/> title={translate('TotalIndexerSuccessfulGrabs')}
/>
</div>
</div> </div>
<div className={styles.halfWidthChart}> <div className={styles.halfWidthChart}>
<BarChart <div className={styles.chartContainer}>
data={getUserAgentQueryData(item.userAgents)} <BarChart
title={translate('TotalUserAgentQueries')} data={getUserAgentQueryData(item.userAgents)}
horizontal={true} title={translate('TotalUserAgentQueries')}
/> horizontal={true}
/>
</div>
</div> </div>
<div className={styles.halfWidthChart}> <div className={styles.halfWidthChart}>
<BarChart <div className={styles.chartContainer}>
data={getUserAgentGrabsData(item.userAgents)} <BarChart
title={translate('TotalUserAgentGrabs')} data={getUserAgentGrabsData(item.userAgents)}
horizontal={true} title={translate('TotalUserAgentGrabs')}
/> horizontal={true}
/>
</div>
</div> </div>
<div className={styles.halfWidthChart}> <div className={styles.halfWidthChart}>
<DoughnutChart <div className={styles.chartContainer}>
data={getHostQueryData(item.hosts)} <DoughnutChart
title={translate('TotalHostQueries')} data={getHostQueryData(item.hosts)}
horizontal={true} title={translate('TotalHostQueries')}
/> horizontal={true}
/>
</div>
</div> </div>
<div className={styles.halfWidthChart}> <div className={styles.halfWidthChart}>
<DoughnutChart <div className={styles.chartContainer}>
data={getHostGrabsData(item.hosts)} <DoughnutChart
title={translate('TotalHostGrabs')} data={getHostGrabsData(item.hosts)}
horizontal={true} title={translate('TotalHostGrabs')}
/> horizontal={true}
/>
</div>
</div> </div>
</div> </div>
)} )}

@ -1,27 +0,0 @@
import React from 'react';
import FilterMenu from 'Components/Menu/FilterMenu';
import { align } from 'Helpers/Props';
interface IndexerStatsFilterMenuProps {
selectedFilterKey: string | number;
filters: object[];
isDisabled: boolean;
onFilterSelect(filterName: string): unknown;
}
function IndexerStatsFilterMenu(props: IndexerStatsFilterMenuProps) {
const { selectedFilterKey, filters, isDisabled, onFilterSelect } = props;
return (
<FilterMenu
alignMenu={align.RIGHT}
isDisabled={isDisabled}
selectedFilterKey={selectedFilterKey}
filters={filters}
customFilters={[]}
onFilterSelect={onFilterSelect}
/>
);
}
export default IndexerStatsFilterMenu;

@ -0,0 +1,56 @@
import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import FilterModal from 'Components/Filter/FilterModal';
import { setIndexerStatsFilter } from 'Store/Actions/indexerStatsActions';
function createIndexerStatsSelector() {
return createSelector(
(state: AppState) => state.indexerStats.item,
(indexerStats) => {
return indexerStats;
}
);
}
function createFilterBuilderPropsSelector() {
return createSelector(
(state: AppState) => state.indexerStats.filterBuilderProps,
(filterBuilderProps) => {
return filterBuilderProps;
}
);
}
interface IndexerStatsFilterModalProps {
isOpen: boolean;
}
export default function IndexerStatsFilterModal(
props: IndexerStatsFilterModalProps
) {
const sectionItems = [useSelector(createIndexerStatsSelector())];
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'indexerStats';
const dispatch = useDispatch();
const dispatchSetFilter = useCallback(
(payload: unknown) => {
dispatch(setIndexerStatsFilter(payload));
},
[dispatch]
);
return (
<FilterModal
// TODO: Don't spread all the props
{...props}
sectionItems={sectionItems}
filterBuilderProps={filterBuilderProps}
customFilterType={customFilterType}
dispatchSetFilter={dispatchSetFilter}
/>
);
}

@ -3,6 +3,7 @@ import { batchActions } from 'redux-batched-actions';
import { filterBuilderTypes, filterBuilderValueTypes } from 'Helpers/Props'; import { filterBuilderTypes, filterBuilderValueTypes } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks'; import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest'; import createAjaxRequest from 'Utilities/createAjaxRequest';
import findSelectedFilters from 'Utilities/Filter/findSelectedFilters';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import { set, update } from './baseActions'; import { set, update } from './baseActions';
import createHandleActions from './Creators/createHandleActions'; import createHandleActions from './Creators/createHandleActions';
@ -55,19 +56,20 @@ export const defaultState = {
filterBuilderProps: [ filterBuilderProps: [
{ {
name: 'startDate', name: 'indexers',
label: 'Start Date', label: () => translate('Indexers'),
type: filterBuilderTypes.EXACT, type: filterBuilderTypes.CONTAINS,
valueType: filterBuilderValueTypes.DATE valueType: filterBuilderValueTypes.INDEXER
}, },
{ {
name: 'endDate', name: 'tags',
label: 'End Date', label: () => translate('Tags'),
type: filterBuilderTypes.EXACT, type: filterBuilderTypes.CONTAINS,
valueType: filterBuilderValueTypes.DATE valueType: filterBuilderValueTypes.TAG
} }
], ],
selectedFilterKey: 'all' selectedFilterKey: 'all',
customFilters: []
}; };
export const persistState = [ export const persistState = [
@ -81,6 +83,10 @@ export const persistState = [
export const FETCH_INDEXER_STATS = 'indexerStats/fetchIndexerStats'; export const FETCH_INDEXER_STATS = 'indexerStats/fetchIndexerStats';
export const SET_INDEXER_STATS_FILTER = 'indexerStats/setIndexerStatsFilter'; export const SET_INDEXER_STATS_FILTER = 'indexerStats/setIndexerStatsFilter';
function getCustomFilters(state, type) {
return state.customFilters.items.filter((customFilter) => customFilter.type === type);
}
// //
// Action Creators // Action Creators
@ -94,23 +100,35 @@ export const actionHandlers = handleThunks({
[FETCH_INDEXER_STATS]: function(getState, payload, dispatch) { [FETCH_INDEXER_STATS]: function(getState, payload, dispatch) {
const state = getState(); const state = getState();
const indexerStats = state.indexerStats; const indexerStats = state.indexerStats;
const customFilters = getCustomFilters(state, section);
const selectedFilters = findSelectedFilters(indexerStats.selectedFilterKey, indexerStats.filters, customFilters);
const requestParams = { const requestParams = {
endDate: moment().toISOString() endDate: moment().toISOString()
}; };
selectedFilters.forEach((selectedFilter) => {
if (selectedFilter.key === 'indexers') {
requestParams.indexers = selectedFilter.value.join(',');
}
if (selectedFilter.key === 'tags') {
requestParams.tags = selectedFilter.value.join(',');
}
});
if (indexerStats.selectedFilterKey !== 'all') { if (indexerStats.selectedFilterKey !== 'all') {
let dayCount = 7; if (indexerStats.selectedFilterKey === 'lastSeven') {
requestParams.startDate = moment().add(-7, 'days').endOf('day').toISOString();
}
if (indexerStats.selectedFilterKey === 'lastThirty') { if (indexerStats.selectedFilterKey === 'lastThirty') {
dayCount = 30; requestParams.startDate = moment().add(-30, 'days').endOf('day').toISOString();
} }
if (indexerStats.selectedFilterKey === 'lastNinety') { if (indexerStats.selectedFilterKey === 'lastNinety') {
dayCount = 90; requestParams.startDate = moment().add(-90, 'days').endOf('day').toISOString();
} }
requestParams.startDate = moment().add(-dayCount, 'days').endOf('day').toISOString();
} }
const basesAttrs = { const basesAttrs = {

@ -187,6 +187,7 @@ module.exports = {
// //
// Charts // Charts
chartBackgroundColor: '#262626',
failedColors: ['#ffbeb2', '#feb4a6', '#fdab9b', '#fca290', '#fb9984', '#fa8f79', '#f9856e', '#f77b66', '#f5715d', '#f36754', '#f05c4d', '#ec5049', '#e74545', '#e13b42', '#da323f', '#d3293d', '#ca223c', '#c11a3b', '#b8163a', '#ae123a'], failedColors: ['#ffbeb2', '#feb4a6', '#fdab9b', '#fca290', '#fb9984', '#fa8f79', '#f9856e', '#f77b66', '#f5715d', '#f36754', '#f05c4d', '#ec5049', '#e74545', '#e13b42', '#da323f', '#d3293d', '#ca223c', '#c11a3b', '#b8163a', '#ae123a'],
chartColorsDiversified: ['#90caf9', '#f4d166', '#ff8a65', '#ce93d8', '#80cba9', '#ffab91', '#8097ea', '#bcaaa4', '#a57583', '#e4e498', '#9e96af', '#c6ab81', '#6972c6', '#619fc6', '#81ad81', '#f48fb1', '#82afca', '#b5b071', '#8b959b', '#7ec0b4'], chartColorsDiversified: ['#90caf9', '#f4d166', '#ff8a65', '#ce93d8', '#80cba9', '#ffab91', '#8097ea', '#bcaaa4', '#a57583', '#e4e498', '#9e96af', '#c6ab81', '#6972c6', '#619fc6', '#81ad81', '#f48fb1', '#82afca', '#b5b071', '#8b959b', '#7ec0b4'],
chartColors: ['#f4d166', '#f6c760', '#f8bc58', '#f8b252', '#f7a84a', '#f69e41', '#f49538', '#f38b2f', '#f28026', '#f0751e', '#eb6c1c', '#e4641e', '#de5d1f', '#d75521', '#cf4f22', '#c64a22', '#bc4623', '#b24223', '#a83e24', '#9e3a26'] chartColors: ['#f4d166', '#f6c760', '#f8bc58', '#f8b252', '#f7a84a', '#f69e41', '#f49538', '#f38b2f', '#f28026', '#f0751e', '#eb6c1c', '#e4641e', '#de5d1f', '#d75521', '#cf4f22', '#c64a22', '#bc4623', '#b24223', '#a83e24', '#9e3a26']

@ -187,6 +187,7 @@ module.exports = {
// //
// Charts // Charts
chartBackgroundColor: '#fff',
failedColors: ['#ffbeb2', '#feb4a6', '#fdab9b', '#fca290', '#fb9984', '#fa8f79', '#f9856e', '#f77b66', '#f5715d', '#f36754', '#f05c4d', '#ec5049', '#e74545', '#e13b42', '#da323f', '#d3293d', '#ca223c', '#c11a3b', '#b8163a', '#ae123a'], failedColors: ['#ffbeb2', '#feb4a6', '#fdab9b', '#fca290', '#fb9984', '#fa8f79', '#f9856e', '#f77b66', '#f5715d', '#f36754', '#f05c4d', '#ec5049', '#e74545', '#e13b42', '#da323f', '#d3293d', '#ca223c', '#c11a3b', '#b8163a', '#ae123a'],
chartColorsDiversified: ['#90caf9', '#f4d166', '#ff8a65', '#ce93d8', '#80cba9', '#ffab91', '#8097ea', '#bcaaa4', '#a57583', '#e4e498', '#9e96af', '#c6ab81', '#6972c6', '#619fc6', '#81ad81', '#f48fb1', '#82afca', '#b5b071', '#8b959b', '#7ec0b4'], chartColorsDiversified: ['#90caf9', '#f4d166', '#ff8a65', '#ce93d8', '#80cba9', '#ffab91', '#8097ea', '#bcaaa4', '#a57583', '#e4e498', '#9e96af', '#c6ab81', '#6972c6', '#619fc6', '#81ad81', '#f48fb1', '#82afca', '#b5b071', '#8b959b', '#7ec0b4'],
chartColors: ['#f4d166', '#f6c760', '#f8bc58', '#f8b252', '#f7a84a', '#f69e41', '#f49538', '#f38b2f', '#f28026', '#f0751e', '#eb6c1c', '#e4641e', '#de5d1f', '#d75521', '#cf4f22', '#c64a22', '#bc4623', '#b24223', '#a83e24', '#9e3a26'] chartColors: ['#f4d166', '#f6c760', '#f8bc58', '#f8b252', '#f7a84a', '#f69e41', '#f49538', '#f38b2f', '#f28026', '#f0751e', '#eb6c1c', '#e4641e', '#de5d1f', '#d75521', '#cf4f22', '#c64a22', '#bc4623', '#b24223', '#a83e24', '#9e3a26']

@ -46,7 +46,7 @@ namespace NzbDrone.Core.Test.IndexerStatsTests
.Setup(o => o.Between(It.IsAny<DateTime>(), It.IsAny<DateTime>())) .Setup(o => o.Between(It.IsAny<DateTime>(), It.IsAny<DateTime>()))
.Returns<DateTime, DateTime>((s, f) => history); .Returns<DateTime, DateTime>((s, f) => history);
var statistics = Subject.IndexerStatistics(DateTime.UtcNow.AddMonths(-1), DateTime.UtcNow); var statistics = Subject.IndexerStatistics(DateTime.UtcNow.AddMonths(-1), DateTime.UtcNow, new List<int> { 5 });
statistics.IndexerStatistics.Count.Should().Be(1); statistics.IndexerStatistics.Count.Should().Be(1);
statistics.IndexerStatistics.First().AverageResponseTime.Should().Be(0); statistics.IndexerStatistics.First().AverageResponseTime.Should().Be(0);

@ -8,7 +8,7 @@ namespace NzbDrone.Core.IndexerStats
{ {
public interface IIndexerStatisticsService public interface IIndexerStatisticsService
{ {
CombinedStatistics IndexerStatistics(DateTime start, DateTime end); CombinedStatistics IndexerStatistics(DateTime start, DateTime end, List<int> indexerIds);
} }
public class IndexerStatisticsService : IIndexerStatisticsService public class IndexerStatisticsService : IIndexerStatisticsService
@ -22,13 +22,15 @@ namespace NzbDrone.Core.IndexerStats
_indexerFactory = indexerFactory; _indexerFactory = indexerFactory;
} }
public CombinedStatistics IndexerStatistics(DateTime start, DateTime end) public CombinedStatistics IndexerStatistics(DateTime start, DateTime end, List<int> indexerIds)
{ {
var history = _historyService.Between(start, end); var history = _historyService.Between(start, end);
var groupedByIndexer = history.GroupBy(h => h.IndexerId); var filteredHistory = history.Where(h => indexerIds.Contains(h.IndexerId));
var groupedByUserAgent = history.GroupBy(h => h.Data.GetValueOrDefault("source") ?? "");
var groupedByHost = history.GroupBy(h => h.Data.GetValueOrDefault("host") ?? ""); var groupedByIndexer = filteredHistory.GroupBy(h => h.IndexerId);
var groupedByUserAgent = filteredHistory.GroupBy(h => h.Data.GetValueOrDefault("source") ?? "");
var groupedByHost = filteredHistory.GroupBy(h => h.Data.GetValueOrDefault("host") ?? "");
var indexerStatsList = new List<IndexerStatistics>(); var indexerStatsList = new List<IndexerStatistics>();
var userAgentStatsList = new List<UserAgentStatistics>(); var userAgentStatsList = new List<UserAgentStatistics>();

@ -1,6 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerStats; using NzbDrone.Core.IndexerStats;
using NzbDrone.Core.Tags;
using Prowlarr.Http; using Prowlarr.Http;
namespace Prowlarr.Api.V1.Indexers namespace Prowlarr.Api.V1.Indexers
@ -9,20 +14,41 @@ namespace Prowlarr.Api.V1.Indexers
public class IndexerStatsController : Controller public class IndexerStatsController : Controller
{ {
private readonly IIndexerStatisticsService _indexerStatisticsService; private readonly IIndexerStatisticsService _indexerStatisticsService;
private readonly IIndexerFactory _indexerFactory;
private readonly ITagService _tagService;
public IndexerStatsController(IIndexerStatisticsService indexerStatisticsService) public IndexerStatsController(IIndexerStatisticsService indexerStatisticsService, IIndexerFactory indexerFactory, ITagService tagService)
{ {
_indexerStatisticsService = indexerStatisticsService; _indexerStatisticsService = indexerStatisticsService;
_indexerFactory = indexerFactory;
_tagService = tagService;
} }
[HttpGet] [HttpGet]
[Produces("application/json")] [Produces("application/json")]
public IndexerStatsResource GetAll(DateTime? startDate, DateTime? endDate) public IndexerStatsResource GetAll(DateTime? startDate, DateTime? endDate, string indexers, string tags)
{ {
var statsStartDate = startDate ?? DateTime.MinValue; var statsStartDate = startDate ?? DateTime.MinValue;
var statsEndDate = endDate ?? DateTime.Now; var statsEndDate = endDate ?? DateTime.Now;
var parsedIndexers = new List<int>();
var parsedTags = new List<int>();
var indexerIds = _indexerFactory.All().Select(i => i.Id).ToList();
var indexerStats = _indexerStatisticsService.IndexerStatistics(statsStartDate, statsEndDate); if (tags.IsNotNullOrWhiteSpace())
{
parsedTags.AddRange(tags.Split(',').Select(_tagService.GetTag).Select(t => t.Id));
indexerIds = indexerIds.Intersect(parsedTags.SelectMany(t => _indexerFactory.AllForTag(t).Select(i => i.Id))).ToList();
}
if (indexers.IsNotNullOrWhiteSpace())
{
parsedIndexers.AddRange(indexers.Split(',').Select(x => Convert.ToInt32(x)));
indexerIds = indexerIds.Intersect(parsedIndexers).ToList();
}
var indexerStats = _indexerStatisticsService.IndexerStatistics(statsStartDate, statsEndDate, indexerIds);
var indexerResource = new IndexerStatsResource var indexerResource = new IndexerStatsResource
{ {

Loading…
Cancel
Save