SearchResult Controller added.

Force Download added.
This commit is contained in:
Mark McDowall 2012-04-22 23:31:11 -07:00
parent aa24e4cac7
commit cef7b6a8dc
25 changed files with 358 additions and 128 deletions

View File

@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.JobTests
Mocker.GetMock<SearchProvider>()
.Setup(c => c.PartialSeasonSearch(notification, 1, 1))
.Returns(resultItems.ToList());
.Returns(episodes.Select(e => e.EpisodeNumber).ToList());
//Act
Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.JobTests
Mocker.GetMock<SearchProvider>()
.Setup(c => c.PartialSeasonSearch(notification, 1, 1))
.Returns(new List<SearchResultItem>{ new SearchResultItem{ Success = true }});
.Returns(new List<int>());
//Act
Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.JobTests
Mocker.GetMock<SearchProvider>()
.Setup(c => c.PartialSeasonSearch(notification, 1, 1))
.Returns(new List<SearchResultItem> { new SearchResultItem { Success = false, SearchError = ReportRejectionType.Size} });
.Returns(new List<int>());
//Act

View File

@ -11,6 +11,7 @@ using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Repository.Search;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
@ -105,7 +106,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.Returns(ReportRejectionType.None);
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -138,7 +139,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(ReportRejectionType.None);
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, new SearchResult(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -163,7 +164,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithQualityNotNeeded();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -187,7 +188,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithNullSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -209,7 +210,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithMisMatchedSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -231,7 +232,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithMatchingSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -253,7 +254,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithMatchingSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -280,7 +281,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithSuccessfulDownload();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1);
//Assert
result.Should().HaveCount(parseResults.Count);
@ -314,7 +315,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.Returns(true);
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1);
//Assert
result.Should().HaveCount(parseResults.Count);

View File

@ -27,7 +27,12 @@ namespace NzbDrone.Core.Datastore.Migrations
new Column("NzbUrl", DbType.String, ColumnProperty.NotNull),
new Column("NzbInfoUrl", DbType.String, ColumnProperty.Null),
new Column("Success", DbType.Boolean, ColumnProperty.NotNull),
new Column("SearchError", DbType.Int32, ColumnProperty.NotNull)
new Column("SearchError", DbType.Int32, ColumnProperty.NotNull),
new Column("Quality", DbType.Int32, ColumnProperty.NotNull),
new Column("Proper", DbType.Boolean, ColumnProperty.NotNull),
new Column("Age", DbType.Int32, ColumnProperty.NotNull),
new Column("Language", DbType.Int32, ColumnProperty.NotNull),
new Column("Size", DbType.Int64, ColumnProperty.NotNull),
});
}
}

View File

@ -87,5 +87,60 @@ namespace NzbDrone.Core
return s.Substring(0, i);
}
public static string AddSpacesToEnum(this Enum enumValue)
{
var text = enumValue.ToString();
if (string.IsNullOrWhiteSpace(text))
return "";
var newText = new StringBuilder(text.Length * 2);
newText.Append(text[0]);
for (int i = 1; i < text.Length; i++)
{
if (char.IsUpper(text[i]) && text[i - 1] != ' ')
newText.Append(' ');
newText.Append(text[i]);
}
return newText.ToString();
}
private const Decimal ONE_KILOBYTE = 1024M;
private const Decimal ONE_MEGABYTE = ONE_KILOBYTE * 1024M;
private const Decimal ONE_GIGABYTE = ONE_MEGABYTE * 1024M;
public static string ToBestFileSize(this long bytes, int precision = 0)
{
if (bytes == 0)
return "0B";
decimal size = Convert.ToDecimal(bytes);
string suffix;
if (size > ONE_GIGABYTE)
{
size /= ONE_GIGABYTE;
suffix = "GB";
}
else if (size > ONE_MEGABYTE)
{
size /= ONE_MEGABYTE;
suffix = "MB";
}
else if (size > ONE_KILOBYTE)
{
size /= ONE_KILOBYTE;
suffix = "KB";
}
else
{
suffix = " B";
}
return String.Format("{0:N" + precision + "} {1}", size, suffix);
}
}
}

View File

@ -1,46 +0,0 @@
using System;
namespace NzbDrone.Core.Helpers
{
public static class FileSizeFormatHelper
{
private const Decimal OneKiloByte = 1024M;
private const Decimal OneMegaByte = OneKiloByte * 1024M;
private const Decimal OneGigaByte = OneMegaByte * 1024M;
public static string Format(long bytes, int precision = 0)
{
if (bytes == 0)
return "0B";
decimal size = Convert.ToDecimal(bytes);
string suffix;
if (size > OneGigaByte)
{
size /= OneGigaByte;
suffix = "GB";
}
else if (size > OneMegaByte)
{
size /= OneMegaByte;
suffix = "MB";
}
else if (size > OneKiloByte)
{
size /= OneKiloByte;
suffix = "KB";
}
else
{
suffix = " B";
}
return String.Format("{0:N" + precision + "}{1}", size, suffix);
}
}
}

View File

@ -240,7 +240,6 @@
<Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" />
<Compile Include="Fluent.cs" />
<Compile Include="Helpers\EpisodeSortingHelper.cs" />
<Compile Include="Helpers\FileSizeFormatHelper.cs" />
<Compile Include="Helpers\SortHelper.cs" />
<Compile Include="Helpers\SabnzbdQueueTimeConverter.cs" />
<Compile Include="Jobs\CheckpointJob.cs" />

View File

@ -91,12 +91,13 @@ namespace NzbDrone.Core.Providers
e => e.EpisodeNumbers = episodeNumbers.ToList()
);
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber);
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber);
_searchResultProvider.Add(searchResult);
return (searchResult.SearchResultItems.Select(s => s.Success).Count() == episodeNumbers.Count);
return (searchResult.Successes.Count == episodeNumbers.Count);
}
public virtual List<SearchResultItem> PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber)
public virtual List<int> PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber)
{
var searchResult = new SearchResult
{
@ -110,12 +111,12 @@ namespace NzbDrone.Core.Providers
if (series == null)
{
Logger.Error("Unable to find an series {0} in database", seriesId);
return new List<SearchResultItem>();
return new List<int>();
}
//Return empty list if the series is a daily series (we only support individual episode searching
if (series.IsDaily)
return new List<SearchResultItem>();
return new List<int>();
notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber);
var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber);
@ -123,13 +124,13 @@ namespace NzbDrone.Core.Providers
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
if (reports.Count == 0)
return new List<SearchResultItem>();
return new List<int>();
notification.CurrentMessage = "Processing search results";
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber);
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber);
_searchResultProvider.Add(searchResult);
return searchResult.SearchResultItems;
return searchResult.Successes;
}
public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId)
@ -182,7 +183,7 @@ namespace NzbDrone.Core.Providers
else
{
searchResult.EpisodeId = episodeId;
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber);
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, episode.Series, episode.SeasonNumber, episode.EpisodeNumber);
_searchResultProvider.Add(searchResult);
if (searchResult.SearchResultItems.Any(r => r.Success))
@ -256,7 +257,7 @@ namespace NzbDrone.Core.Providers
return reports;
}
public List<SearchResultItem> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, int seasonNumber, int? episodeNumber = null)
public List<SearchResultItem> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, SearchResult searchResult, Series series, int seasonNumber, int? episodeNumber = null)
{
var successes = new List<int>();
var items = new List<SearchResultItem>();
@ -271,7 +272,12 @@ namespace NzbDrone.Core.Providers
{
ReportTitle = episodeParseResult.OriginalString,
NzbUrl = episodeParseResult.NzbUrl,
Indexer = episodeParseResult.Indexer
Indexer = episodeParseResult.Indexer,
Quality = episodeParseResult.Quality.QualityType,
Proper = episodeParseResult.Quality.Proper,
Size = episodeParseResult.Size,
Age = episodeParseResult.Age,
Language = episodeParseResult.Language
};
items.Add(item);
@ -360,7 +366,12 @@ namespace NzbDrone.Core.Providers
{
ReportTitle = episodeParseResult.OriginalString,
NzbUrl = episodeParseResult.NzbUrl,
Indexer = episodeParseResult.Indexer
Indexer = episodeParseResult.Indexer,
Quality = episodeParseResult.Quality.QualityType,
Proper = episodeParseResult.Quality.Proper,
Size = episodeParseResult.Size,
Age = episodeParseResult.Age,
Language = episodeParseResult.Language
};
items.Add(item);
@ -414,6 +425,7 @@ namespace NzbDrone.Core.Providers
{
Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e);
notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult);
item.SearchError = ReportRejectionType.DownloadClientFailure;
}
}
}

View File

@ -13,12 +13,17 @@ namespace NzbDrone.Core.Providers
public class SearchResultProvider
{
private readonly IDatabase _database;
private readonly SeriesProvider _seriesProvider;
private readonly DownloadProvider _downloadProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public SearchResultProvider(IDatabase database)
public SearchResultProvider(IDatabase database, SeriesProvider seriesProvider,
DownloadProvider downloadProvider)
{
_database = database;
_seriesProvider = seriesProvider;
_downloadProvider = downloadProvider;
}
public SearchResultProvider()
@ -54,7 +59,7 @@ namespace NzbDrone.Core.Providers
Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title as EpisodeTitle,
Episodes.AirDate,
Count(SearchResultItems.Id) as TotalItems,
SUM(CASE WHEN SearchResultItems.Success = 1 THEN 1 ELSE 0 END) as Successes
SUM(CASE WHEN SearchResultItems.Success = 1 THEN 1 ELSE 0 END) as SuccessfulCount
FROM SearchResults
INNER JOIN Series
ON Series.SeriesId = SearchResults.SeriesId
@ -73,10 +78,36 @@ namespace NzbDrone.Core.Providers
public virtual SearchResult GetSearchResult(int id)
{
var result = _database.Single<SearchResult>(id);
var sql = @"SELECT SearchResults.Id, SearchResults.SeriesId, SearchResults.SeasonNumber,
SearchResults.EpisodeId, SearchResults.SearchTime,
Series.Title as SeriesTitle, Series.IsDaily,
Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title as EpisodeTitle,
Episodes.AirDate
FROM SearchResults
INNER JOIN Series
ON Series.SeriesId = SearchResults.SeriesId
LEFT JOIN Episodes
ON Episodes.EpisodeId = SearchResults.EpisodeId
WHERE SearchResults.Id = @0";
var result = _database.Single<SearchResult>(sql, id);
result.SearchResultItems = _database.Fetch<SearchResultItem>("WHERE SearchResultId = @0", id);
return result;
}
public virtual void ForceDownload(int itemId)
{
var item = _database.Single<SearchResultItem>(itemId);
var searchResult = _database.Single<SearchResult>(item.SearchResultId);
var series = _seriesProvider.GetSeries(searchResult.SeriesId);
var parseResult = Parser.ParseTitle(item.ReportTitle);
parseResult.NzbUrl = item.NzbUrl;
parseResult.Series = series;
parseResult.Indexer = item.Indexer;
_downloadProvider.DownloadReport(parseResult);
}
}
}

View File

@ -21,6 +21,9 @@ namespace NzbDrone.Core.Repository.Search
[ResultColumn]
public List<SearchResultItem> SearchResultItems { get; set; }
[Ignore]
public List<int> Successes { get; set; }
[ResultColumn]
public string SeriesTitle { get; set; }
@ -40,6 +43,6 @@ namespace NzbDrone.Core.Repository.Search
public int TotalItems { get; set; }
[ResultColumn]
public int Successes { get; set; }
public int SuccessfulCount { get; set; }
}
}

View File

@ -19,5 +19,15 @@ namespace NzbDrone.Core.Repository.Search
public string NzbInfoUrl { get; set; }
public bool Success { get; set; }
public ReportRejectionType SearchError { get; set; }
public QualityTypes Quality { get; set; }
public bool Proper { get; set; }
public int Age { get; set; }
public LanguageType Language { get; set; }
public long Size { get; set; }
public override string ToString()
{
return String.Format("{0} - {1} - {2}", ReportTitle, Quality, SearchError);
}
}
}

