You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
9.9 KiB
230 lines
9.9 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using NzbDrone.Common.Extensions;
|
|
using NzbDrone.Core.Datastore;
|
|
using NzbDrone.Core.MediaFiles;
|
|
using NzbDrone.Core.Messaging.Events;
|
|
using NzbDrone.Core.Qualities;
|
|
|
|
namespace NzbDrone.Core.Books
|
|
{
|
|
public interface IBookRepository : IBasicRepository<Book>
|
|
{
|
|
List<Book> GetBooks(int authorId);
|
|
List<Book> GetLastBooks(IEnumerable<int> authorMetadataIds);
|
|
List<Book> GetNextBooks(IEnumerable<int> authorMetadataIds);
|
|
List<Book> GetBooksByAuthorMetadataId(int authorMetadataId);
|
|
List<Book> GetBooksForRefresh(int authorMetadataId, List<string> foreignIds);
|
|
List<Book> GetBooksByFileIds(IEnumerable<int> fileIds);
|
|
Book FindByTitle(int authorMetadataId, string title);
|
|
Book FindById(string foreignBookId);
|
|
Book FindBySlug(string titleSlug);
|
|
PagingSpec<Book> BooksWithoutFiles(PagingSpec<Book> pagingSpec);
|
|
PagingSpec<Book> BooksWhereCutoffUnmet(PagingSpec<Book> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff);
|
|
List<Book> BooksBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored);
|
|
List<Book> AuthorBooksBetweenDates(Author author, DateTime startDate, DateTime endDate, bool includeUnmonitored);
|
|
void SetMonitoredFlat(Book book, bool monitored);
|
|
void SetMonitored(IEnumerable<int> ids, bool monitored);
|
|
List<Book> GetAuthorBooksWithFiles(Author author);
|
|
}
|
|
|
|
public class BookRepository : BasicRepository<Book>, IBookRepository
|
|
{
|
|
public BookRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
|
: base(database, eventAggregator)
|
|
{
|
|
}
|
|
|
|
public List<Book> GetBooks(int authorId)
|
|
{
|
|
return Query(Builder().Join<Book, Author>((l, r) => l.AuthorMetadataId == r.AuthorMetadataId).Where<Author>(a => a.Id == authorId));
|
|
}
|
|
|
|
public List<Book> GetLastBooks(IEnumerable<int> authorMetadataIds)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
|
|
var inner = Builder()
|
|
.Select("MIN(\"Books\".\"Id\") as id, MAX(\"Books\".\"ReleaseDate\") as date")
|
|
.Where<Book>(x => authorMetadataIds.Contains(x.AuthorMetadataId) && x.ReleaseDate < now)
|
|
.GroupBy<Book>(x => x.AuthorMetadataId)
|
|
.AddSelectTemplate(typeof(Book));
|
|
|
|
var outer = Builder()
|
|
.Join($"({inner.RawSql}) ids on ids.id = \"Books\".\"Id\" and ids.date = \"Books\".\"ReleaseDate\"")
|
|
.AddParameters(inner.Parameters);
|
|
|
|
return Query(outer);
|
|
}
|
|
|
|
public List<Book> GetNextBooks(IEnumerable<int> authorMetadataIds)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
|
|
var inner = Builder()
|
|
.Select("MIN(\"Books\".\"Id\") as id, MIN(\"Books\".\"ReleaseDate\") as date")
|
|
.Where<Book>(x => authorMetadataIds.Contains(x.AuthorMetadataId) && x.ReleaseDate > now)
|
|
.GroupBy<Book>(x => x.AuthorMetadataId)
|
|
.AddSelectTemplate(typeof(Book));
|
|
|
|
var outer = Builder()
|
|
.Join($"({inner.RawSql}) ids on ids.id = \"Books\".\"Id\" and ids.date = \"Books\".\"ReleaseDate\"")
|
|
.AddParameters(inner.Parameters);
|
|
|
|
return Query(outer);
|
|
}
|
|
|
|
public List<Book> GetBooksByAuthorMetadataId(int authorMetadataId)
|
|
{
|
|
return Query(s => s.AuthorMetadataId == authorMetadataId);
|
|
}
|
|
|
|
public List<Book> GetBooksForRefresh(int authorMetadataId, List<string> foreignIds)
|
|
{
|
|
return Query(a => a.AuthorMetadataId == authorMetadataId || foreignIds.Contains(a.ForeignBookId));
|
|
}
|
|
|
|
public List<Book> GetBooksByFileIds(IEnumerable<int> fileIds)
|
|
{
|
|
return Query(new SqlBuilder(_database.DatabaseType)
|
|
.Join<Book, Edition>((b, e) => b.Id == e.BookId)
|
|
.Join<Edition, BookFile>((l, r) => l.Id == r.EditionId)
|
|
.Where<BookFile>(f => fileIds.Contains(f.Id)))
|
|
.DistinctBy(x => x.Id)
|
|
.ToList();
|
|
}
|
|
|
|
public Book FindById(string foreignBookId)
|
|
{
|
|
return Query(s => s.ForeignBookId == foreignBookId).SingleOrDefault();
|
|
}
|
|
|
|
public Book FindBySlug(string titleSlug)
|
|
{
|
|
return Query(s => s.TitleSlug == titleSlug).SingleOrDefault();
|
|
}
|
|
|
|
//x.Id == null is converted to SQL, so warning incorrect
|
|
#pragma warning disable CS0472
|
|
private SqlBuilder BooksWithoutFilesBuilder(DateTime currentTime) => Builder()
|
|
.Join<Book, Author>((l, r) => l.AuthorMetadataId == r.AuthorMetadataId)
|
|
.Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id)
|
|
.Join<Book, Edition>((b, e) => b.Id == e.BookId)
|
|
.LeftJoin<Edition, BookFile>((t, f) => t.Id == f.EditionId)
|
|
.Where<BookFile>(f => f.Id == null)
|
|
.Where<Edition>(e => e.Monitored == true)
|
|
.Where<Book>(a => a.ReleaseDate <= currentTime);
|
|
#pragma warning restore CS0472
|
|
|
|
public PagingSpec<Book> BooksWithoutFiles(PagingSpec<Book> pagingSpec)
|
|
{
|
|
var currentTime = DateTime.UtcNow;
|
|
|
|
pagingSpec.Records = GetPagedRecords(BooksWithoutFilesBuilder(currentTime), pagingSpec, PagedQuery);
|
|
pagingSpec.TotalRecords = GetPagedRecordCount(BooksWithoutFilesBuilder(currentTime).SelectCountDistinct<Book>(x => x.Id), pagingSpec);
|
|
|
|
return pagingSpec;
|
|
}
|
|
|
|
private SqlBuilder BooksWhereCutoffUnmetBuilder(List<QualitiesBelowCutoff> qualitiesBelowCutoff) => Builder()
|
|
.Join<Book, Author>((l, r) => l.AuthorMetadataId == r.AuthorMetadataId)
|
|
.Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id)
|
|
.Join<Book, Edition>((b, e) => b.Id == e.BookId)
|
|
.LeftJoin<Edition, BookFile>((t, f) => t.Id == f.EditionId)
|
|
.Where<Edition>(e => e.Monitored == true)
|
|
.Where(BuildQualityCutoffWhereClause(qualitiesBelowCutoff));
|
|
|
|
private string BuildQualityCutoffWhereClause(List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
|
{
|
|
var clauses = new List<string>();
|
|
|
|
foreach (var profile in qualitiesBelowCutoff)
|
|
{
|
|
foreach (var belowCutoff in profile.QualityIds)
|
|
{
|
|
clauses.Add(string.Format("(\"Authors\".\"QualityProfileId\" = {0} AND \"BookFiles\".\"Quality\" LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff));
|
|
}
|
|
}
|
|
|
|
return string.Format("({0})", string.Join(" OR ", clauses));
|
|
}
|
|
|
|
public PagingSpec<Book> BooksWhereCutoffUnmet(PagingSpec<Book> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
|
{
|
|
pagingSpec.Records = GetPagedRecords(BooksWhereCutoffUnmetBuilder(qualitiesBelowCutoff), pagingSpec, PagedQuery);
|
|
|
|
var countTemplate = $"SELECT COUNT(*) FROM (SELECT /**select**/ FROM \"{TableMapping.Mapper.TableNameMapping(typeof(Book))}\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/) AS \"Inner\"";
|
|
pagingSpec.TotalRecords = GetPagedRecordCount(BooksWhereCutoffUnmetBuilder(qualitiesBelowCutoff).Select(typeof(Book)), pagingSpec, countTemplate);
|
|
|
|
return pagingSpec;
|
|
}
|
|
|
|
public List<Book> BooksBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored)
|
|
{
|
|
var builder = Builder().Where<Book>(rg => rg.ReleaseDate >= startDate && rg.ReleaseDate <= endDate);
|
|
|
|
if (!includeUnmonitored)
|
|
{
|
|
builder = builder.Where<Book>(e => e.Monitored == true)
|
|
.Join<Book, Author>((l, r) => l.AuthorMetadataId == r.AuthorMetadataId)
|
|
.Where<Author>(e => e.Monitored == true);
|
|
}
|
|
|
|
return Query(builder);
|
|
}
|
|
|
|
public List<Book> AuthorBooksBetweenDates(Author author, DateTime startDate, DateTime endDate, bool includeUnmonitored)
|
|
{
|
|
var builder = Builder().Where<Book>(rg => rg.ReleaseDate >= startDate &&
|
|
rg.ReleaseDate <= endDate &&
|
|
rg.AuthorMetadataId == author.AuthorMetadataId);
|
|
|
|
if (!includeUnmonitored)
|
|
{
|
|
builder = builder.Where<Book>(e => e.Monitored == true)
|
|
.Join<Book, Author>((l, r) => l.AuthorMetadataId == r.AuthorMetadataId)
|
|
.Where<Author>(e => e.Monitored == true);
|
|
}
|
|
|
|
return Query(builder);
|
|
}
|
|
|
|
public void SetMonitoredFlat(Book book, bool monitored)
|
|
{
|
|
book.Monitored = monitored;
|
|
SetFields(book, p => p.Monitored);
|
|
|
|
ModelUpdated(book, true);
|
|
}
|
|
|
|
public void SetMonitored(IEnumerable<int> ids, bool monitored)
|
|
{
|
|
var books = ids.Select(x => new Book { Id = x, Monitored = monitored }).ToList();
|
|
SetFields(books, p => p.Monitored);
|
|
}
|
|
|
|
public Book FindByTitle(int authorMetadataId, string title)
|
|
{
|
|
var cleanTitle = Parser.Parser.CleanAuthorName(title);
|
|
|
|
if (string.IsNullOrEmpty(cleanTitle))
|
|
{
|
|
cleanTitle = title;
|
|
}
|
|
|
|
return Query(s => (s.CleanTitle == cleanTitle || s.Title == title) && s.AuthorMetadataId == authorMetadataId)
|
|
.ExclusiveOrDefault();
|
|
}
|
|
|
|
public List<Book> GetAuthorBooksWithFiles(Author author)
|
|
{
|
|
return Query(Builder()
|
|
.Join<Book, Edition>((b, e) => b.Id == e.BookId)
|
|
.Join<Edition, BookFile>((t, f) => t.Id == f.EditionId)
|
|
.Where<Book>(x => x.AuthorMetadataId == author.AuthorMetadataId)
|
|
.Where<Edition>(e => e.Monitored == true));
|
|
}
|
|
}
|
|
}
|