People Aliases and Merging (#3795)
Co-authored-by: Joseph Milazzo <josephmajora@gmail.com>
This commit is contained in:
parent
cd2a6af6f2
commit
7ce36bfc44
67 changed files with 5288 additions and 284 deletions
|
@ -1678,6 +1678,130 @@ public class ExternalMetadataServiceTests : AbstractDbTest
|
|||
|
||||
#endregion
|
||||
|
||||
#region People Alias
|
||||
|
||||
[Fact]
|
||||
public async Task PeopleAliasing_AddAsAlias()
|
||||
{
|
||||
await ResetDb();
|
||||
|
||||
const string seriesName = "Test - People - Add as Alias";
|
||||
var series = new SeriesBuilder(seriesName)
|
||||
.WithLibraryId(1)
|
||||
.WithMetadata(new SeriesMetadataBuilder()
|
||||
.Build())
|
||||
.Build();
|
||||
Context.Series.Attach(series);
|
||||
Context.Person.Add(new PersonBuilder("John Doe").Build());
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
var metadataSettings = await UnitOfWork.SettingsRepository.GetMetadataSettings();
|
||||
metadataSettings.Enabled = true;
|
||||
metadataSettings.EnablePeople = true;
|
||||
metadataSettings.FirstLastPeopleNaming = true;
|
||||
metadataSettings.Overrides = [MetadataSettingField.People];
|
||||
metadataSettings.PersonRoles = [PersonRole.Writer];
|
||||
Context.MetadataSettings.Update(metadataSettings);
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
await _externalMetadataService.WriteExternalMetadataToSeries(new ExternalSeriesDetailDto()
|
||||
{
|
||||
Name = seriesName,
|
||||
Staff = [CreateStaff("Doe", "John", "Story")]
|
||||
}, 1);
|
||||
|
||||
var postSeries = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Metadata);
|
||||
Assert.NotNull(postSeries);
|
||||
|
||||
var allWriters = postSeries.Metadata.People.Where(p => p.Role == PersonRole.Writer).ToList();
|
||||
Assert.Single(allWriters);
|
||||
|
||||
var johnDoe = allWriters[0].Person;
|
||||
|
||||
Assert.Contains("Doe John", johnDoe.Aliases.Select(pa => pa.Alias));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PeopleAliasing_AddOnAlias()
|
||||
{
|
||||
await ResetDb();
|
||||
|
||||
const string seriesName = "Test - People - Add as Alias";
|
||||
var series = new SeriesBuilder(seriesName)
|
||||
.WithLibraryId(1)
|
||||
.WithMetadata(new SeriesMetadataBuilder()
|
||||
.Build())
|
||||
.Build();
|
||||
Context.Series.Attach(series);
|
||||
|
||||
Context.Person.Add(new PersonBuilder("John Doe").WithAlias("Doe John").Build());
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
var metadataSettings = await UnitOfWork.SettingsRepository.GetMetadataSettings();
|
||||
metadataSettings.Enabled = true;
|
||||
metadataSettings.EnablePeople = true;
|
||||
metadataSettings.FirstLastPeopleNaming = true;
|
||||
metadataSettings.Overrides = [MetadataSettingField.People];
|
||||
metadataSettings.PersonRoles = [PersonRole.Writer];
|
||||
Context.MetadataSettings.Update(metadataSettings);
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
await _externalMetadataService.WriteExternalMetadataToSeries(new ExternalSeriesDetailDto()
|
||||
{
|
||||
Name = seriesName,
|
||||
Staff = [CreateStaff("Doe", "John", "Story")]
|
||||
}, 1);
|
||||
|
||||
var postSeries = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Metadata);
|
||||
Assert.NotNull(postSeries);
|
||||
|
||||
var allWriters = postSeries.Metadata.People.Where(p => p.Role == PersonRole.Writer).ToList();
|
||||
Assert.Single(allWriters);
|
||||
|
||||
var johnDoe = allWriters[0].Person;
|
||||
|
||||
Assert.Contains("Doe John", johnDoe.Aliases.Select(pa => pa.Alias));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PeopleAliasing_DontAddAsAlias_SameButNotSwitched()
|
||||
{
|
||||
await ResetDb();
|
||||
|
||||
const string seriesName = "Test - People - Add as Alias";
|
||||
var series = new SeriesBuilder(seriesName)
|
||||
.WithLibraryId(1)
|
||||
.WithMetadata(new SeriesMetadataBuilder()
|
||||
.Build())
|
||||
.Build();
|
||||
Context.Series.Attach(series);
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
var metadataSettings = await UnitOfWork.SettingsRepository.GetMetadataSettings();
|
||||
metadataSettings.Enabled = true;
|
||||
metadataSettings.EnablePeople = true;
|
||||
metadataSettings.FirstLastPeopleNaming = true;
|
||||
metadataSettings.Overrides = [MetadataSettingField.People];
|
||||
metadataSettings.PersonRoles = [PersonRole.Writer];
|
||||
Context.MetadataSettings.Update(metadataSettings);
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
await _externalMetadataService.WriteExternalMetadataToSeries(new ExternalSeriesDetailDto()
|
||||
{
|
||||
Name = seriesName,
|
||||
Staff = [CreateStaff("John", "Doe Doe", "Story"), CreateStaff("Doe", "John Doe", "Story")]
|
||||
}, 1);
|
||||
|
||||
var postSeries = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Metadata);
|
||||
Assert.NotNull(postSeries);
|
||||
|
||||
var allWriters = postSeries.Metadata.People.Where(p => p.Role == PersonRole.Writer).ToList();
|
||||
Assert.Equal(2, allWriters.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region People - Characters
|
||||
|
||||
[Fact]
|
||||
|
|
286
API.Tests/Services/PersonServiceTests.cs
Normal file
286
API.Tests/Services/PersonServiceTests.cs
Normal file
|
@ -0,0 +1,286 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data.Repositories;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Person;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace API.Tests.Services;
|
||||
|
||||
public class PersonServiceTests: AbstractDbTest
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public async Task PersonMerge_KeepNonEmptyMetadata()
|
||||
{
|
||||
var ps = new PersonService(UnitOfWork);
|
||||
|
||||
var person1 = new Person
|
||||
{
|
||||
Name = "Casey Delores",
|
||||
NormalizedName = "Casey Delores".ToNormalized(),
|
||||
HardcoverId = "ANonEmptyId",
|
||||
MalId = 12,
|
||||
};
|
||||
|
||||
var person2 = new Person
|
||||
{
|
||||
Name= "Delores Casey",
|
||||
NormalizedName = "Delores Casey".ToNormalized(),
|
||||
Description = "Hi, I'm Delores Casey!",
|
||||
Aliases = [new PersonAliasBuilder("Casey, Delores").Build()],
|
||||
AniListId = 27,
|
||||
};
|
||||
|
||||
UnitOfWork.PersonRepository.Attach(person1);
|
||||
UnitOfWork.PersonRepository.Attach(person2);
|
||||
await UnitOfWork.CommitAsync();
|
||||
|
||||
await ps.MergePeopleAsync(person2, person1);
|
||||
|
||||
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
|
||||
Assert.Single(allPeople);
|
||||
|
||||
var person = allPeople[0];
|
||||
Assert.Equal("Casey Delores", person.Name);
|
||||
Assert.NotEmpty(person.Description);
|
||||
Assert.Equal(27, person.AniListId);
|
||||
Assert.NotNull(person.HardcoverId);
|
||||
Assert.NotEmpty(person.HardcoverId);
|
||||
Assert.Contains(person.Aliases, pa => pa.Alias == "Delores Casey");
|
||||
Assert.Contains(person.Aliases, pa => pa.Alias == "Casey, Delores");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PersonMerge_MergedPersonDestruction()
|
||||
{
|
||||
var ps = new PersonService(UnitOfWork);
|
||||
|
||||
var person1 = new Person
|
||||
{
|
||||
Name = "Casey Delores",
|
||||
NormalizedName = "Casey Delores".ToNormalized(),
|
||||
};
|
||||
|
||||
var person2 = new Person
|
||||
{
|
||||
Name = "Delores Casey",
|
||||
NormalizedName = "Delores Casey".ToNormalized(),
|
||||
};
|
||||
|
||||
UnitOfWork.PersonRepository.Attach(person1);
|
||||
UnitOfWork.PersonRepository.Attach(person2);
|
||||
await UnitOfWork.CommitAsync();
|
||||
|
||||
await ps.MergePeopleAsync(person2, person1);
|
||||
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
|
||||
Assert.Single(allPeople);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PersonMerge_RetentionChapters()
|
||||
{
|
||||
var ps = new PersonService(UnitOfWork);
|
||||
|
||||
var library = new LibraryBuilder("My Library").Build();
|
||||
UnitOfWork.LibraryRepository.Add(library);
|
||||
await UnitOfWork.CommitAsync();
|
||||
|
||||
var user = new AppUserBuilder("Amelia", "amelia@localhost")
|
||||
.WithLibrary(library).Build();
|
||||
UnitOfWork.UserRepository.Add(user);
|
||||
|
||||
var person = new PersonBuilder("Jillian Cowan").Build();
|
||||
|
||||
var person2 = new PersonBuilder("Cowan Jillian").Build();
|
||||
|
||||
var chapter = new ChapterBuilder("1")
|
||||
.WithPerson(person, PersonRole.Editor)
|
||||
.Build();
|
||||
|
||||
var chapter2 = new ChapterBuilder("2")
|
||||
.WithPerson(person2, PersonRole.Editor)
|
||||
.Build();
|
||||
|
||||
var series = new SeriesBuilder("Test 1")
|
||||
.WithLibraryId(library.Id)
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithChapter(chapter)
|
||||
.Build())
|
||||
.Build();
|
||||
|
||||
var series2 = new SeriesBuilder("Test 2")
|
||||
.WithLibraryId(library.Id)
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithChapter(chapter2)
|
||||
.Build())
|
||||
.Build();
|
||||
|
||||
UnitOfWork.SeriesRepository.Add(series);
|
||||
UnitOfWork.SeriesRepository.Add(series2);
|
||||
await UnitOfWork.CommitAsync();
|
||||
|
||||
await ps.MergePeopleAsync(person2, person);
|
||||
|
||||
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
|
||||
Assert.Single(allPeople);
|
||||
var mergedPerson = allPeople[0];
|
||||
|
||||
Assert.Equal("Jillian Cowan", mergedPerson.Name);
|
||||
|
||||
var chapters = await UnitOfWork.PersonRepository.GetChaptersForPersonByRole(1, 1, PersonRole.Editor);
|
||||
Assert.Equal(2, chapters.Count());
|
||||
|
||||
chapter = await UnitOfWork.ChapterRepository.GetChapterAsync(1, ChapterIncludes.People);
|
||||
Assert.NotNull(chapter);
|
||||
Assert.Single(chapter.People);
|
||||
|
||||
chapter2 = await UnitOfWork.ChapterRepository.GetChapterAsync(2, ChapterIncludes.People);
|
||||
Assert.NotNull(chapter2);
|
||||
Assert.Single(chapter2.People);
|
||||
|
||||
Assert.Equal(chapter.People.First().PersonId, chapter2.People.First().PersonId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PersonMerge_NoDuplicateChaptersOrSeries()
|
||||
{
|
||||
await ResetDb();
|
||||
|
||||
var ps = new PersonService(UnitOfWork);
|
||||
|
||||
var library = new LibraryBuilder("My Library").Build();
|
||||
UnitOfWork.LibraryRepository.Add(library);
|
||||
await UnitOfWork.CommitAsync();
|
||||
|
||||
var user = new AppUserBuilder("Amelia", "amelia@localhost")
|
||||
.WithLibrary(library).Build();
|
||||
UnitOfWork.UserRepository.Add(user);
|
||||
|
||||
var person = new PersonBuilder("Jillian Cowan").Build();
|
||||
|
||||
var person2 = new PersonBuilder("Cowan Jillian").Build();
|
||||
|
||||
var chapter = new ChapterBuilder("1")
|
||||
.WithPerson(person, PersonRole.Editor)
|
||||
.WithPerson(person2, PersonRole.Colorist)
|
||||
.Build();
|
||||
|
||||
var chapter2 = new ChapterBuilder("2")
|
||||
.WithPerson(person2, PersonRole.Editor)
|
||||
.WithPerson(person, PersonRole.Editor)
|
||||
.Build();
|
||||
|
||||
var series = new SeriesBuilder("Test 1")
|
||||
.WithLibraryId(library.Id)
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithChapter(chapter)
|
||||
.Build())
|
||||
.WithMetadata(new SeriesMetadataBuilder()
|
||||
.WithPerson(person, PersonRole.Editor)
|
||||
.WithPerson(person2, PersonRole.Editor)
|
||||
.Build())
|
||||
.Build();
|
||||
|
||||
var series2 = new SeriesBuilder("Test 2")
|
||||
.WithLibraryId(library.Id)
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithChapter(chapter2)
|
||||
.Build())
|
||||
.WithMetadata(new SeriesMetadataBuilder()
|
||||
.WithPerson(person, PersonRole.Editor)
|
||||
.WithPerson(person2, PersonRole.Colorist)
|
||||
.Build())
|
||||
.Build();
|
||||
|
||||
UnitOfWork.SeriesRepository.Add(series);
|
||||
UnitOfWork.SeriesRepository.Add(series2);
|
||||
await UnitOfWork.CommitAsync();
|
||||
|
||||
await ps.MergePeopleAsync(person2, person);
|
||||
var allPeople = await UnitOfWork.PersonRepository.GetAllPeople();
|
||||
Assert.Single(allPeople);
|
||||
|
||||
var mergedPerson = await UnitOfWork.PersonRepository.GetPersonById(person.Id, PersonIncludes.All);
|
||||
Assert.NotNull(mergedPerson);
|
||||
Assert.Equal(3, mergedPerson.ChapterPeople.Count);
|
||||
Assert.Equal(3, mergedPerson.SeriesMetadataPeople.Count);
|
||||
|
||||
chapter = await UnitOfWork.ChapterRepository.GetChapterAsync(chapter.Id, ChapterIncludes.People);
|
||||
Assert.NotNull(chapter);
|
||||
Assert.Equal(2, chapter.People.Count);
|
||||
Assert.Single(chapter.People.Select(p => p.Person.Id).Distinct());
|
||||
Assert.Contains(chapter.People, p => p.Role == PersonRole.Editor);
|
||||
Assert.Contains(chapter.People, p => p.Role == PersonRole.Colorist);
|
||||
|
||||
chapter2 = await UnitOfWork.ChapterRepository.GetChapterAsync(chapter2.Id, ChapterIncludes.People);
|
||||
Assert.NotNull(chapter2);
|
||||
Assert.Single(chapter2.People);
|
||||
Assert.Contains(chapter2.People, p => p.Role == PersonRole.Editor);
|
||||
Assert.DoesNotContain(chapter2.People, p => p.Role == PersonRole.Colorist);
|
||||
|
||||
series = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(series.Id, SeriesIncludes.Metadata);
|
||||
Assert.NotNull(series);
|
||||
Assert.Single(series.Metadata.People);
|
||||
Assert.Contains(series.Metadata.People, p => p.Role == PersonRole.Editor);
|
||||
Assert.DoesNotContain(series.Metadata.People, p => p.Role == PersonRole.Colorist);
|
||||
|
||||
series2 = await UnitOfWork.SeriesRepository.GetSeriesByIdAsync(series2.Id, SeriesIncludes.Metadata);
|
||||
Assert.NotNull(series2);
|
||||
Assert.Equal(2, series2.Metadata.People.Count);
|
||||
Assert.Contains(series2.Metadata.People, p => p.Role == PersonRole.Editor);
|
||||
Assert.Contains(series2.Metadata.People, p => p.Role == PersonRole.Colorist);
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PersonAddAlias_NoOverlap()
|
||||
{
|
||||
await ResetDb();
|
||||
|
||||
UnitOfWork.PersonRepository.Attach(new PersonBuilder("Jillian Cowan").Build());
|
||||
UnitOfWork.PersonRepository.Attach(new PersonBuilder("Jilly Cowan").WithAlias("Jolly Cowan").Build());
|
||||
await UnitOfWork.CommitAsync();
|
||||
|
||||
var ps = new PersonService(UnitOfWork);
|
||||
|
||||
var person1 = await UnitOfWork.PersonRepository.GetPersonByNameOrAliasAsync("Jillian Cowan");
|
||||
var person2 = await UnitOfWork.PersonRepository.GetPersonByNameOrAliasAsync("Jilly Cowan");
|
||||
Assert.NotNull(person1);
|
||||
Assert.NotNull(person2);
|
||||
|
||||
// Overlap on Name
|
||||
var success = await ps.UpdatePersonAliasesAsync(person1, ["Jilly Cowan"]);
|
||||
Assert.False(success);
|
||||
|
||||
// Overlap on alias
|
||||
success = await ps.UpdatePersonAliasesAsync(person1, ["Jolly Cowan"]);
|
||||
Assert.False(success);
|
||||
|
||||
// No overlap
|
||||
success = await ps.UpdatePersonAliasesAsync(person2, ["Jilly Joy Cowan"]);
|
||||
Assert.True(success);
|
||||
|
||||
// Some overlap
|
||||
success = await ps.UpdatePersonAliasesAsync(person1, ["Jolly Cowan", "Jilly Joy Cowan"]);
|
||||
Assert.False(success);
|
||||
|
||||
// Some overlap
|
||||
success = await ps.UpdatePersonAliasesAsync(person1, ["Jolly Cowan", "Jilly Joy Cowan"]);
|
||||
Assert.False(success);
|
||||
|
||||
Assert.Single(person2.Aliases);
|
||||
}
|
||||
|
||||
protected override async Task ResetDb()
|
||||
{
|
||||
Context.Person.RemoveRange(Context.Person.ToList());
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using API.Data;
|
|||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Metadata;
|
||||
using API.DTOs.Person;
|
||||
using API.DTOs.SeriesDetail;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue