Add category ladders
This commit is contained in:
parent
90eccbf2f6
commit
ea6adeb58f
@ -141,7 +141,7 @@ namespace ApplicationServices
|
|||||||
PictureId = a.Book.PictureId,
|
PictureId = a.Book.PictureId,
|
||||||
IsAbridged = a.Book.IsAbridged,
|
IsAbridged = a.Book.IsAbridged,
|
||||||
DatePublished = a.Book.DatePublished,
|
DatePublished = a.Book.DatePublished,
|
||||||
CategoriesNames = a.Book.CategoriesNames().Any() ? a.Book.CategoriesNames().Aggregate((a, b) => $"{a}, {b}") : "",
|
CategoriesNames = a.Book.LowestCategoryNames().Any() ? a.Book.LowestCategoryNames().Aggregate((a, b) => $"{a}, {b}") : "",
|
||||||
MyRatingOverall = a.Book.UserDefinedItem.Rating.OverallRating,
|
MyRatingOverall = a.Book.UserDefinedItem.Rating.OverallRating,
|
||||||
MyRatingPerformance = a.Book.UserDefinedItem.Rating.PerformanceRating,
|
MyRatingPerformance = a.Book.UserDefinedItem.Rating.PerformanceRating,
|
||||||
MyRatingStory = a.Book.UserDefinedItem.Rating.StoryRating,
|
MyRatingStory = a.Book.UserDefinedItem.Rating.StoryRating,
|
||||||
|
|||||||
26
Source/DataLayer/Configurations/BookCategoryConfig.cs
Normal file
26
Source/DataLayer/Configurations/BookCategoryConfig.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DataLayer.Configurations
|
||||||
|
{
|
||||||
|
internal class BookCategoryConfig : IEntityTypeConfiguration<BookCategory>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<BookCategory> entity)
|
||||||
|
{
|
||||||
|
entity.HasKey(bc => new { bc.BookId, bc.CategoryLadderId });
|
||||||
|
|
||||||
|
entity.HasIndex(bc => bc.BookId);
|
||||||
|
entity.HasIndex(bc => bc.CategoryLadderId);
|
||||||
|
|
||||||
|
entity
|
||||||
|
.HasOne(bc => bc.Book)
|
||||||
|
.WithMany(b => b.CategoriesLink)
|
||||||
|
.HasForeignKey(bc => bc.BookId);
|
||||||
|
|
||||||
|
entity
|
||||||
|
.HasOne(bc => bc.CategoryLadder)
|
||||||
|
.WithMany(c => c.BooksLink)
|
||||||
|
.HasForeignKey(bc => bc.CategoryLadderId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -72,9 +72,10 @@ namespace DataLayer.Configurations
|
|||||||
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||||
|
|
||||||
entity
|
entity
|
||||||
.HasOne(b => b.Category)
|
.Metadata
|
||||||
.WithMany()
|
.FindNavigation(nameof(Book.CategoriesLink))
|
||||||
.HasForeignKey(b => b.CategoryId);
|
// PropertyAccessMode.Field : Categories is a get-only property, not a field, so use its backing field
|
||||||
|
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10,8 +10,11 @@ namespace DataLayer.Configurations
|
|||||||
entity.HasKey(c => c.CategoryId);
|
entity.HasKey(c => c.CategoryId);
|
||||||
entity.HasIndex(c => c.AudibleCategoryId);
|
entity.HasIndex(c => c.AudibleCategoryId);
|
||||||
|
|
||||||
// seeds go here. examples in Dinah.EntityFrameworkCore.Tests\DbContextFactoryExample.cs
|
entity.Ignore(c => c.CategoryLadders);
|
||||||
entity.HasData(Category.GetEmpty());
|
|
||||||
|
entity
|
||||||
|
.HasMany(e => e._categoryLadders)
|
||||||
|
.WithMany(e => e._categories);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
Source/DataLayer/Configurations/CategoryLadderConfig.cs
Normal file
24
Source/DataLayer/Configurations/CategoryLadderConfig.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DataLayer.Configurations
|
||||||
|
{
|
||||||
|
internal class CategoryLadderConfig : IEntityTypeConfiguration<CategoryLadder>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<CategoryLadder> entity)
|
||||||
|
{
|
||||||
|
entity.HasKey(cl => cl.CategoryLadderId);
|
||||||
|
|
||||||
|
entity.Ignore(cl => cl.Categories);
|
||||||
|
|
||||||
|
entity
|
||||||
|
.HasMany(cl => cl._categories)
|
||||||
|
.WithMany(c => c._categoryLadders);
|
||||||
|
|
||||||
|
entity
|
||||||
|
.Metadata
|
||||||
|
.FindNavigation(nameof(CategoryLadder.BooksLink))
|
||||||
|
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -55,10 +55,6 @@ namespace DataLayer
|
|||||||
public DateTime? DatePublished { get; private set; }
|
public DateTime? DatePublished { get; private set; }
|
||||||
public string Language { get; private set; }
|
public string Language { get; private set; }
|
||||||
|
|
||||||
// non-null. use "empty pattern"
|
|
||||||
internal int CategoryId { get; private set; }
|
|
||||||
public Category Category { get; private set; }
|
|
||||||
|
|
||||||
// is owned, not optional 1:1
|
// is owned, not optional 1:1
|
||||||
public UserDefinedItem UserDefinedItem { get; private set; }
|
public UserDefinedItem UserDefinedItem { get; private set; }
|
||||||
|
|
||||||
@ -79,7 +75,6 @@ namespace DataLayer
|
|||||||
ContentType contentType,
|
ContentType contentType,
|
||||||
IEnumerable<Contributor> authors,
|
IEnumerable<Contributor> authors,
|
||||||
IEnumerable<Contributor> narrators,
|
IEnumerable<Contributor> narrators,
|
||||||
Category category,
|
|
||||||
string localeName)
|
string localeName)
|
||||||
{
|
{
|
||||||
// validate
|
// validate
|
||||||
@ -96,11 +91,10 @@ namespace DataLayer
|
|||||||
// non-ef-ctor init.s
|
// non-ef-ctor init.s
|
||||||
UserDefinedItem = new UserDefinedItem(this);
|
UserDefinedItem = new UserDefinedItem(this);
|
||||||
_contributorsLink = new HashSet<BookContributor>();
|
_contributorsLink = new HashSet<BookContributor>();
|
||||||
|
_categoriesLink = new HashSet<BookCategory>();
|
||||||
_seriesLink = new HashSet<SeriesBook>();
|
_seriesLink = new HashSet<SeriesBook>();
|
||||||
_supplements = new HashSet<Supplement>();
|
_supplements = new HashSet<Supplement>();
|
||||||
|
|
||||||
Category = category;
|
|
||||||
|
|
||||||
// simple assigns
|
// simple assigns
|
||||||
UpdateTitle(title, subtitle);
|
UpdateTitle(title, subtitle);
|
||||||
Description = description?.Trim() ?? "";
|
Description = description?.Trim() ?? "";
|
||||||
@ -182,9 +176,30 @@ namespace DataLayer
|
|||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
#region categories
|
||||||
|
private HashSet<BookCategory> _categoriesLink;
|
||||||
|
public IEnumerable<BookCategory> CategoriesLink => _categoriesLink?.ToList();
|
||||||
|
public void UpsertCategories(CategoryLadder ladder)
|
||||||
|
{
|
||||||
|
ArgumentValidator.EnsureNotNull(ladder, nameof(ladder));
|
||||||
|
|
||||||
#region series
|
var singleBookCategory = _categoriesLink.SingleOrDefault(bc => bc.CategoryLadder.Equals(ladder));
|
||||||
private HashSet<SeriesBook> _seriesLink;
|
|
||||||
|
if (singleBookCategory is null)
|
||||||
|
_categoriesLink.Add(new BookCategory(this, ladder));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (var i = 0; i < ladder._categories.Count; i++)
|
||||||
|
{
|
||||||
|
//Update the category name
|
||||||
|
singleBookCategory.CategoryLadder._categories[i].Name = ladder._categories[i].Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region series
|
||||||
|
private HashSet<SeriesBook> _seriesLink;
|
||||||
public IEnumerable<SeriesBook> SeriesLink => _seriesLink?.ToList();
|
public IEnumerable<SeriesBook> SeriesLink => _seriesLink?.ToList();
|
||||||
|
|
||||||
public void UpsertSeries(Series series, string order, DbContext context = null)
|
public void UpsertSeries(Series series, string order, DbContext context = null)
|
||||||
@ -234,15 +249,6 @@ namespace DataLayer
|
|||||||
Language = language?.FirstCharToUpper() ?? Language;
|
Language = language?.FirstCharToUpper() ?? Language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateCategory(Category category, DbContext context = null)
|
|
||||||
{
|
|
||||||
// since category is never null, nullity means it hasn't been loaded
|
|
||||||
if (Category is null)
|
|
||||||
getEntry(context).Reference(s => s.Category).Load();
|
|
||||||
|
|
||||||
Category = category;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => $"[{AudibleProductId}] {TitleWithSubtitle}";
|
public override string ToString() => $"[{AudibleProductId}] {TitleWithSubtitle}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
Source/DataLayer/EfClasses/BookCategory.cs
Normal file
20
Source/DataLayer/EfClasses/BookCategory.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Dinah.Core;
|
||||||
|
|
||||||
|
namespace DataLayer
|
||||||
|
{
|
||||||
|
public class BookCategory
|
||||||
|
{
|
||||||
|
internal int BookId { get; private set; }
|
||||||
|
internal int CategoryLadderId { get; private set; }
|
||||||
|
|
||||||
|
public Book Book { get; private set; }
|
||||||
|
public CategoryLadder CategoryLadder { get; private set; }
|
||||||
|
private BookCategory() { }
|
||||||
|
|
||||||
|
internal BookCategory(Book book, CategoryLadder categoriesList)
|
||||||
|
{
|
||||||
|
Book = ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||||
|
CategoryLadder = ArgumentValidator.EnsureNotNull(categoriesList, nameof(categoriesList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,5 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace DataLayer
|
namespace DataLayer
|
||||||
{
|
{
|
||||||
@ -15,20 +12,20 @@ namespace DataLayer
|
|||||||
Id = id;
|
Id = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Category
|
public class Category
|
||||||
{
|
{
|
||||||
// Empty is a special case. use private ctor w/o validation
|
|
||||||
public static Category GetEmpty() => new() { CategoryId = -1, AudibleCategoryId = "", Name = "" };
|
|
||||||
|
|
||||||
internal int CategoryId { get; private set; }
|
internal int CategoryId { get; private set; }
|
||||||
public string AudibleCategoryId { get; private set; }
|
public string AudibleCategoryId { get; private set; }
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; internal set; }
|
||||||
public Category ParentCategory { get; private set; }
|
|
||||||
|
|
||||||
private Category() { }
|
internal List<CategoryLadder> _categoryLadders = new();
|
||||||
|
public IReadOnlyCollection<CategoryLadder> CategoryLadders => _categoryLadders.AsReadOnly();
|
||||||
|
|
||||||
|
private Category() { }
|
||||||
/// <summary>special id class b/c it's too easy to get string order mixed up</summary>
|
/// <summary>special id class b/c it's too easy to get string order mixed up</summary>
|
||||||
public Category(AudibleCategoryId audibleSeriesId, string name, Category parentCategory = null)
|
public Category(AudibleCategoryId audibleSeriesId, string name)
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNull(audibleSeriesId, nameof(audibleSeriesId));
|
ArgumentValidator.EnsureNotNull(audibleSeriesId, nameof(audibleSeriesId));
|
||||||
var id = audibleSeriesId.Id;
|
var id = audibleSeriesId.Id;
|
||||||
@ -37,15 +34,6 @@ namespace DataLayer
|
|||||||
|
|
||||||
AudibleCategoryId = id;
|
AudibleCategoryId = id;
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|
||||||
UpdateParentCategory(parentCategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateParentCategory(Category parentCategory)
|
|
||||||
{
|
|
||||||
// don't overwrite with null but not an error
|
|
||||||
if (parentCategory is not null)
|
|
||||||
ParentCategory = parentCategory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"[{AudibleCategoryId}] {Name}";
|
public override string ToString() => $"[{AudibleCategoryId}] {Name}";
|
||||||
|
|||||||
48
Source/DataLayer/EfClasses/CategoryLadder.cs
Normal file
48
Source/DataLayer/EfClasses/CategoryLadder.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using Dinah.Core;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace DataLayer
|
||||||
|
{
|
||||||
|
public class CategoryLadder : IEquatable<CategoryLadder>
|
||||||
|
{
|
||||||
|
internal int CategoryLadderId { get; private set; }
|
||||||
|
|
||||||
|
internal List<Category> _categories;
|
||||||
|
public ReadOnlyCollection<Category> Categories => _categories.AsReadOnly();
|
||||||
|
|
||||||
|
private HashSet<BookCategory> _booksLink;
|
||||||
|
public IEnumerable<BookCategory> BooksLink => _booksLink?.ToList();
|
||||||
|
private CategoryLadder() { _categories = new(); }
|
||||||
|
public CategoryLadder(List<Category> categories)
|
||||||
|
{
|
||||||
|
ArgumentValidator.EnsureNotNull(categories, nameof(categories));
|
||||||
|
ArgumentValidator.EnsureGreaterThan(categories.Count, nameof(categories), 0);
|
||||||
|
_booksLink = new HashSet<BookCategory>();
|
||||||
|
_categories = categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hashCode = default;
|
||||||
|
foreach (var category in _categories)
|
||||||
|
hashCode.Add(category.AudibleCategoryId);
|
||||||
|
return hashCode.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(CategoryLadder other)
|
||||||
|
{
|
||||||
|
if (other?._categories is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return Equals(other._categories.Select(c => c.AudibleCategoryId));
|
||||||
|
}
|
||||||
|
public bool Equals(IEnumerable<string> categoryIds)
|
||||||
|
=> _categories.Select(c => c.AudibleCategoryId).SequenceEqual(categoryIds);
|
||||||
|
public override bool Equals(object obj) => obj is CategoryLadder other && Equals(other);
|
||||||
|
|
||||||
|
public override string ToString() => string.Join(" > ", _categories.Select(c => c.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,14 +46,23 @@ namespace DataLayer
|
|||||||
? $"{sb.Series.Name} (#{sb.Order})"
|
? $"{sb.Series.Name} (#{sb.Order})"
|
||||||
: sb.Series.Name;
|
: sb.Series.Name;
|
||||||
}
|
}
|
||||||
public static string[] CategoriesNames(this Book book)
|
|
||||||
=> book.Category is null ? new string[0]
|
public static string[] LowestCategoryNames(this Book book)
|
||||||
: book.Category.ParentCategory is null ? new[] { book.Category.Name }
|
=> book.CategoriesLink?.Any() is not true ? Array.Empty<string>()
|
||||||
: new[] { book.Category.ParentCategory.Name, book.Category.Name };
|
: book
|
||||||
public static string[] CategoriesIds(this Book book)
|
.CategoriesLink
|
||||||
=> book.Category is null ? null
|
.Select(cl => cl.CategoryLadder.Categories.LastOrDefault()?.Name)
|
||||||
: book.Category.ParentCategory is null ? new[] { book.Category.AudibleCategoryId }
|
.Where(c => c is not null)
|
||||||
: new[] { book.Category.ParentCategory.AudibleCategoryId, book.Category.AudibleCategoryId };
|
.Distinct()
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
public static string[] CategoriesIds(this Book book)
|
||||||
|
=> book.CategoriesLink?.Any() is not true ? null
|
||||||
|
: book
|
||||||
|
.CategoriesLink
|
||||||
|
.SelectMany(cl => cl.CategoryLadder.Categories)
|
||||||
|
.Select(c => c.AudibleCategoryId)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
public static string AggregateTitles(this IEnumerable<LibraryBook> libraryBooks, int max = 5)
|
public static string AggregateTitles(this IEnumerable<LibraryBook> libraryBooks, int max = 5)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,6 +23,7 @@ namespace DataLayer
|
|||||||
public DbSet<Contributor> Contributors { get; private set; }
|
public DbSet<Contributor> Contributors { get; private set; }
|
||||||
public DbSet<Series> Series { get; private set; }
|
public DbSet<Series> Series { get; private set; }
|
||||||
public DbSet<Category> Categories { get; private set; }
|
public DbSet<Category> Categories { get; private set; }
|
||||||
|
public DbSet<CategoryLadder> CategoryLadders { get; private set; }
|
||||||
|
|
||||||
public static LibationContext Create(string connectionString)
|
public static LibationContext Create(string connectionString)
|
||||||
{
|
{
|
||||||
@ -39,13 +40,15 @@ namespace DataLayer
|
|||||||
{
|
{
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.ApplyConfiguration(new BookConfig());
|
modelBuilder.ApplyConfiguration(new BookConfig());
|
||||||
modelBuilder.ApplyConfiguration(new ContributorConfig());
|
modelBuilder.ApplyConfiguration(new ContributorConfig());
|
||||||
modelBuilder.ApplyConfiguration(new BookContributorConfig());
|
modelBuilder.ApplyConfiguration(new BookContributorConfig());
|
||||||
modelBuilder.ApplyConfiguration(new LibraryBookConfig());
|
modelBuilder.ApplyConfiguration(new LibraryBookConfig());
|
||||||
modelBuilder.ApplyConfiguration(new SeriesConfig());
|
modelBuilder.ApplyConfiguration(new SeriesConfig());
|
||||||
modelBuilder.ApplyConfiguration(new SeriesBookConfig());
|
modelBuilder.ApplyConfiguration(new SeriesBookConfig());
|
||||||
modelBuilder.ApplyConfiguration(new CategoryConfig());
|
modelBuilder.ApplyConfiguration(new CategoryConfig());
|
||||||
|
modelBuilder.ApplyConfiguration(new CategoryLadderConfig());
|
||||||
|
modelBuilder.ApplyConfiguration(new BookCategoryConfig());
|
||||||
|
|
||||||
// views are now supported via "keyless entity types" (instead of "entity types" or the prev "query types"):
|
// views are now supported via "keyless entity types" (instead of "entity types" or the prev "query types"):
|
||||||
// https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types
|
// https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types
|
||||||
|
|||||||
479
Source/DataLayer/Migrations/20230717220642_AddCategoriesList.Designer.cs
generated
Normal file
479
Source/DataLayer/Migrations/20230717220642_AddCategoriesList.Designer.cs
generated
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using DataLayer;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DataLayer.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(LibationContext))]
|
||||||
|
[Migration("20230717220642_AddCategoriesList")]
|
||||||
|
partial class AddCategoriesList
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
|
||||||
|
|
||||||
|
modelBuilder.Entity("CategoryCategoryLadder", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("_categoriesCategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("_categoryLaddersCategoryLadderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("_categoriesCategoryId", "_categoryLaddersCategoryLadderId");
|
||||||
|
|
||||||
|
b.HasIndex("_categoryLaddersCategoryLadderId");
|
||||||
|
|
||||||
|
b.ToTable("CategoryCategoryLadder");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleProductId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ContentType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DatePublished")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAbridged")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Language")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("LengthInMinutes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Locale")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PictureId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PictureLarge")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Subtitle")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<long>("_audioFormat")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("AudibleProductId");
|
||||||
|
|
||||||
|
b.ToTable("Books");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryLadderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("BookId", "CategoryLadderId");
|
||||||
|
|
||||||
|
b.HasIndex("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryLadderId");
|
||||||
|
|
||||||
|
b.ToTable("BookCategory");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ContributorId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Role")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<byte>("Order")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("BookId", "ContributorId", "Role");
|
||||||
|
|
||||||
|
b.HasIndex("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("ContributorId");
|
||||||
|
|
||||||
|
b.ToTable("BookContributor");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Category", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleCategoryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("CategoryId");
|
||||||
|
|
||||||
|
b.HasIndex("AudibleCategoryId");
|
||||||
|
|
||||||
|
b.ToTable("Categories");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
CategoryId = -1,
|
||||||
|
AudibleCategoryId = "",
|
||||||
|
Name = ""
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.CategoryLadder", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("CategoryLadderId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("CategoryLadderId");
|
||||||
|
|
||||||
|
b.ToTable("CategoryLadders");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
CategoryLadderId = -1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ContributorId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleContributorId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("ContributorId");
|
||||||
|
|
||||||
|
b.HasIndex("Name");
|
||||||
|
|
||||||
|
b.ToTable("Contributors");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
ContributorId = -1,
|
||||||
|
Name = ""
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("AbsentFromLastScan")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Account")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateAdded")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("BookId");
|
||||||
|
|
||||||
|
b.ToTable("LibraryBooks");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Series", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("SeriesId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleSeriesId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("SeriesId");
|
||||||
|
|
||||||
|
b.HasIndex("AudibleSeriesId");
|
||||||
|
|
||||||
|
b.ToTable("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("SeriesId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Order")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("SeriesId", "BookId");
|
||||||
|
|
||||||
|
b.HasIndex("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("SeriesId");
|
||||||
|
|
||||||
|
b.ToTable("SeriesBook");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CategoryCategoryLadder", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Category", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("_categoriesCategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.CategoryLadder", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("_categoryLaddersCategoryLadderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
|
{
|
||||||
|
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<float>("OverallRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b1.Property<float>("PerformanceRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b1.Property<float>("StoryRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b1.HasKey("BookId");
|
||||||
|
|
||||||
|
b1.ToTable("Books");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("BookId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("SupplementId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("Url")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("SupplementId");
|
||||||
|
|
||||||
|
b1.HasIndex("BookId");
|
||||||
|
|
||||||
|
b1.ToTable("Supplement");
|
||||||
|
|
||||||
|
b1.WithOwner("Book")
|
||||||
|
.HasForeignKey("BookId");
|
||||||
|
|
||||||
|
b1.Navigation("Book");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<int>("BookStatus")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<DateTime?>("LastDownloaded")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("LastDownloadedVersion")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<int?>("PdfStatus")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("Tags")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("BookId");
|
||||||
|
|
||||||
|
b1.ToTable("UserDefinedItem", (string)null);
|
||||||
|
|
||||||
|
b1.WithOwner("Book")
|
||||||
|
.HasForeignKey("BookId");
|
||||||
|
|
||||||
|
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||||
|
{
|
||||||
|
b2.Property<int>("UserDefinedItemBookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b2.Property<float>("OverallRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b2.Property<float>("PerformanceRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b2.Property<float>("StoryRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b2.HasKey("UserDefinedItemBookId");
|
||||||
|
|
||||||
|
b2.ToTable("UserDefinedItem");
|
||||||
|
|
||||||
|
b2.WithOwner()
|
||||||
|
.HasForeignKey("UserDefinedItemBookId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b1.Navigation("Book");
|
||||||
|
|
||||||
|
b1.Navigation("Rating");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("Rating");
|
||||||
|
|
||||||
|
b.Navigation("Supplements");
|
||||||
|
|
||||||
|
b.Navigation("UserDefinedItem");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookCategory", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithMany("CategoriesLink")
|
||||||
|
.HasForeignKey("BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.CategoryLadder", "CategoryLadder")
|
||||||
|
.WithMany("BooksLink")
|
||||||
|
.HasForeignKey("CategoryLadderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Book");
|
||||||
|
|
||||||
|
b.Navigation("CategoryLadder");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithMany("ContributorsLink")
|
||||||
|
.HasForeignKey("BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||||
|
.WithMany("BooksLink")
|
||||||
|
.HasForeignKey("ContributorId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Book");
|
||||||
|
|
||||||
|
b.Navigation("Contributor");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Book");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithMany("SeriesLink")
|
||||||
|
.HasForeignKey("BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.Series", "Series")
|
||||||
|
.WithMany("BooksLink")
|
||||||
|
.HasForeignKey("SeriesId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Book");
|
||||||
|
|
||||||
|
b.Navigation("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CategoriesLink");
|
||||||
|
|
||||||
|
b.Navigation("ContributorsLink");
|
||||||
|
|
||||||
|
b.Navigation("SeriesLink");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.CategoryLadder", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("BooksLink");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("BooksLink");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Series", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("BooksLink");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
Source/DataLayer/Migrations/20230717220642_AddCategoriesList.cs
Normal file
176
Source/DataLayer/Migrations/20230717220642_AddCategoriesList.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DataLayer.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddCategoriesList : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Books_Categories_CategoryId",
|
||||||
|
table: "Books");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Categories_Categories_ParentCategoryCategoryId",
|
||||||
|
table: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Categories_ParentCategoryCategoryId",
|
||||||
|
table: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Books_CategoryId",
|
||||||
|
table: "Books");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ParentCategoryCategoryId",
|
||||||
|
table: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Books");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "CategoryLadders",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
CategoryLadderId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_CategoryLadders", x => x.CategoryLadderId);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "BookCategory",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
BookId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
CategoryLadderId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_BookCategory", x => new { x.BookId, x.CategoryLadderId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_BookCategory_Books_BookId",
|
||||||
|
column: x => x.BookId,
|
||||||
|
principalTable: "Books",
|
||||||
|
principalColumn: "BookId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_BookCategory_CategoryLadders_CategoryLadderId",
|
||||||
|
column: x => x.CategoryLadderId,
|
||||||
|
principalTable: "CategoryLadders",
|
||||||
|
principalColumn: "CategoryLadderId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "CategoryCategoryLadder",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
_categoriesCategoryId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
_categoryLaddersCategoryLadderId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_CategoryCategoryLadder", x => new { x._categoriesCategoryId, x._categoryLaddersCategoryLadderId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_CategoryCategoryLadder_Categories__categoriesCategoryId",
|
||||||
|
column: x => x._categoriesCategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "CategoryId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_CategoryCategoryLadder_CategoryLadders__categoryLaddersCategoryLadderId",
|
||||||
|
column: x => x._categoryLaddersCategoryLadderId,
|
||||||
|
principalTable: "CategoryLadders",
|
||||||
|
principalColumn: "CategoryLadderId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "CategoryLadders",
|
||||||
|
column: "CategoryLadderId",
|
||||||
|
value: -1);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_BookCategory_BookId",
|
||||||
|
table: "BookCategory",
|
||||||
|
column: "BookId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_BookCategory_CategoryLadderId",
|
||||||
|
table: "BookCategory",
|
||||||
|
column: "CategoryLadderId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_CategoryCategoryLadder__categoryLaddersCategoryLadderId",
|
||||||
|
table: "CategoryCategoryLadder",
|
||||||
|
column: "_categoryLaddersCategoryLadderId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "BookCategory");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "CategoryCategoryLadder");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "CategoryLadders");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ParentCategoryCategoryId",
|
||||||
|
table: "Categories",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Books",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "CategoryId",
|
||||||
|
keyValue: -1,
|
||||||
|
column: "ParentCategoryCategoryId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Categories_ParentCategoryCategoryId",
|
||||||
|
table: "Categories",
|
||||||
|
column: "ParentCategoryCategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Books_CategoryId",
|
||||||
|
table: "Books",
|
||||||
|
column: "CategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Books_Categories_CategoryId",
|
||||||
|
table: "Books",
|
||||||
|
column: "CategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "CategoryId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Categories_Categories_ParentCategoryCategoryId",
|
||||||
|
table: "Categories",
|
||||||
|
column: "ParentCategoryCategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "CategoryId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,21 @@ namespace DataLayer.Migrations
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
|
||||||
|
|
||||||
|
modelBuilder.Entity("CategoryCategoryLadder", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("_categoriesCategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("_categoryLaddersCategoryLadderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("_categoriesCategoryId", "_categoryLaddersCategoryLadderId");
|
||||||
|
|
||||||
|
b.HasIndex("_categoryLaddersCategoryLadderId");
|
||||||
|
|
||||||
|
b.ToTable("CategoryCategoryLadder");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.Book", b =>
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("BookId")
|
b.Property<int>("BookId")
|
||||||
@ -26,9 +41,6 @@ namespace DataLayer.Migrations
|
|||||||
b.Property<string>("AudibleProductId")
|
b.Property<string>("AudibleProductId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("CategoryId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<int>("ContentType")
|
b.Property<int>("ContentType")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@ -69,11 +81,26 @@ namespace DataLayer.Migrations
|
|||||||
|
|
||||||
b.HasIndex("AudibleProductId");
|
b.HasIndex("AudibleProductId");
|
||||||
|
|
||||||
b.HasIndex("CategoryId");
|
|
||||||
|
|
||||||
b.ToTable("Books");
|
b.ToTable("Books");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryLadderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("BookId", "CategoryLadderId");
|
||||||
|
|
||||||
|
b.HasIndex("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryLadderId");
|
||||||
|
|
||||||
|
b.ToTable("BookCategory");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("BookId")
|
b.Property<int>("BookId")
|
||||||
@ -109,15 +136,10 @@ namespace DataLayer.Migrations
|
|||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("ParentCategoryCategoryId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.HasKey("CategoryId");
|
b.HasKey("CategoryId");
|
||||||
|
|
||||||
b.HasIndex("AudibleCategoryId");
|
b.HasIndex("AudibleCategoryId");
|
||||||
|
|
||||||
b.HasIndex("ParentCategoryCategoryId");
|
|
||||||
|
|
||||||
b.ToTable("Categories");
|
b.ToTable("Categories");
|
||||||
|
|
||||||
b.HasData(
|
b.HasData(
|
||||||
@ -129,6 +151,23 @@ namespace DataLayer.Migrations
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.CategoryLadder", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("CategoryLadderId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("CategoryLadderId");
|
||||||
|
|
||||||
|
b.ToTable("CategoryLadders");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
CategoryLadderId = -1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("ContributorId")
|
b.Property<int>("ContributorId")
|
||||||
@ -216,14 +255,23 @@ namespace DataLayer.Migrations
|
|||||||
b.ToTable("SeriesBook");
|
b.ToTable("SeriesBook");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.Book", b =>
|
modelBuilder.Entity("CategoryCategoryLadder", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DataLayer.Category", "Category")
|
b.HasOne("DataLayer.Category", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("CategoryId")
|
.HasForeignKey("_categoriesCategoryId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.CategoryLadder", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("_categoryLaddersCategoryLadderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
|
{
|
||||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<int>("BookId")
|
b1.Property<int>("BookId")
|
||||||
@ -324,8 +372,6 @@ namespace DataLayer.Migrations
|
|||||||
b1.Navigation("Rating");
|
b1.Navigation("Rating");
|
||||||
});
|
});
|
||||||
|
|
||||||
b.Navigation("Category");
|
|
||||||
|
|
||||||
b.Navigation("Rating");
|
b.Navigation("Rating");
|
||||||
|
|
||||||
b.Navigation("Supplements");
|
b.Navigation("Supplements");
|
||||||
@ -333,6 +379,25 @@ namespace DataLayer.Migrations
|
|||||||
b.Navigation("UserDefinedItem");
|
b.Navigation("UserDefinedItem");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookCategory", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithMany("CategoriesLink")
|
||||||
|
.HasForeignKey("BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.CategoryLadder", "CategoryLadder")
|
||||||
|
.WithMany("BooksLink")
|
||||||
|
.HasForeignKey("CategoryLadderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Book");
|
||||||
|
|
||||||
|
b.Navigation("CategoryLadder");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DataLayer.Book", "Book")
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
@ -352,15 +417,6 @@ namespace DataLayer.Migrations
|
|||||||
b.Navigation("Contributor");
|
b.Navigation("Contributor");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.Category", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("ParentCategoryCategoryId");
|
|
||||||
|
|
||||||
b.Navigation("ParentCategory");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DataLayer.Book", "Book")
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
@ -393,11 +449,18 @@ namespace DataLayer.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("DataLayer.Book", b =>
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("CategoriesLink");
|
||||||
|
|
||||||
b.Navigation("ContributorsLink");
|
b.Navigation("ContributorsLink");
|
||||||
|
|
||||||
b.Navigation("SeriesLink");
|
b.Navigation("SeriesLink");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.CategoryLadder", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("BooksLink");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("BooksLink");
|
b.Navigation("BooksLink");
|
||||||
|
|||||||
@ -34,7 +34,7 @@ namespace DataLayer
|
|||||||
// owned items are always loaded. eg: book.UserDefinedItem, book.Supplements
|
// owned items are always loaded. eg: book.UserDefinedItem, book.Supplements
|
||||||
.Include(b => b.SeriesLink).ThenInclude(sb => sb.Series)
|
.Include(b => b.SeriesLink).ThenInclude(sb => sb.Series)
|
||||||
.Include(b => b.ContributorsLink).ThenInclude(c => c.Contributor)
|
.Include(b => b.ContributorsLink).ThenInclude(c => c.Contributor)
|
||||||
.Include(b => b.Category).ThenInclude(c => c.ParentCategory);
|
.Include(b => b.CategoriesLink).ThenInclude(c => c.CategoryLadder).ThenInclude(c => c._categories);
|
||||||
|
|
||||||
public static bool IsProduct(this Book book)
|
public static bool IsProduct(this Book book)
|
||||||
=> book.ContentType is not ContentType.Episode and not ContentType.Parent;
|
=> book.ContentType is not ContentType.Episode and not ContentType.Parent;
|
||||||
|
|||||||
@ -55,7 +55,7 @@ namespace DataLayer
|
|||||||
// owned items are always loaded. eg: book.UserDefinedItem, book.Supplements
|
// owned items are always loaded. eg: book.UserDefinedItem, book.Supplements
|
||||||
.Include(le => le.Book).ThenInclude(b => b.SeriesLink).ThenInclude(sb => sb.Series)
|
.Include(le => le.Book).ThenInclude(b => b.SeriesLink).ThenInclude(sb => sb.Series)
|
||||||
.Include(le => le.Book).ThenInclude(b => b.ContributorsLink).ThenInclude(c => c.Contributor)
|
.Include(le => le.Book).ThenInclude(b => b.ContributorsLink).ThenInclude(c => c.Contributor)
|
||||||
.Include(le => le.Book).ThenInclude(b => b.Category).ThenInclude(c => c.ParentCategory);
|
.Include(le => le.Book).ThenInclude(b => b.CategoriesLink).ThenInclude(c => c.CategoryLadder).ThenInclude(c => c._categories);
|
||||||
|
|
||||||
public static IEnumerable<LibraryBook> ParentedEpisodes(this IEnumerable<LibraryBook> libraryBooks)
|
public static IEnumerable<LibraryBook> ParentedEpisodes(this IEnumerable<LibraryBook> libraryBooks)
|
||||||
=> libraryBooks.Where(lb => lb.Book.IsEpisodeParent()).SelectMany(libraryBooks.FindChildren);
|
=> libraryBooks.Where(lb => lb.Book.IsEpisodeParent()).SelectMany(libraryBooks.FindChildren);
|
||||||
|
|||||||
@ -99,20 +99,6 @@ namespace DtoImporterService
|
|||||||
.Select(n => contributorImporter.Cache[n.Name])
|
.Select(n => contributorImporter.Cache[n.Name])
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// categories are laid out for a breadcrumb. category is 1st, subcategory is 2nd
|
|
||||||
// absence of categories is also possible
|
|
||||||
|
|
||||||
// CATEGORY HACK: only use the 1st 2 categories
|
|
||||||
// after we support full arbitrary-depth category trees and multiple categories per book, the real impl will be something like this
|
|
||||||
// var lastCategory = item.Categories.LastOrDefault()?.CategoryId ?? "";
|
|
||||||
var lastCategory
|
|
||||||
= item.Categories.Length == 0 ? ""
|
|
||||||
: item.Categories.Length == 1 ? item.Categories[0].CategoryId
|
|
||||||
// 2+
|
|
||||||
: item.Categories[1].CategoryId;
|
|
||||||
|
|
||||||
var category = categoryImporter.Cache[lastCategory];
|
|
||||||
|
|
||||||
Book book;
|
Book book;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -125,7 +111,6 @@ namespace DtoImporterService
|
|||||||
contentType,
|
contentType,
|
||||||
authors,
|
authors,
|
||||||
narrators,
|
narrators,
|
||||||
category,
|
|
||||||
importItem.LocaleName)
|
importItem.LocaleName)
|
||||||
).Entity;
|
).Entity;
|
||||||
Cache.Add(book.AudibleProductId, book);
|
Cache.Add(book.AudibleProductId, book);
|
||||||
@ -140,7 +125,6 @@ namespace DtoImporterService
|
|||||||
contentType,
|
contentType,
|
||||||
QtyAuthors = authors?.Count,
|
QtyAuthors = authors?.Count,
|
||||||
QtyNarrators = narrators?.Count,
|
QtyNarrators = narrators?.Count,
|
||||||
Category = category?.Name,
|
|
||||||
importItem.LocaleName
|
importItem.LocaleName
|
||||||
});
|
});
|
||||||
throw;
|
throw;
|
||||||
@ -201,6 +185,17 @@ namespace DtoImporterService
|
|||||||
book.UpsertSeries(series, seriesEntry.Sequence);
|
book.UpsertSeries(series, seriesEntry.Sequence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.CategoryLadders is not null)
|
||||||
|
{
|
||||||
|
foreach (var ladder in item.CategoryLadders.Select(cl => cl.Ladder).Where(l => l?.Length > 0))
|
||||||
|
{
|
||||||
|
var categoryIds = ladder.Select(l => l.CategoryId).ToList();
|
||||||
|
var cata = categoryImporter.LadderCache.Single(c => c.Equals(categoryIds));
|
||||||
|
|
||||||
|
book.UpsertCategories(cata);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataLayer.ContentType GetContentType(Item item)
|
private static DataLayer.ContentType GetContentType(Item item)
|
||||||
|
|||||||
@ -12,7 +12,8 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
protected override IValidator Validator => new CategoryValidator();
|
protected override IValidator Validator => new CategoryValidator();
|
||||||
|
|
||||||
public Dictionary<string, Category> Cache { get; private set; } = new();
|
private Dictionary<string, Category> Cache { get; set; } = new();
|
||||||
|
public HashSet<DataLayer.CategoryLadder> LadderCache { get; private set; } = new();
|
||||||
|
|
||||||
public CategoryImporter(LibationContext context) : base(context) { }
|
public CategoryImporter(LibationContext context) : base(context) { }
|
||||||
|
|
||||||
@ -30,44 +31,39 @@ namespace DtoImporterService
|
|||||||
loadLocal_categories(categoryIds);
|
loadLocal_categories(categoryIds);
|
||||||
|
|
||||||
// upsert
|
// upsert
|
||||||
var categoryPairs = importItems
|
var categoryLadders = importItems
|
||||||
.Select(i => i.DtoItem)
|
.SelectMany(i => i.DtoItem.CategoryLadders)
|
||||||
.GetCategoryPairsDistinct()
|
.Select(cl => cl.Ladder)
|
||||||
|
.Where(l => l?.Length > 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
var qtyNew = upsertCategories(categoryPairs);
|
|
||||||
|
var qtyNew = upsertCategories(categoryLadders);
|
||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadLocal_categories(List<string> categoryIds)
|
private void loadLocal_categories(List<string> categoryIds)
|
||||||
{
|
{
|
||||||
// must include default/empty/missing
|
|
||||||
categoryIds.Add(Category.GetEmpty().AudibleCategoryId);
|
|
||||||
|
|
||||||
// load existing => local
|
// load existing => local
|
||||||
Cache = DbContext.Categories
|
Cache = DbContext.Categories
|
||||||
.Where(c => categoryIds.Contains(c.AudibleCategoryId))
|
.Where(c => categoryIds.Contains(c.AudibleCategoryId))
|
||||||
.ToDictionarySafe(c => c.AudibleCategoryId);
|
.ToDictionarySafe(c => c.AudibleCategoryId);
|
||||||
|
|
||||||
|
LadderCache = DbContext.CategoryLadders.ToHashSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
// only use after loading contributors => local
|
// only use after loading contributors => local
|
||||||
private int upsertCategories(List<Ladder[]> categoryPairs)
|
private int upsertCategories(List<Ladder[]> ladders)
|
||||||
{
|
{
|
||||||
var qtyNew = 0;
|
var qtyNew = 0;
|
||||||
|
|
||||||
foreach (var pair in categoryPairs)
|
foreach (var ladder in ladders)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < pair.Length; i++)
|
var categories = new List<Category>(ladder.Length);
|
||||||
|
|
||||||
|
for (var i = 0; i < ladder.Length; i++)
|
||||||
{
|
{
|
||||||
// CATEGORY HACK: not yet supported: depth beyond 0 and 1
|
var id = ladder[i].CategoryId;
|
||||||
if (i > 1)
|
var name = ladder[i].CategoryName;
|
||||||
break;
|
|
||||||
|
|
||||||
var id = pair[i].CategoryId;
|
|
||||||
var name = pair[i].CategoryName;
|
|
||||||
|
|
||||||
Category parentCategory = null;
|
|
||||||
if (i == 1)
|
|
||||||
Cache.TryGetValue(pair[0].CategoryId, out parentCategory);
|
|
||||||
|
|
||||||
if (!Cache.TryGetValue(id, out var category))
|
if (!Cache.TryGetValue(id, out var category))
|
||||||
{
|
{
|
||||||
@ -75,13 +71,37 @@ namespace DtoImporterService
|
|||||||
qtyNew++;
|
qtyNew++;
|
||||||
}
|
}
|
||||||
|
|
||||||
category.UpdateParentCategory(parentCategory);
|
categories.Add(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
var categoryLadder = new DataLayer.CategoryLadder(categories);
|
||||||
|
if (!LadderCache.Contains(categoryLadder))
|
||||||
|
{
|
||||||
|
addCategoryLadder(categoryLadder);
|
||||||
|
qtyNew++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DataLayer.CategoryLadder addCategoryLadder(DataLayer.CategoryLadder categoryList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var entityEntry = DbContext.CategoryLadders.Add(categoryList);
|
||||||
|
var entity = entityEntry.Entity;
|
||||||
|
|
||||||
|
LadderCache.Add(entity);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, "Error adding category ladder. {@DebugInfo}", categoryList);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Category addCategory(string id, string name)
|
private Category addCategory(string id, string name)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@ -140,7 +140,7 @@ namespace FileLiberator
|
|||||||
new AaxcDownloadSingleConverter(outFileName, cacheDir, dlOptions);
|
new AaxcDownloadSingleConverter(outFileName, cacheDir, dlOptions);
|
||||||
|
|
||||||
if (config.AllowLibationFixup)
|
if (config.AllowLibationFixup)
|
||||||
converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames());
|
converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.LowestCategoryNames());
|
||||||
|
|
||||||
abDownloader = converter;
|
abDownloader = converter;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,7 +115,7 @@ Author(s): {Book.AuthorNames()}
|
|||||||
Narrator(s): {Book.NarratorNames()}
|
Narrator(s): {Book.NarratorNames()}
|
||||||
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
||||||
Audio Bitrate: {Book.AudioFormat}
|
Audio Bitrate: {Book.AudioFormat}
|
||||||
Category: {string.Join(" > ", Book.CategoriesNames())}
|
Category: {string.Join(", ", Book.LowestCategoryNames())}
|
||||||
Purchase Date: {libraryBook.DateAdded:d}
|
Purchase Date: {libraryBook.DateAdded:d}
|
||||||
Language: {Book.Language}
|
Language: {Book.Language}
|
||||||
Audible ID: {Book.AudibleProductId}
|
Audible ID: {Book.AudibleProductId}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ namespace LibationSearchEngine
|
|||||||
{ FieldType.String, lb => lb.Book.Publisher, nameof(Book.Publisher) },
|
{ FieldType.String, lb => lb.Book.Publisher, nameof(Book.Publisher) },
|
||||||
{ FieldType.String, lb => lb.Book.SeriesNames(), "SeriesNames", "Narrator", "Series" },
|
{ FieldType.String, lb => lb.Book.SeriesNames(), "SeriesNames", "Narrator", "Series" },
|
||||||
{ FieldType.String, lb => string.Join(", ", lb.Book.SeriesLink.Select(s => s.Series.AudibleSeriesId)), "SeriesId" },
|
{ FieldType.String, lb => string.Join(", ", lb.Book.SeriesLink.Select(s => s.Series.AudibleSeriesId)), "SeriesId" },
|
||||||
{ FieldType.String, lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), nameof(Book.Category), "Categories", "CategoriesId", "CategoryId", "CategoriesNames" },
|
{ FieldType.String, lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), "Category", "Categories", "CategoriesId", "CategoryId", "CategoriesNames" },
|
||||||
{ FieldType.String, lb => lb.Book.UserDefinedItem.Tags, TAGS.FirstCharToUpper() },
|
{ FieldType.String, lb => lb.Book.UserDefinedItem.Tags, TAGS.FirstCharToUpper() },
|
||||||
{ FieldType.String, lb => lb.Book.Locale, "Locale", "Region" },
|
{ FieldType.String, lb => lb.Book.Locale, "Locale", "Region" },
|
||||||
{ FieldType.String, lb => lb.Account, "Account", "Email" },
|
{ FieldType.String, lb => lb.Account, "Account", "Email" },
|
||||||
|
|||||||
@ -116,7 +116,7 @@ namespace LibationUiBase.GridView
|
|||||||
ProductRating = Book.Rating ?? new Rating(0, 0, 0);
|
ProductRating = Book.Rating ?? new Rating(0, 0, 0);
|
||||||
Authors = Book.AuthorNames();
|
Authors = Book.AuthorNames();
|
||||||
Narrators = Book.NarratorNames();
|
Narrators = Book.NarratorNames();
|
||||||
Category = string.Join(" > ", Book.CategoriesNames());
|
Category = string.Join(", ", Book.LowestCategoryNames());
|
||||||
Misc = GetMiscDisplay(libraryBook);
|
Misc = GetMiscDisplay(libraryBook);
|
||||||
LastDownload = new(Book.UserDefinedItem);
|
LastDownload = new(Book.UserDefinedItem);
|
||||||
LongDescription = GetDescriptionDisplay(Book);
|
LongDescription = GetDescriptionDisplay(Book);
|
||||||
|
|||||||
@ -50,7 +50,7 @@ Author(s): {Book.AuthorNames()}
|
|||||||
Narrator(s): {Book.NarratorNames()}
|
Narrator(s): {Book.NarratorNames()}
|
||||||
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
||||||
Audio Bitrate: {Book.AudioFormat}
|
Audio Bitrate: {Book.AudioFormat}
|
||||||
Category: {string.Join(" > ", Book.CategoriesNames())}
|
Category: {string.Join(", ", Book.LowestCategoryNames())}
|
||||||
Purchase Date: {_libraryBook.DateAdded:d}
|
Purchase Date: {_libraryBook.DateAdded:d}
|
||||||
Language: {Book.Language}
|
Language: {Book.Language}
|
||||||
Audible ID: {Book.AudibleProductId}
|
Audible ID: {Book.AudibleProductId}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user