Skip to content

Commit

Permalink
Polish 6 (#3371)
Browse files Browse the repository at this point in the history
  • Loading branch information
majora2007 authored Nov 16, 2024
1 parent 6a75291 commit c849eff
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 35 deletions.
13 changes: 12 additions & 1 deletion API/Controllers/PersonController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ public async Task<ActionResult<PersonDto>> UpdatePerson(UpdatePersonDto dto)
return Ok(_mapper.Map<PersonDto>(person));
}

/// <summary>
/// Attempts to download the cover from CoversDB (Note: Not yet release in Kavita)
/// </summary>
/// <param name="personId"></param>
/// <returns></returns>
[HttpPost("fetch-cover")]
public async Task<ActionResult<string>> DownloadCoverImage([FromQuery] int personId)
{
Expand All @@ -129,13 +134,13 @@ public async Task<ActionResult<string>> DownloadCoverImage([FromQuery] int perso
var personImage = await _coverDbService.DownloadPersonImageAsync(person, settings.EncodeMediaAs);

if (string.IsNullOrEmpty(personImage)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "person-image-doesnt-exist"));

person.CoverImage = personImage;
_imageService.UpdateColorScape(person);
_unitOfWork.PersonRepository.Update(person);
await _unitOfWork.CommitAsync();
await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate, MessageFactory.CoverUpdateEvent(person.Id, "person"), false);


return Ok(personImage);
}

Expand All @@ -150,6 +155,12 @@ public async Task<ActionResult<IEnumerable<SeriesDto>>> GetKnownSeries(int perso
return Ok(await _unitOfWork.PersonRepository.GetSeriesKnownFor(personId));
}

/// <summary>
/// Returns all individual chapters by role. Limited to 20 results.
/// </summary>
/// <param name="personId"></param>
/// <param name="role"></param>
/// <returns></returns>
[HttpGet("chapters-by-role")]
public async Task<ActionResult<IEnumerable<StandaloneChapterDto>>> GetChaptersByRole(int personId, PersonRole role)
{
Expand Down
2 changes: 1 addition & 1 deletion API/DTOs/VolumeDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public bool IsLooseLeaf()
}

