Improved importing
This commit is contained in:
parent
c8e759c067
commit
a2e30df51f
@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
namespace DataLayer.Migrations
|
namespace DataLayer.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(LibationContext))]
|
[DbContext(typeof(LibationContext))]
|
||||||
[Migration("20191119144803_Fresh")]
|
[Migration("20191125182309_Fresh")]
|
||||||
partial class Fresh
|
partial class Fresh
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@ -129,6 +129,13 @@ namespace DataLayer.Migrations
|
|||||||
b.HasIndex("Name");
|
b.HasIndex("Name");
|
||||||
|
|
||||||
b.ToTable("Contributors");
|
b.ToTable("Contributors");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
ContributorId = -1,
|
||||||
|
Name = ""
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||||
@ -200,6 +200,11 @@ namespace DataLayer.Migrations
|
|||||||
columns: new[] { "CategoryId", "AudibleCategoryId", "Name", "ParentCategoryCategoryId" },
|
columns: new[] { "CategoryId", "AudibleCategoryId", "Name", "ParentCategoryCategoryId" },
|
||||||
values: new object[] { -1, "", "", null });
|
values: new object[] { -1, "", "", null });
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Contributors",
|
||||||
|
columns: new[] { "ContributorId", "AudibleContributorId", "Name" },
|
||||||
|
values: new object[] { -1, null, "" });
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_BookContributor_BookId",
|
name: "IX_BookContributor_BookId",
|
||||||
table: "BookContributor",
|
table: "BookContributor",
|
||||||
@ -127,6 +127,13 @@ namespace DataLayer.Migrations
|
|||||||
b.HasIndex("Name");
|
b.HasIndex("Name");
|
||||||
|
|
||||||
b.ToTable("Contributors");
|
b.ToTable("Contributors");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
ContributorId = -1,
|
||||||
|
Name = ""
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||||
|
|||||||
@ -62,7 +62,8 @@ namespace DataLayer
|
|||||||
string description,
|
string description,
|
||||||
int lengthInMinutes,
|
int lengthInMinutes,
|
||||||
IEnumerable<Contributor> authors,
|
IEnumerable<Contributor> authors,
|
||||||
IEnumerable<Contributor> narrators)
|
IEnumerable<Contributor> narrators,
|
||||||
|
Category category)
|
||||||
{
|
{
|
||||||
// validate
|
// validate
|
||||||
ArgumentValidator.EnsureNotNull(audibleProductId, nameof(audibleProductId));
|
ArgumentValidator.EnsureNotNull(audibleProductId, nameof(audibleProductId));
|
||||||
@ -80,8 +81,7 @@ namespace DataLayer
|
|||||||
_seriesLink = new HashSet<SeriesBook>();
|
_seriesLink = new HashSet<SeriesBook>();
|
||||||
_supplements = new HashSet<Supplement>();
|
_supplements = new HashSet<Supplement>();
|
||||||
|
|
||||||
// since category/id is never null, nullity means it hasn't been loaded
|
Category = category;
|
||||||
CategoryId = Category.GetEmpty().CategoryId;
|
|
||||||
|
|
||||||
// simple assigns
|
// simple assigns
|
||||||
Title = title;
|
Title = title;
|
||||||
@ -233,8 +233,8 @@ namespace DataLayer
|
|||||||
|
|
||||||
public void UpdateCategory(Category category, DbContext context = null)
|
public void UpdateCategory(Category category, DbContext context = null)
|
||||||
{
|
{
|
||||||
// since category is never null, nullity means it hasn't been loaded
|
// since category is never null, nullity means it hasn't been loaded. non null means we're correctly loaded. just overwrite
|
||||||
if (Category != null || CategoryId == Category.GetEmpty().CategoryId)
|
if (Category != null)
|
||||||
{
|
{
|
||||||
Category = category;
|
Category = category;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -18,8 +18,7 @@ namespace DataLayer
|
|||||||
public class Category
|
public class Category
|
||||||
{
|
{
|
||||||
// Empty is a special case. use private ctor w/o validation
|
// Empty is a special case. use private ctor w/o validation
|
||||||
public static Category GetEmpty() => new Category { CategoryId = -1, AudibleCategoryId = "", Name = "", ParentCategory = null };
|
public static Category GetEmpty() => new Category { CategoryId = -1, AudibleCategoryId = "", Name = "" };
|
||||||
public bool IsEmpty() => string.IsNullOrWhiteSpace(AudibleCategoryId) || string.IsNullOrWhiteSpace(Name) || ParentCategory == null;
|
|
||||||
|
|
||||||
internal int CategoryId { get; private set; }
|
internal int CategoryId { get; private set; }
|
||||||
public string AudibleCategoryId { get; private set; }
|
public string AudibleCategoryId { get; private set; }
|
||||||
|
|||||||
@ -6,6 +6,9 @@ namespace DataLayer
|
|||||||
{
|
{
|
||||||
public class Contributor
|
public class Contributor
|
||||||
{
|
{
|
||||||
|
// Empty is a special case. use private ctor w/o validation
|
||||||
|
public static Contributor GetEmpty() => new Contributor { ContributorId = -1, Name = "" };
|
||||||
|
|
||||||
// contributors search links are just name with url-encoding. space can be + or %20
|
// contributors search links are just name with url-encoding. space can be + or %20
|
||||||
// author search link: /search?searchAuthor=Robert+Bevan
|
// author search link: /search?searchAuthor=Robert+Bevan
|
||||||
// narrator search link: /search?searchNarrator=Robert+Bevan
|
// narrator search link: /search?searchNarrator=Robert+Bevan
|
||||||
|
|||||||
@ -60,6 +60,9 @@ namespace DataLayer
|
|||||||
modelBuilder
|
modelBuilder
|
||||||
.Entity<Category>()
|
.Entity<Category>()
|
||||||
.HasData(Category.GetEmpty());
|
.HasData(Category.GetEmpty());
|
||||||
|
modelBuilder
|
||||||
|
.Entity<Contributor>()
|
||||||
|
.HasData(Contributor.GetEmpty());
|
||||||
|
|
||||||
// views are now supported via "query types" (instead of "entity types"): https://docs.microsoft.com/en-us/ef/core/modeling/query-types
|
// views are now supported via "query types" (instead of "entity types"): https://docs.microsoft.com/en-us/ef/core/modeling/query-types
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,28 +63,39 @@ namespace DtoImporterService
|
|||||||
|
|
||||||
private static Book createNewBook(Item item, LibationContext context)
|
private static Book createNewBook(Item item, LibationContext context)
|
||||||
{
|
{
|
||||||
|
// absence of authors is very rare, but possible
|
||||||
|
if (!item.Authors?.Any() ?? true)
|
||||||
|
item.Authors = new[] { new Person { Name = "", Asin = null } };
|
||||||
|
|
||||||
// nested logic is required so order of names is retained. else, contributors may appear in the order they were inserted into the db
|
// nested logic is required so order of names is retained. else, contributors may appear in the order they were inserted into the db
|
||||||
var authors = item
|
var authors = item
|
||||||
.Authors
|
.Authors
|
||||||
.Select(a => context.Contributors.Local.Single(c => a.Name == c.Name))
|
.Select(a => context.Contributors.Local.Single(c => a.Name == c.Name))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
var narrators
|
||||||
|
= item.Narrators is null || !item.Narrators.Any()
|
||||||
// if no narrators listed, author is the narrator
|
// if no narrators listed, author is the narrator
|
||||||
if (item.Narrators is null || !item.Narrators.Any())
|
? authors
|
||||||
item.Narrators = item.Authors;
|
|
||||||
// nested logic is required so order of names is retained. else, contributors may appear in the order they were inserted into the db
|
// nested logic is required so order of names is retained. else, contributors may appear in the order they were inserted into the db
|
||||||
var narrators = item
|
: item
|
||||||
.Narrators
|
.Narrators
|
||||||
.Select(n => context.Contributors.Local.Single(c => n.Name == c.Name))
|
.Select(n => context.Contributors.Local.Single(c => n.Name == c.Name))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// categories are laid out for a breadcrumb. category is 1st, subcategory is 2nd
|
||||||
|
// absence of categories is very rare, but possible
|
||||||
|
var lastCategory = item.Categories.LastOrDefault()?.CategoryId ?? "";
|
||||||
|
var category = context.Categories.Local.SingleOrDefault(c => c.AudibleCategoryId == lastCategory);
|
||||||
|
|
||||||
var book = context.Books.Add(new Book(
|
var book = context.Books.Add(new Book(
|
||||||
new AudibleProductId(item.ProductId),
|
new AudibleProductId(item.ProductId),
|
||||||
item.Title,
|
item.Title,
|
||||||
item.Description,
|
item.Description,
|
||||||
item.LengthInMinutes,
|
item.LengthInMinutes,
|
||||||
authors,
|
authors,
|
||||||
narrators)
|
narrators,
|
||||||
|
category)
|
||||||
).Entity;
|
).Entity;
|
||||||
|
|
||||||
var publisherName = item.Publisher;
|
var publisherName = item.Publisher;
|
||||||
@ -94,11 +105,6 @@ namespace DtoImporterService
|
|||||||
book.ReplacePublisher(publisher);
|
book.ReplacePublisher(publisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
// categories are laid out for a breadcrumb. category is 1st, subcategory is 2nd
|
|
||||||
var category = context.Categories.Local.SingleOrDefault(c => c.AudibleCategoryId == item.Categories.LastOrDefault().CategoryId);
|
|
||||||
if (category != null)
|
|
||||||
book.UpdateCategory(category, context);
|
|
||||||
|
|
||||||
book.UpdateBookDetails(item.IsAbridged, item.DatePublished);
|
book.UpdateBookDetails(item.IsAbridged, item.DatePublished);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(item.SupplementUrl))
|
if (!string.IsNullOrWhiteSpace(item.SupplementUrl))
|
||||||
|
|||||||
@ -33,8 +33,11 @@ namespace DtoImporterService
|
|||||||
.Except(localIds)
|
.Except(localIds)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// load existing => local
|
||||||
|
// remember to include default/empty/missing
|
||||||
|
var emptyName = Contributor.GetEmpty().Name;
|
||||||
if (remainingCategoryIds.Any())
|
if (remainingCategoryIds.Any())
|
||||||
context.Categories.Where(c => remainingCategoryIds.Contains(c.AudibleCategoryId)).ToList();
|
context.Categories.Where(c => remainingCategoryIds.Contains(c.AudibleCategoryId) || c.Name == emptyName).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// only use after loading contributors => local
|
// only use after loading contributors => local
|
||||||
|
|||||||
@ -47,11 +47,10 @@ namespace DtoImporterService
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// load existing => local
|
// load existing => local
|
||||||
|
// remember to include default/empty/missing
|
||||||
|
var emptyName = Contributor.GetEmpty().Name;
|
||||||
if (remainingContribNames.Any())
|
if (remainingContribNames.Any())
|
||||||
context.Contributors.Where(c => remainingContribNames.Contains(c.Name)).ToList();
|
context.Contributors.Where(c => remainingContribNames.Contains(c.Name) || c.Name == emptyName).ToList();
|
||||||
// _________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
// i tried to extract this pattern, but this part prohibits doing so
|
|
||||||
// wouldn't work anyway for Books.GetBooks()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// only use after loading contributors => local
|
// only use after loading contributors => local
|
||||||
|
|||||||
@ -29,12 +29,12 @@ namespace InternalUtilities
|
|||||||
{
|
{
|
||||||
var exceptions = new List<Exception>();
|
var exceptions = new List<Exception>();
|
||||||
|
|
||||||
|
// a book having no authors is rare but allowed
|
||||||
|
|
||||||
if (items.Any(i => string.IsNullOrWhiteSpace(i.ProductId)))
|
if (items.Any(i => string.IsNullOrWhiteSpace(i.ProductId)))
|
||||||
exceptions.Add(new ArgumentException($"Collection contains item(s) with blank {nameof(Item.ProductId)}", nameof(items)));
|
exceptions.Add(new ArgumentException($"Collection contains item(s) with blank {nameof(Item.ProductId)}", nameof(items)));
|
||||||
if (items.Any(i => string.IsNullOrWhiteSpace(i.Title)))
|
if (items.Any(i => string.IsNullOrWhiteSpace(i.Title)))
|
||||||
exceptions.Add(new ArgumentException($"Collection contains item(s) with blank {nameof(Item.Title)}", nameof(items)));
|
exceptions.Add(new ArgumentException($"Collection contains item(s) with blank {nameof(Item.Title)}", nameof(items)));
|
||||||
if (items.Any(i => i.Authors is null))
|
|
||||||
exceptions.Add(new ArgumentException($"Collection contains item(s) with null {nameof(Item.Authors)}", nameof(items)));
|
|
||||||
|
|
||||||
return exceptions;
|
return exceptions;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user