Browse by Genre/Tag/Person with new metadata system for People (#3835)
Co-authored-by: Stepan Goremykin <s.goremykin@proton.me> Co-authored-by: goremykin <goremukin@gmail.com> Co-authored-by: Christopher <39032787+MrRobotjs@users.noreply.github.com> Co-authored-by: Fesaa <77553571+Fesaa@users.noreply.github.com>
This commit is contained in:
parent
00c4712fc3
commit
c52ed1f65d
147 changed files with 6612 additions and 958 deletions
|
|
@ -2,13 +2,19 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data.Misc;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Filtering.v2;
|
||||
using API.DTOs.Metadata.Browse;
|
||||
using API.DTOs.Metadata.Browse.Requests;
|
||||
using API.DTOs.Person;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Person;
|
||||
using API.Extensions;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using API.Extensions.QueryExtensions.Filtering;
|
||||
using API.Helpers;
|
||||
using API.Helpers.Converters;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
@ -45,7 +51,7 @@ public interface IPersonRepository
|
|||
Task<string?> GetCoverImageAsync(int personId);
|
||||
Task<string?> GetCoverImageByNameAsync(string name);
|
||||
Task<IEnumerable<PersonRole>> GetRolesForPersonByName(int personId, int userId);
|
||||
Task<PagedList<BrowsePersonDto>> GetAllWritersAndSeriesCount(int userId, UserParams userParams);
|
||||
Task<PagedList<BrowsePersonDto>> GetBrowsePersonDtos(int userId, BrowsePersonFilterDto filter, UserParams userParams);
|
||||
Task<Person?> GetPersonById(int personId, PersonIncludes includes = PersonIncludes.None);
|
||||
Task<PersonDto?> GetPersonDtoByName(string name, int userId, PersonIncludes includes = PersonIncludes.Aliases);
|
||||
/// <summary>
|
||||
|
|
@ -194,36 +200,82 @@ public class PersonRepository : IPersonRepository
|
|||
return chapterRoles.Union(seriesRoles).Distinct();
|
||||
}
|
||||
|
||||
public async Task<PagedList<BrowsePersonDto>> GetAllWritersAndSeriesCount(int userId, UserParams userParams)
|
||||
public async Task<PagedList<BrowsePersonDto>> GetBrowsePersonDtos(int userId, BrowsePersonFilterDto filter, UserParams userParams)
|
||||
{
|
||||
List<PersonRole> roles = [PersonRole.Writer, PersonRole.CoverArtist];
|
||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
var query = _context.Person
|
||||
.Where(p => p.SeriesMetadataPeople.Any(smp => roles.Contains(smp.Role)) || p.ChapterPeople.Any(cmp => roles.Contains(cmp.Role)))
|
||||
.RestrictAgainstAgeRestriction(ageRating)
|
||||
.Select(p => new BrowsePersonDto
|
||||
{
|
||||
Id = p.Id,
|
||||
Name = p.Name,
|
||||
Description = p.Description,
|
||||
CoverImage = p.CoverImage,
|
||||
SeriesCount = p.SeriesMetadataPeople
|
||||
.Where(smp => roles.Contains(smp.Role))
|
||||
.Select(smp => smp.SeriesMetadata.SeriesId)
|
||||
.Distinct()
|
||||
.Count(),
|
||||
IssueCount = p.ChapterPeople
|
||||
.Where(cp => roles.Contains(cp.Role))
|
||||
.Select(cp => cp.Chapter.Id)
|
||||
.Distinct()
|
||||
.Count()
|
||||
})
|
||||
.OrderBy(p => p.Name);
|
||||
var query = CreateFilteredPersonQueryable(userId, filter, ageRating);
|
||||
|
||||
return await PagedList<BrowsePersonDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
private IQueryable<BrowsePersonDto> CreateFilteredPersonQueryable(int userId, BrowsePersonFilterDto filter, AgeRestriction ageRating)
|
||||
{
|
||||
var query = _context.Person.AsNoTracking();
|
||||
|
||||
// Apply filtering based on statements
|
||||
query = BuildPersonFilterQuery(userId, filter, query);
|
||||
|
||||
// Apply age restriction
|
||||
query = query.RestrictAgainstAgeRestriction(ageRating);
|
||||
|
||||
// Apply sorting and limiting
|
||||
var sortedQuery = query.SortBy(filter.SortOptions);
|
||||
|
||||
var limitedQuery = ApplyPersonLimit(sortedQuery, filter.LimitTo);
|
||||
|
||||
// Project to DTO
|
||||
var projectedQuery = limitedQuery.Select(p => new BrowsePersonDto
|
||||
{
|
||||
Id = p.Id,
|
||||
Name = p.Name,
|
||||
Description = p.Description,
|
||||
CoverImage = p.CoverImage,
|
||||
SeriesCount = p.SeriesMetadataPeople
|
||||
.Select(smp => smp.SeriesMetadata.SeriesId)
|
||||
.Distinct()
|
||||
.Count(),
|
||||
ChapterCount = p.ChapterPeople
|
||||
.Select(cp => cp.Chapter.Id)
|
||||
.Distinct()
|
||||
.Count()
|
||||
});
|
||||
|
||||
return projectedQuery;
|
||||
}
|
||||
|
||||
private static IQueryable<Person> BuildPersonFilterQuery(int userId, BrowsePersonFilterDto filterDto, IQueryable<Person> query)
|
||||
{
|
||||
if (filterDto.Statements == null || filterDto.Statements.Count == 0) return query;
|
||||
|
||||
var queries = filterDto.Statements
|
||||
.Select(statement => BuildPersonFilterGroup(userId, statement, query))
|
||||
.ToList();
|
||||
|
||||
return filterDto.Combination == FilterCombination.And
|
||||
? queries.Aggregate((q1, q2) => q1.Intersect(q2))
|
||||
: queries.Aggregate((q1, q2) => q1.Union(q2));
|
||||
}
|
||||
|
||||
private static IQueryable<Person> BuildPersonFilterGroup(int userId, PersonFilterStatementDto statement, IQueryable<Person> query)
|
||||
{
|
||||
var value = PersonFilterFieldValueConverter.ConvertValue(statement.Field, statement.Value);
|
||||
|
||||
return statement.Field switch
|
||||
{
|
||||
PersonFilterField.Name => query.HasPersonName(true, statement.Comparison, (string)value),
|
||||
PersonFilterField.Role => query.HasPersonRole(true, statement.Comparison, (IList<PersonRole>)value),
|
||||
PersonFilterField.SeriesCount => query.HasPersonSeriesCount(true, statement.Comparison, (int)value),
|
||||
PersonFilterField.ChapterCount => query.HasPersonChapterCount(true, statement.Comparison, (int)value),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(statement.Field), $"Unexpected value for field: {statement.Field}")
|
||||
};
|
||||
}
|
||||
|
||||
private static IQueryable<Person> ApplyPersonLimit(IQueryable<Person> query, int limit)
|
||||
{
|
||||
return limit <= 0 ? query : query.Take(limit);
|
||||
}
|
||||
|
||||
public async Task<Person?> GetPersonById(int personId, PersonIncludes includes = PersonIncludes.None)
|
||||
{
|
||||
return await _context.Person.Where(p => p.Id == personId)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue