Compare commits
49
Commits
18d1640808
..
1.53.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e77666d9e | ||
|
|
4d21740124 | ||
|
|
1977b6b301 | ||
|
|
0ab14d8b63 | ||
|
|
466458e230 | ||
|
|
141d32f591 | ||
|
|
d9e7f225ae | ||
|
|
6ae63e2ad1 | ||
|
|
f5efdde37c | ||
|
|
1592d5dc8f | ||
|
|
50a8a59d92 | ||
|
|
b70d9559b0 | ||
|
|
81d5e8f07c | ||
|
|
20a53942b5 | ||
|
|
9edb2aa4aa | ||
|
|
6ed023f2cf | ||
|
|
2c9f5a846c | ||
|
|
41f7c05be3 | ||
|
|
1a03355e84 | ||
|
|
7743c3178e | ||
|
|
ab3d8e6e9a | ||
|
|
db4c348288 | ||
|
|
6683234642 | ||
|
|
6ddbb9479a | ||
|
|
6c7349a0f8 | ||
|
|
e97fd6cd3f | ||
|
|
184c7c252a | ||
|
|
bfe8c458d6 | ||
|
|
e6e0475db1 | ||
|
|
5090c60797 | ||
|
|
9432252e15 | ||
|
|
47111a1a3a | ||
|
|
6eb3d50375 | ||
|
|
4deb732804 | ||
|
|
20d9387d0b | ||
|
|
9f6d0ccaa0 | ||
|
|
1acbc4d213 | ||
|
|
8c99668fac | ||
|
|
ad44f46204 | ||
|
|
49d999c1e3 | ||
|
|
9ed4777a18 | ||
|
|
0cf44f68cc | ||
|
|
41ed5a4288 | ||
|
|
bbcba5e06c | ||
|
|
502cc326dd | ||
|
|
4675d4c5fc | ||
|
|
f80bb2fff9 | ||
|
|
6767906b0d | ||
|
|
a344af4498 |
@@ -0,0 +1,133 @@
|
|||||||
|
using LiteCharms.Features.MidrandBooks.Categories;
|
||||||
|
using LiteCharms.Features.MidrandBooks.Products;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.MidrandBooks.Seed;
|
||||||
|
|
||||||
|
public class CategorySeederService(CategoryService categoryService, ProductService productService, IFeatureManager features,
|
||||||
|
ILogger<CategorySeederService> logger) : BackgroundService
|
||||||
|
{
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
if (!await features.IsEnabledAsync("CategorySeederService")) return;
|
||||||
|
|
||||||
|
logger.LogInformation("Category and Product-Tag Mapping Seeding started (15-char limit applied)");
|
||||||
|
|
||||||
|
// Initialize Bogus to ensure repeatable distribution matrix pathing
|
||||||
|
var faker = new Faker();
|
||||||
|
Randomizer.Seed = new Random(101);
|
||||||
|
|
||||||
|
// 1. Curate Broad Book Categories (IsMain = true, Max 20, Max 15 chars)
|
||||||
|
var broadMainCategories = new[]
|
||||||
|
{
|
||||||
|
"Fiction", "Non-Fiction", "Youth & Kids", "Academic",
|
||||||
|
"Biographies", "Business", "Sci-Fi & Fantasy",
|
||||||
|
"Thrillers", "Self-Help", "History",
|
||||||
|
"Spirituality", "Arts & Photo", "Technology",
|
||||||
|
"Cookbooks", "Travel & Maps", "Poetry & Drama", "Graphic Novels"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Curate Niche Subcategories/Tags (IsMain = false, Max 15 chars)
|
||||||
|
var specializedSubCategories = new[]
|
||||||
|
{
|
||||||
|
"Cyberpunk", "Space Opera", "Historical Fix", "Cozy Mystery", "True Crime",
|
||||||
|
"Agile Project", "Software Eng", "AI & ML", "Cloud Comput",
|
||||||
|
"SA History", "African Lit", "Apartheid Era", "Mandela Legacy",
|
||||||
|
"Finance", "Investments", "Startup", "Leadership",
|
||||||
|
"CBT Therapy", "Mindfulness", "Yoga & Health",
|
||||||
|
"Baking Basics", "African Food", "Vegan Recipes",
|
||||||
|
"Ancient World", "WWII History", "Geopolitics",
|
||||||
|
"Writing Guides", "Criticism", "Classic Poetry",
|
||||||
|
"Early Learning", "Teen Romance", "Survival",
|
||||||
|
"Urban Fantasy", "Dark Fantasy", "Psych Thriller", "Hard Sci-Fi",
|
||||||
|
"Data Science", "DevOps", "Cybersecurity",
|
||||||
|
"Economics", "Real Estate", "Governance",
|
||||||
|
"Essays", "Memoirs", "Art History",
|
||||||
|
"Architecture", "Photography", "Travel Writing",
|
||||||
|
"Gaming Culture", "Philosophy", "Ethics",
|
||||||
|
"DIY Home", "SA Gardening", "Parenting"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Seed Main Categories into the System
|
||||||
|
logger.LogInformation("Seeding broad main categories...");
|
||||||
|
foreach (var mainCat in broadMainCategories)
|
||||||
|
{
|
||||||
|
if (stoppingToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
|
// Defensive truncation fallback just in case strings get modified later
|
||||||
|
string safeName = mainCat.Length > 15 ? mainCat.Substring(0, 15) : mainCat;
|
||||||
|
|
||||||
|
var result = await categoryService.CreateCategoryAsync(safeName, isMain: true, stoppingToken);
|
||||||
|
if (result.IsFailed && !result.Errors[0].Message.Contains("already exists", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
logger.LogWarning("Notice while adding main category '{Name}': {Msg}", safeName, result.Errors[0].Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Seed Subcategories into the System
|
||||||
|
logger.LogInformation("Seeding boundless specialized niche tags...");
|
||||||
|
foreach (var subCat in specializedSubCategories)
|
||||||
|
{
|
||||||
|
if (stoppingToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
|
string safeName = subCat.Length > 15 ? subCat.Substring(0, 15) : subCat;
|
||||||
|
|
||||||
|
var result = await categoryService.CreateCategoryAsync(safeName, isMain: false, stoppingToken);
|
||||||
|
if (result.IsFailed && !result.Errors[0].Message.Contains("already exists", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
logger.LogWarning("Notice while adding subcategory '{Name}': {Msg}", safeName, result.Errors[0].Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Query back all enabled categories to extract active IDs for junction mapping
|
||||||
|
var fetchMainResult = await categoryService.GetCategoriesAsync(isMain: true, stoppingToken);
|
||||||
|
var fetchSubResult = await categoryService.GetCategoriesAsync(isMain: false, stoppingToken);
|
||||||
|
|
||||||
|
if (fetchMainResult.IsFailed || fetchSubResult.IsFailed)
|
||||||
|
{
|
||||||
|
logger.LogError("Aborting junction seeding: Could not retrieve categories from data store.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainCategoryIds = fetchMainResult.Value.Select(c => c.Id).ToArray();
|
||||||
|
var subCategoryIds = fetchSubResult.Value.Select(c => c.Id).ToArray();
|
||||||
|
|
||||||
|
// 6. Map Categories to your Product Collection (Product IDs 0 - 21)
|
||||||
|
logger.LogInformation("Beginning Product-Category mapping assignments for Product IDs 0 through 21...");
|
||||||
|
|
||||||
|
for (long productId = 0; productId <= 21; productId++)
|
||||||
|
{
|
||||||
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
|
// Every book belongs to 1 or 2 main categories
|
||||||
|
int mainCategoriesToAssign = faker.Random.Number(1, 2);
|
||||||
|
var chosenMainIds = faker.PickRandom(mainCategoryIds, mainCategoriesToAssign).Distinct();
|
||||||
|
|
||||||
|
foreach (var mainId in chosenMainIds)
|
||||||
|
{
|
||||||
|
var linkResult = await productService.AddProductCategoryAsync(productId, mainId, stoppingToken);
|
||||||
|
if (linkResult.IsFailed)
|
||||||
|
{
|
||||||
|
if (!linkResult.Errors[0].Message.Contains("exist", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Junction note for Product {PId} and Main Category {CId}: {Msg}", productId, mainId, linkResult.Errors[0].Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every book gets 1 to 4 granular subgenre tags
|
||||||
|
int subCategoriesToAssign = faker.Random.Number(1, 4);
|
||||||
|
var chosenSubIds = faker.PickRandom(subCategoryIds, subCategoriesToAssign).Distinct();
|
||||||
|
|
||||||
|
foreach (var subId in chosenSubIds)
|
||||||
|
{
|
||||||
|
var linkResult = await productService.AddProductCategoryAsync(productId, subId, stoppingToken);
|
||||||
|
if (linkResult.IsFailed && !linkResult.Errors[0].Message.Contains("exist", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Junction note for Product {PId} and Sub Category {CId}: {Msg}", productId, subId, linkResult.Errors[0].Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation("Category and Product-Tag Mapping Seeding completed successfully.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ builder.Services
|
|||||||
.AddLogging()
|
.AddLogging()
|
||||||
.AddShopServices()
|
.AddShopServices()
|
||||||
.AddHostedService<ProductsSeederService>()
|
.AddHostedService<ProductsSeederService>()
|
||||||
|
.AddHostedService<CategorySeederService>()
|
||||||
.AddHostedService<CustomerSeederService>()
|
.AddHostedService<CustomerSeederService>()
|
||||||
.AddMidrandShopDatabase(builder.Configuration);
|
.AddMidrandShopDatabase(builder.Configuration);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"FeatureManagement": {
|
"FeatureManagement": {
|
||||||
|
"CategorySeederService": true,
|
||||||
"CustomerSeederService": false,
|
"CustomerSeederService": false,
|
||||||
"ProductsSeederService": false
|
"ProductsSeederService": false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public sealed class BooksService(IDbContextFactory<MidrandBooksDbContext> contex
|
|||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(b => b.Author)
|
.Include(b => b.Author)
|
||||||
.Include(b => b.Product)
|
.Include(b => b.Product)
|
||||||
.ThenInclude(b => b.Prices)
|
.ThenInclude(b => b!.Prices)
|
||||||
.OrderByDescending(b => b.CreatedAt)
|
.OrderByDescending(b => b.CreatedAt)
|
||||||
.Where(b => b.AuthorId == authorId)
|
.Where(b => b.AuthorId == authorId)
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|||||||
Reference in New Issue
Block a user