View File

@ -4,11 +4,10 @@ using System.Linq;
using System.Linq.Dynamic;
using System.Text;
using System.Web.Mvc;
using DataTables.Mvc.Core;
using DataTables.Mvc.Core.Models;
using NzbDrone.Common;
using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository.Search;
using NzbDrone.Web.Models;
namespace NzbDrone.Web.Controllers
@ -18,15 +17,13 @@ namespace NzbDrone.Web.Controllers
private readonly LogProvider _logProvider;
private readonly EnvironmentProvider _environmentProvider;
private readonly DiskProvider _diskProvider;
private readonly SearchResultProvider _searchResultProvider;
public LogController(LogProvider logProvider, EnvironmentProvider environmentProvider,
DiskProvider diskProvider, SearchResultProvider searchResultProvider)
DiskProvider diskProvider)
{
_logProvider = logProvider;
_environmentProvider = environmentProvider;
_diskProvider = diskProvider;
_searchResultProvider = searchResultProvider;
}
public ActionResult Index()
@ -55,28 +52,6 @@ namespace NzbDrone.Web.Controllers
return JsonNotificationResult.Info("Logs Cleared");
}
public ActionResult SearchResults()
{
var results = _searchResultProvider.AllSearchResults();
var model = results.Select(s => new SearchResultsModel
{
Id = s.Id,
SearchTime = s.SearchTime.ToString(),
DisplayName = GetDisplayName(s),
ReportCount = s.TotalItems,
Successful = s.Successes > 0
});
return View(model);
}
public ActionResult SearchDetails(int searchId)
{
var model = _searchResultProvider.GetSearchResult(searchId);
return View(model);
}
public ActionResult AjaxBinding(DataTablesParams dataTablesParams)
{
var logs = _logProvider.GetAllLogs();
@ -129,24 +104,5 @@ namespace NzbDrone.Web.Controllers
},
JsonRequestBehavior.AllowGet);
}
public string GetDisplayName(SearchResult searchResult)
{
if (!searchResult.EpisodeNumber.HasValue)
{
return String.Format("{0} - Season {1}", searchResult.SeriesTitle, searchResult.SeasonNumber);
}
string episodeString;
if (searchResult.IsDaily)
episodeString = searchResult.AirDate.ToShortDateString().Replace('/', '-');
else
episodeString = String.Format("S{0:00}E{1:00}", searchResult.SeasonNumber,
searchResult.EpisodeNumber);
return String.Format("{0} - {1} - {2}", searchResult.SeriesTitle, episodeString, searchResult.EpisodeTitle);
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NzbDrone.Core;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository.Search;
using NzbDrone.Web.Models;
namespace NzbDrone.Web.Controllers
{
public class SearchResultController : Controller
{
private readonly SearchResultProvider _searchResultProvider;
public SearchResultController(SearchResultProvider searchResultProvider)
{
_searchResultProvider = searchResultProvider;
}
public ActionResult Index()
{
var results = _searchResultProvider.AllSearchResults();
var model = results.Select(s => new SearchResultsModel
{
Id = s.Id,
SearchTime = s.SearchTime.ToString(),
DisplayName = GetDisplayName(s),
ReportCount = s.TotalItems,
Successful = s.SuccessfulCount > 0
});
return View(model);
}
public ActionResult Details(int searchId)
{
var searchResult = _searchResultProvider.GetSearchResult(searchId);
var model = new SearchDetailsModel
{
Id = searchResult.Id,
DisplayName = GetDisplayName(searchResult),
SearchResultItems =
searchResult.SearchResultItems.Select(s => new SearchItemModel
{
Id = s.Id,
ReportTitle = s.ReportTitle,
Indexer = s.Indexer,
NzbUrl = s.NzbUrl,
NzbInfoUrl = s.NzbInfoUrl,
Success = s.Success,
SearchError = s.SearchError.AddSpacesToEnum().Replace("None", "Grabbed"),
Quality = s.Quality.ToString(),
QualityInt = (int)s.Quality,
Proper = s.Proper,
Age = s.Age,
Size = s.Size.ToBestFileSize(1),
Language = s.Language.ToString()
}).ToList()
};
return View(model);
}
public JsonResult ForceDownload(int id)
{
_searchResultProvider.ForceDownload(id);
return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public string GetDisplayName(SearchResult searchResult)
{
if (!searchResult.EpisodeNumber.HasValue)
{
return String.Format("{0} - Season {1}", searchResult.SeriesTitle, searchResult.SeasonNumber);
}
string episodeString;
if (searchResult.IsDaily)
episodeString = searchResult.AirDate.ToShortDateString().Replace('/', '-');
else
episodeString = String.Format("S{0:00}E{1:00}", searchResult.SeasonNumber,
searchResult.EpisodeNumber);
return String.Format("{0} - {1} - {2}", searchResult.SeriesTitle, episodeString, searchResult.EpisodeTitle);
}
}
}

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using NzbDrone.Common;
using NzbDrone.Core;
using NzbDrone.Core.Helpers;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Providers;
@ -129,7 +130,7 @@ namespace NzbDrone.Web.Controllers
foreach (var fileInfo in files)
{
fileResult += String.Format("<div><div style=\"width: 600px; display: inline-block;\">{0}</div><div style=\"display: inline-block;\">{1}</div></div>", fileInfo.Name,
FileSizeFormatHelper.Format(fileInfo.Length, 1));
fileInfo.Length.ToBestFileSize(1));
}
model.Files = fileResult;

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Repository.Search;
namespace NzbDrone.Web.Models
{
public class SearchDetailsModel
{
public int Id { get; set; }
public string DisplayName { get; set; }
public List<SearchItemModel> SearchResultItems { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Web.Models
{
public class SearchItemModel
{
public int Id { get; set; }
public string ReportTitle { get; set; }
public string Indexer { get; set; }
public string NzbUrl { get; set; }
public string NzbInfoUrl { get; set; }
public bool Success { get; set; }
public string SearchError { get; set; }
public string Quality { get; set; }
public int QualityInt { get; set; }
public bool Proper { get; set; }
public int Age { get; set; }
public string Language { get; set; }
public string Size { get; set; }
public string Details { get; set; }
}
}

View File

@ -52,7 +52,8 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="DataTables.Mvc.Core">
<HintPath>..\packages\DataTables.Mvc.0.1.0.54\lib\DataTables.Mvc.Core.dll</HintPath>
<HintPath>..\packages\DataTables.Mvc.0.1.0.67\lib\DataTables.Mvc.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dynamic">
<HintPath>..\packages\DynamicQuery.1.0\lib\35\Dynamic.dll</HintPath>
@ -141,6 +142,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\DataTablesMvc.cs" />
<Compile Include="Controllers\SearchResultController.cs" />
<Compile Include="Helpers\Validation\RequiredIfAnyAttribute.cs" />
<Compile Include="Helpers\Validation\RequiredIfAttribute.cs" />
<Content Include="Content\DataTables-1.9.0\media\css\jquery.dataTables.css" />
@ -234,9 +236,11 @@
<Compile Include="Helpers\DescriptionExtension.cs" />
<Compile Include="Helpers\HtmlPrefixScopeExtensions.cs" />
<Compile Include="Helpers\IsCurrentActionHelper.cs" />
<Compile Include="Models\SearchDetailsModel.cs" />
<Compile Include="Models\JobModel.cs" />
<Compile Include="Models\LogModel.cs" />
<Compile Include="Models\PostUpgradeModel.cs" />
<Compile Include="Models\SearchItemModel.cs" />
<Compile Include="Models\SearchResultsModel.cs" />
<Compile Include="Models\UpcomingEpisodesModel.cs" />
<Compile Include="Models\SeasonModel.cs" />
@ -521,7 +525,10 @@
<Content Include="Views\Update\Post.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Log\SearchResults.cshtml" />
<Content Include="Views\SearchResult\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\SearchResult\Details.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>

View File

@ -0,0 +1,45 @@
@using DataTables.Mvc.Core
@using DataTables.Mvc.Core.Enum
@model NzbDrone.Web.Models.SearchDetailsModel
@{
ViewBag.Title = "Search Details";
}
<h2>@Model.DisplayName</h2>
@Html.GridHtml("searchDetailsGrid")
@section Scripts
{
@(Html.GridScriptFor(m => m.SearchResultItems, "#searchDetailsGrid")
.PageLength(20)
.ChangePageLength(false)
.AddColumn(new Column().Image("/Content/Images/Indexers/{Indexer}.png", new { alt = "{Indexer}", title = "{Indexer}" }, "{Indexer}").Sortable(false).Title("").Width("20px"))
.AddColumn(new Column().DataProperty("ReportTitle").Title("Report Title"))
.AddColumn(new Column().DataProperty("Success").Title("Successful").Width("120px"))
.AddColumn(new Column().DisplayAndSort("Quality", "QualityInt").Title("Quality").Width("80px"))
.AddColumn(new Column().DataProperty("SearchError").Title("Error"))
.AddColumn(new Column().DataProperty("return actionColumn(source, type, val);", true))
.AddColumn(new Column().DataProperty("Details").RenderFunction("return getDetails(row, val);").Visible(false))
.AddSorting(3, SortDirection.Desc))
<script type="text/javascript">
function getDetails(row, val) {
var result = "<a href=\"" + row.aData["NzbInfoUrl"] + "\">Nzb Info</a><br/>" +
"<b>Proper: </b>" + row.aData["Proper"] + " <br/>" +
"<b>Age: </b>" + row.aData["Age"] + " days<br/>" +
"<b>Size: </b>" + row.aData["Size"] + " <br/>" +
"<b>Language: </b>" + row.aData["Language"] + " <br/>";
return result;
}
function actionColumn(source, type, val) {
if (type === 'display' || type === 'filter') {
return '<a href="/SearchResult/ForceDownload/' + source["Id"] + '" data-ajax="true" data-ajax-confirm="Are you sure?"><img src="/Content/Images/Plus.png" alt="Force" title="Force" class="gridAction"/></a>';
}
// 'sort' and 'type' both just use the raw data
return '';
}
</script>
}

View File

@ -10,10 +10,10 @@
@section Scripts
{
@(
Html.GridScriptForModel("#searchResultsGrid")
Html.GridScriptForModel("#searchResultsGrid")
.PageLength(20)
.ChangePageLength(false)
.AddColumn(new Column().DataProperty("DisplayName").Link("SearchDetails?searchId={Id}", "{DisplayName}").Title("Name"))
.AddColumn(new Column().DataProperty("DisplayName").Link("SearchResult/Details?searchId={Id}", "{DisplayName}", null).Title("Name"))
.AddColumn(new Column().DataProperty("SearchTime").Title("Time").Width("170px"))
.AddColumn(new Column().DataProperty("ReportCount").Title("Reports Found").Width("140px"))
.AddColumn(new Column().DataProperty("Successful").Title("Successful").Width("110px"))

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DataTables.Mvc" version="0.1.0.54" />
<package id="DataTables.Mvc" version="0.1.0.67" />
<package id="DynamicQuery" version="1.0" />
<package id="EntityFramework" version="4.3.0" />
<package id="EntityFramework.SqlServerCompact" version="4.1.8482.2" />

View File

@ -0,0 +1,17 @@
using DataTables.Mvc.Core.Helpers;
using DataTables.Mvc.Core.Models;
using System.Web.Mvc;
[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.DataTablesModelBinderActivator), "Start")]
namespace $rootnamespace$.App_Start
{
public static class DataTablesModelBinderActivator
{
public static void Start()
{
if (!ModelBinders.Binders.ContainsKey(typeof(DataTablesParams)))
ModelBinders.Binders.Add(typeof(DataTablesParams), new DataTablesModelBinder());
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
param($installPath, $toolsPath, $package, $project)
$path = [System.IO.Path]
$appstart = $path::Combine($path::GetDirectoryName($project.FileName), "App_Start\DataTablesMvc.cs")
$DTE.ItemOperations.OpenFile($appstart)