/// <summary>
/// Does this volume hold only specials?
/// Does this volume hold only specials
/// </summary>
/// <returns></returns>
public bool IsSpecial()
Expand Down
2 changes: 1 addition & 1 deletion API/Data/Repositories/SeriesRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,7 +1192,7 @@ private static IQueryable<Series> ApplyLibraryFilter(FilterV2Dto filter, IQuerya

private static IQueryable<Series> BuildFilterQuery(int userId, FilterV2Dto filterDto, IQueryable<Series> query)
{
if (filterDto.Statements == null || !filterDto.Statements.Any()) return query;
if (filterDto.Statements == null || filterDto.Statements.Count == 0) return query;


var queries = filterDto.Statements
Expand Down
52 changes: 26 additions & 26 deletions API/Extensions/QueryExtensions/Filtering/SeriesFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public static IQueryable<Series> HasAgeRating(this IQueryable<Series> queryable,
throw new ArgumentOutOfRangeException(nameof(comparison), comparison, null);
}
}

public static IQueryable<Series> HasAverageReadTime(this IQueryable<Series> queryable, bool condition,
FilterComparison comparison, int avgReadTime)
{
Expand All @@ -175,17 +176,17 @@ public static IQueryable<Series> HasAverageReadTime(this IQueryable<Series> quer
switch (comparison)
{
case FilterComparison.NotEqual:
return queryable.Where(s => s.AvgHoursToRead != avgReadTime);
return queryable.WhereNotEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.Equal:
return queryable.Where(s => s.AvgHoursToRead == avgReadTime);
return queryable.WhereEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.GreaterThan:
return queryable.Where(s => s.AvgHoursToRead > avgReadTime);
return queryable.WhereGreaterThan(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.GreaterThanEqual:
return queryable.Where(s => s.AvgHoursToRead >= avgReadTime);
return queryable.WhereGreaterThanOrEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.LessThan:
return queryable.Where(s => s.AvgHoursToRead < avgReadTime);
return queryable.WhereLessThan(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.LessThanEqual:
return queryable.Where(s => s.AvgHoursToRead <= avgReadTime);
return queryable.WhereLessThanOrEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.Contains:
case FilterComparison.Matches:
case FilterComparison.NotContains:
Expand Down Expand Up @@ -257,29 +258,29 @@ public static IQueryable<Series> HasReadingProgress(this IQueryable<Series> quer
Series = s,
Percentage = s.Progress
.Where(p => p != null && p.AppUserId == userId)
.Sum(p => p != null ? (p.PagesRead * 1.0f / s.Pages) : 0) * 100
.Sum(p => p != null ? (p.PagesRead * 1.0f / s.Pages) : 0f) * 100f
})
.AsSplitQuery();

switch (comparison)
{
case FilterComparison.Equal:
subQuery = subQuery.Where(s => Math.Abs(s.Percentage - readProgress) < FloatingPointTolerance);
subQuery = subQuery.WhereEqual(s => s.Percentage, readProgress);
break;
case FilterComparison.GreaterThan:
subQuery = subQuery.Where(s => s.Percentage > readProgress);
subQuery = subQuery.WhereGreaterThan(s => s.Percentage, readProgress);
break;
case FilterComparison.GreaterThanEqual:
subQuery = subQuery.Where(s => s.Percentage >= readProgress);
subQuery = subQuery.WhereGreaterThanOrEqual(s => s.Percentage, readProgress);
break;
case FilterComparison.LessThan:
subQuery = subQuery.Where(s => s.Percentage < readProgress);
subQuery = subQuery.WhereLessThan(s => s.Percentage, readProgress);
break;
case FilterComparison.LessThanEqual:
subQuery = subQuery.Where(s => s.Percentage <= readProgress);
subQuery = subQuery.WhereLessThanOrEqual(s => s.Percentage, readProgress);
break;
case FilterComparison.NotEqual:
subQuery = subQuery.Where(s => Math.Abs(s.Percentage - readProgress) > FloatingPointTolerance);
subQuery = subQuery.WhereNotEqual(s => s.Percentage, readProgress);
break;
case FilterComparison.IsEmpty:
case FilterComparison.Matches:
Expand All @@ -306,7 +307,6 @@ public static IQueryable<Series> HasAverageRating(this IQueryable<Series> querya
{
if (!condition) return queryable;


var subQuery = queryable
.Where(s => s.ExternalSeriesMetadata != null)
.Include(s => s.ExternalSeriesMetadata)
Expand All @@ -316,27 +316,27 @@ public static IQueryable<Series> HasAverageRating(this IQueryable<Series> querya
AverageRating = s.ExternalSeriesMetadata.AverageExternalRating
})
.AsSplitQuery()
.AsEnumerable();
.AsQueryable();

switch (comparison)
{
case FilterComparison.Equal:
subQuery = subQuery.Where(s => Math.Abs(s.AverageRating - rating) < FloatingPointTolerance);
subQuery = subQuery.WhereEqual(s => s.AverageRating, rating);
break;
case FilterComparison.GreaterThan:
subQuery = subQuery.Where(s => s.AverageRating > rating);
subQuery = subQuery.WhereGreaterThan(s => s.AverageRating, rating);
break;
case FilterComparison.GreaterThanEqual:
subQuery = subQuery.Where(s => s.AverageRating >= rating);
subQuery = subQuery.WhereGreaterThanOrEqual(s => s.AverageRating, rating);
break;
case FilterComparison.LessThan:
subQuery = subQuery.Where(s => s.AverageRating < rating);
subQuery = subQuery.WhereLessThan(s => s.AverageRating, rating);
break;
case FilterComparison.LessThanEqual:
subQuery = subQuery.Where(s => s.AverageRating <= rating);
subQuery = subQuery.WhereLessThanOrEqual(s => s.AverageRating, rating);
break;
case FilterComparison.NotEqual:
subQuery = subQuery.Where(s => Math.Abs(s.AverageRating - rating) > FloatingPointTolerance);
subQuery = subQuery.WhereNotEqual(s => s.AverageRating, rating);
break;
case FilterComparison.Matches:
case FilterComparison.Contains:
Expand Down Expand Up @@ -534,21 +534,21 @@ public static IQueryable<Series> HasPeople(this IQueryable<Series> queryable, bo
{
case FilterComparison.Equal:
case FilterComparison.Contains:
return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.PersonId)));
return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.PersonId) && p.Role == role));
case FilterComparison.NotEqual:
case FilterComparison.NotContains:
return queryable.Where(s => s.Metadata.People.All(t => !people.Contains(t.PersonId)));
return queryable.Where(s => s.Metadata.People.All(p => !people.Contains(p.PersonId) || p.Role != role));
case FilterComparison.MustContains:
// Deconstruct and do a Union of a bunch of where statements since this doesn't translate
var queries = new List<IQueryable<Series>>()
{
queryable
};
queries.AddRange(people.Select(gId => queryable.Where(s => s.Metadata.People.Any(p => p.PersonId == gId))));
queries.AddRange(people.Select(personId =>
queryable.Where(s => s.Metadata.People.Any(p => p.PersonId == personId && p.Role == role))));

return queries.Aggregate((q1, q2) => q1.Intersect(q2));
case FilterComparison.IsEmpty:
// Check if there are no people with specific roles (e.g., Writer, Penciller, etc.)
// Ensure no person with the given role exists
return queryable.Where(s => s.Metadata.People.All(p => p.Role != role));
case FilterComparison.GreaterThan:
case FilterComparison.GreaterThanEqual:
Expand Down
136 changes: 136 additions & 0 deletions API/Extensions/QueryExtensions/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace API.Extensions.QueryExtensions;

public static class QueryableExtensions
{
private const float DefaultTolerance = 0.001f;

public static Task<AgeRestriction> GetUserAgeRestriction(this DbSet<AppUser> queryable, int userId)
{
if (userId < 1)
Expand Down Expand Up @@ -125,6 +127,140 @@ public static IQueryable<T> WhereLike<T>(this IQueryable<T> queryable, bool cond
return queryable.Where(lambda);
}

public static IQueryable<T> WhereGreaterThan<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;

// Absolute difference comparison: (propertyAccess - value) > tolerance
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);

var greaterThanExpression = Expression.GreaterThan(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.GreaterThan(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(greaterThanExpression, toleranceExpression);

var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);

return source.Where(lambda);
}

public static IQueryable<T> WhereGreaterThanOrEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;

var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);

var greaterThanOrEqualExpression = Expression.GreaterThanOrEqual(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.GreaterThanOrEqual(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(greaterThanOrEqualExpression, toleranceExpression);

var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);

return source.Where(lambda);
}

public static IQueryable<T> WhereLessThan<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;

var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);

var lessThanExpression = Expression.LessThan(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.LessThan(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(lessThanExpression, toleranceExpression);

var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);

return source.Where(lambda);
}

public static IQueryable<T> WhereLessThanOrEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;

var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);

var lessThanOrEqualExpression = Expression.LessThanOrEqual(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.LessThanOrEqual(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(lessThanOrEqualExpression, toleranceExpression);

var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);

return source.Where(lambda);
}

public static IQueryable<T> WhereEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;

// Absolute difference comparison: Math.Abs(propertyAccess - value) < tolerance
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);

var toleranceExpression = Expression.LessThan(absoluteDifference, Expression.Constant(tolerance));
var lambda = Expression.Lambda<Func<T, bool>>(toleranceExpression, parameter);

return source.Where(lambda);
}

public static IQueryable<T> WhereNotEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;

var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);

var toleranceExpression = Expression.GreaterThan(absoluteDifference, Expression.Constant(tolerance));
var lambda = Expression.Lambda<Func<T, bool>>(toleranceExpression, parameter);

return source.Where(lambda);
}

/// <summary>
/// Performs a WhereLike that ORs multiple fields
/// </summary>
Expand Down
5 changes: 2 additions & 3 deletions API/Services/Tasks/Scanner/ProcessSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,8 @@ await _eventHub.SendMessageAsync(MessageFactory.SeriesAdded,
return;
}

BackgroundJob.Enqueue(() =>
_metadataService.GenerateCoversForSeries(series.LibraryId, series.Id, false, false));
BackgroundJob.Enqueue(() => _wordCountAnalyzerService.ScanSeries(series.LibraryId, series.Id, forceUpdate));
await _metadataService.GenerateCoversForSeries(series.LibraryId, series.Id, false, false);
await _wordCountAnalyzerService.ScanSeries(series.LibraryId, series.Id, forceUpdate);
}

private async Task ReportDuplicateSeriesLookup(Library library, ParserInfo firstInfo, Exception ex)
Expand Down
Loading

0 comments on commit c849eff

Please sign in to comment.