using System; using System.Threading.Tasks; using System.Collections.Generic; using AyaNova.Models; using AyaNova.Biz; using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; using Bogus; using AyaNova.Api.ControllerHelpers; using System.Diagnostics; namespace AyaNova.Util { public static class Seeder { public enum SeedLevel { NotValid, SmallOneManShopTrialDataSet, MediumLocalServiceCompanyTrialDataSet, LargeCorporateMultiRegionalTrialDataSet, HugeForLoadTest }; public static int SeededUserCount = 0; public static SeedLevel StringToSeedLevel(string size) { switch (size.ToLowerInvariant()) { case "small": return SeedLevel.SmallOneManShopTrialDataSet; case "medium": return SeedLevel.MediumLocalServiceCompanyTrialDataSet; case "large": return SeedLevel.LargeCorporateMultiRegionalTrialDataSet; case "huge": return SeedLevel.HugeForLoadTest; default: return SeedLevel.NotValid; } } ////////////////////////////////////////////////////// //Seed database for trial and testing purposes // public static async Task SeedDatabaseAsync(SeedLevel slevel, Decimal timeZoneOffset) { await SeedDatabaseAsync(slevel, Guid.Empty, timeZoneOffset); } public static async Task SeedDatabaseAsync(SeedLevel slevel, Guid JobId, Decimal timeZoneOffset) { bool LogJob = JobId != Guid.Empty; SeededUserCount = 0; ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("Seeder"); ApiServerState apiServerState = (ApiServerState)ServiceProviderProvider.Provider.GetService(typeof(ApiServerState)); //get the current server state so can set back to it later ApiServerState.ServerState wasServerState = apiServerState.GetState(); string wasReason = apiServerState.Reason; //START SERIAL NUMBER GENERATORS if (ServerBootConfig.WIDGET_SERIAL == null) ServerBootConfig.WIDGET_SERIAL = new AutoId(0); try { await LogStatusAsync(JobId, LogJob, log, $"SEEDER: Seeding data level is {slevel.ToString()}, time zone offset is {timeZoneOffset.ToString()}"); //Only allow this in a trial database if (!AyaNova.Core.License.ActiveKey.TrialLicense) { var msg = $"This database has a registered license key so it can't be seeded: {AyaNova.Core.License.LicenseInfoLogFormat}"; await LogStatusAsync(JobId, LogJob, log, msg); throw new System.NotSupportedException(msg); } //validate timezone offset if (timeZoneOffset > 14 || timeZoneOffset < (-12)) { var msg = $"Time zone offset \"{timeZoneOffset.ToString()}\" is not valid"; await LogStatusAsync(JobId, LogJob, log, msg); throw new System.NotSupportedException(msg); } apiServerState.SetOpsOnly("Seeding database"); //Erase all the data except for the license, schema and the manager user await DbUtil.EmptyBizDataFromDatabaseForSeedingOrImportingAsync(log); // //Set the default user options of the manager account // using (var cct = ServiceProviderProvider.DBContext) // { // var mgr = await cct.UserOptions.FirstAsync(m => m.Id == 1); // mgr.TimeZoneOffset = timeZoneOffset; // await cct.SaveChangesAsync(); // } //WIDGET sample form customization { var fc = new FormCustom() { FormKey = AyaType.Widget.ToString(), Template = @"[ { ""fld"": ""Notes"", ""required"": true }, { ""fld"": ""WidgetCustom1"", ""required"": false, ""type"": 1 }, { ""fld"": ""WidgetCustom2"", ""required"": true, ""type"": 4 }, { ""fld"": ""WidgetCustom3"", ""required"": false, ""type"": 5 }, { ""fld"": ""WidgetCustom4"", ""required"": false, ""type"": 6 }, { ""fld"": ""WidgetCustom5"", ""required"": false, ""type"": 8 }, { ""fld"": ""WidgetCustom6"", ""required"": false, ""type"": 2 }, { ""fld"": ""WidgetCustom7"", ""required"": false, ""type"": 3 } ]" }; //Create and save to db using (var cct = ServiceProviderProvider.DBContext) { await FormCustomBiz.GetBiz(cct).CreateAsync(fc); } } //Create a couple of DataListView's for development and testing { var dlv = new DataListView() { Name = "Name starts with generic", UserId = 1, ListKey = "TestWidgetDataList", Public = true, ListView = @"[{""fld"": ""widgetname"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Generic""}]}}]" }; //Create and save to db using (var cct = ServiceProviderProvider.DBContext) { await DataListViewBiz.GetBiz(cct).CreateAsync(dlv); } dlv = new DataListView() { Name = "Awesome (lots of fields)", UserId = 1, ListKey = "TestWidgetDataList", Public = true, ListView = @"[{""fld"": ""widgetname"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Awesome""}]}},{""fld"":""widgetserial""},{""fld"":""widgetdollaramount""},{""fld"":""widgetusertype""},{""fld"":""widgetstartdate""},{""fld"":""widgetactive""},{""fld"":""username""},{""fld"":""widgettags""},{""fld"":""widgetcustom1""},{""fld"":""widgetcustom2""}]" }; //Create and save to db using (var cct = ServiceProviderProvider.DBContext) { await DataListViewBiz.GetBiz(cct).CreateAsync(dlv); } } //Seed special test data for integration testing //log.LogInformation("Seeding known users"); await SeedKnownUsersAsync(log); //log.LogInformation("Seeding all other data"); switch (slevel) { case SeedLevel.SmallOneManShopTrialDataSet: { #region GenSmall //This is for a busy but one man shop with a single office person handling stuff back at the shop //PERF await LogStatusAsync(JobId, LogJob, log, $"Seeding SMALL number of user(s)...."); var watch = new Stopwatch(); watch.Start(); //Generate owner and lead tech await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.OpsAdminFull, UserType.Schedulable); //Generate one office person / secretary await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.AccountingFull, UserType.NonSchedulable); //PERF watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms"); //100 widgets watch = new Stopwatch(); watch.Start(); await GenSeedWidgetAsync(log, 100); //PERF watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"100 Widgets seeded in {watch.ElapsedMilliseconds} ms"); #endregion gensmall } break; case SeedLevel.MediumLocalServiceCompanyTrialDataSet: { #region GenMedium //This is for a typical AyaNova medium busy shop //has one location, many techs and full staff for each department //PERF await LogStatusAsync(JobId, LogJob, log, $"Seeding MEDIUM number of user(s)...."); var watch = new Stopwatch(); watch.Start(); //One IT administrator, can change ops but nothing else await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminFull, UserType.NonSchedulable); //One business administrator, can view ops issues await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //One owner who doesn't control anything but views stuff await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminLimited | AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited | AuthorizationRoles.SalesLimited, UserType.NonSchedulable); //20 techs await GenSeedUserAsync(log, 20, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Schedulable); //2 subcontractors await GenSeedUserAsync(log, 2, AuthorizationRoles.SubContractorFull, UserType.Subcontractor); //3 generic office people people await GenSeedUserAsync(log, 3, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //2 Full sales people await GenSeedUserAsync(log, 2, AuthorizationRoles.SalesFull, UserType.NonSchedulable); //1 dispatch manager await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //1 Inventory manager await GenSeedUserAsync(log, 1, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NonSchedulable); //1 accountant / bookkeeper await GenSeedUserAsync(log, 1, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NonSchedulable); //10 full on customer users await GenSeedUserAsync(log, 10, AuthorizationRoles.CustomerLimited, UserType.Customer); //10 limited customer users await GenSeedUserAsync(log, 10, AuthorizationRoles.CustomerLimited, UserType.Customer); //PERF watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms"); //500 widgets await LogStatusAsync(JobId, LogJob, log, $"Seeding 500 Widgets...."); watch = new Stopwatch(); watch.Start(); //await GenSeedWidgetAsync(log, 500); await GenSeedWidgetAsync(log, 500); //PERF watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"500 Widgets seeded in {watch.ElapsedMilliseconds} ms"); #endregion genmedium } break; case SeedLevel.LargeCorporateMultiRegionalTrialDataSet: { #region GenLarge //this is a large corporation with multiple branches in multiple locations all in the same country //Each location has a full staff and corporate head office has an overarching staff member in charge of each location //PERF await LogStatusAsync(JobId, LogJob, log, $"Seeding LARGE number of user(s)...."); var watch = new Stopwatch(); watch.Start(); //IT administrator, can change ops but nothing else await GenSeedUserAsync(log, 2, AuthorizationRoles.OpsAdminFull, UserType.NonSchedulable); //business administrator, can view ops issues await GenSeedUserAsync(log, 2, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //owner / upper management who doesn't control anything but views stuff await GenSeedUserAsync(log, 5, AuthorizationRoles.BizAdminLimited | AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //100 techs await GenSeedUserAsync(log, 100, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Schedulable); //limited techs await GenSeedUserAsync(log, 50, AuthorizationRoles.TechLimited | AuthorizationRoles.DispatchLimited, UserType.Schedulable); //20 subcontractors await GenSeedUserAsync(log, 20, AuthorizationRoles.SubContractorFull, UserType.Subcontractor); //10 limited subcontractors await GenSeedUserAsync(log, 10, AuthorizationRoles.SubContractorLimited, UserType.Subcontractor); //30 generic office people people await GenSeedUserAsync(log, 30, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //10 Full sales people await GenSeedUserAsync(log, 10, AuthorizationRoles.SalesFull, UserType.NonSchedulable); //5 Limited sales people await GenSeedUserAsync(log, 5, AuthorizationRoles.SalesLimited, UserType.NonSchedulable); //5 dispatch manager await GenSeedUserAsync(log, 5, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //5 Inventory manager await GenSeedUserAsync(log, 5, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NonSchedulable); //10 Inventory manager assistants await GenSeedUserAsync(log, 5, AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //5 accountant / bookkeeper await GenSeedUserAsync(log, 5, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NonSchedulable); //100 full on customer users await GenSeedUserAsync(log, 20, AuthorizationRoles.CustomerFull, UserType.Customer); //100 limited customer users await GenSeedUserAsync(log, 20, AuthorizationRoles.CustomerLimited, UserType.Customer); //PERF watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms"); //5000 widgets await LogStatusAsync(JobId, LogJob, log, $"Seeding 5,000 Widgets...."); watch = new Stopwatch(); watch.Start(); //await GenSeedWidgetAsync(log, 5000); await GenSeedWidgetAsync(log, 5000); //PERF watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"5k Widgets seeded in {watch.ElapsedMilliseconds} ms"); #endregion genlarge } break; case SeedLevel.HugeForLoadTest: { #region GenHuge //this is the HUGE dataset for load and other testing //It is acceptable for this seeding to take many hours as it would normally be used rarely //PERF await LogStatusAsync(JobId, LogJob, log, $"Seeding HUGE number of user(s)...."); var watch = new Stopwatch(); watch.Start(); //IT administrator, can change ops but nothing else await GenSeedUserAsync(log, 10, AuthorizationRoles.OpsAdminFull, UserType.NonSchedulable); //business administrator, can view ops issues await GenSeedUserAsync(log, 10, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //owner / upper management who doesn't control anything but views stuff await GenSeedUserAsync(log, 20, AuthorizationRoles.BizAdminLimited | AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //regular techs await GenSeedUserAsync(log, 500, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Schedulable); //limited techs await GenSeedUserAsync(log, 200, AuthorizationRoles.TechLimited | AuthorizationRoles.DispatchLimited, UserType.Schedulable); //subcontractors await GenSeedUserAsync(log, 80, AuthorizationRoles.SubContractorFull, UserType.Subcontractor); //limited subcontractors await GenSeedUserAsync(log, 40, AuthorizationRoles.SubContractorLimited, UserType.Subcontractor); //generic office people people await GenSeedUserAsync(log, 200, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //20 Full sales people await GenSeedUserAsync(log, 20, AuthorizationRoles.SalesFull, UserType.NonSchedulable); //10 Limited sales people await GenSeedUserAsync(log, 10, AuthorizationRoles.SalesLimited, UserType.NonSchedulable); //dispatch manager await GenSeedUserAsync(log, 20, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //Inventory manager await GenSeedUserAsync(log, 40, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NonSchedulable); //Inventory manager assistants await GenSeedUserAsync(log, 20, AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //accountant / bookkeeper await GenSeedUserAsync(log, 20, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NonSchedulable); //full on customer users await GenSeedUserAsync(log, 200, AuthorizationRoles.CustomerFull, UserType.Customer); //limited customer users await GenSeedUserAsync(log, 50, AuthorizationRoles.CustomerLimited, UserType.Customer); //PERF watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms"); //20000 widgets await LogStatusAsync(JobId, LogJob, log, $"Seeding 20,000 Widgets...."); watch = new Stopwatch(); watch.Start(); //await GenSeedWidgetAsync(log, 20000); await GenSeedWidgetAsync(log, 20000); watch.Stop(); await LogStatusAsync(JobId, LogJob, log, $"20k Widgets seeded in {watch.ElapsedMilliseconds} ms"); #endregion genhuge } break; } await LogStatusAsync(JobId, LogJob, log, "Seeding completed successfully"); } catch (Exception ex) { log.LogError(ex, "Seeder:SeedDatabase error during ops"); if (LogJob) await JobsBiz.LogJobAsync(JobId, $"Seeder:SeedDatabase error during ops\r\n{ex.Message}"); throw ex; } finally { //TODO: fully async: watch the job and don't turn the state back until the job is done? log.LogInformation($"Seeder: setting server state back to {wasServerState.ToString()}"); apiServerState.SetState(wasServerState, wasReason); } } //Log the status and also job if it's run via job private static async Task LogStatusAsync(Guid JobId, bool LogJob, ILogger log, string msg) { log.LogInformation(msg); if (LogJob) await JobsBiz.LogJobAsync(JobId, msg); } public static long RUNNING_COUNT = 0; public static string Uniquify(string s) { return s + " " + (++RUNNING_COUNT).ToString(); } private static string[] TagSet = new[] { "red", "orange", "yellow", "green", "blue", "indigo", "violet", "brown", "black", "white", "silver", "gold", "fuchsia", "jade", "mauve", "purple", "quince", "xanthic", "zebra", "zone0", "zone1", "zone2", "zone3", "zone4", "zone5", "zone6", "zone7", "zone8", "zone9" }; private static List RandomTags(Faker f) { var t = f.PickRandom(TagSet, f.Random.Int(1, 5));//pick up to 5 tags to apply return new List(t); } ////////////////////////////////////////////////////// //Seed test data for integration tests // public static async Task SeedKnownUsersAsync(ILogger log) { try { var KnownUserTags = new List(); KnownUserTags.Add("known-user"); KnownUserTags.Add("test-role-user"); //TEST USERS //one of each role type await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminLimited, UserType.NonSchedulable, "BizAdminLimited", "BizAdminLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminFull, UserType.NonSchedulable, "BizAdminFull", "BizAdminFull", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchLimited, UserType.NonSchedulable, "DispatchLimited", "DispatchLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchFull, UserType.NonSchedulable, "DispatchFull", "DispatchFull", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.InventoryLimited, UserType.NonSchedulable, "InventoryLimited", "InventoryLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.InventoryFull, UserType.NonSchedulable, "InventoryFull", "InventoryFull", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.AccountingFull, UserType.NonSchedulable, "Accounting", "Accounting", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.TechLimited, UserType.Schedulable, "TechLimited", "TechLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.TechFull, UserType.Schedulable, "TechFull", "TechFull", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.SalesLimited, UserType.NonSchedulable, "SalesLimited", "SalesLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.SalesFull, UserType.NonSchedulable, "SalesFull", "SalesFull", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.SubContractorLimited, UserType.Subcontractor, "SubContractorLimited", "SubContractorLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.SubContractorFull, UserType.Subcontractor, "SubContractorFull", "SubContractorFull", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.CustomerLimited, UserType.Customer, "CustomerLimited", "CustomerLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.CustomerFull, UserType.Customer, "CustomerFull", "CustomerFull", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.CustomerFull, UserType.HeadOffice, "HeadOffice", "HeadOffice", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, "OpsAdminLimited", "OpsAdminLimited", KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminFull, UserType.NonSchedulable, "OpsAdminFull", "OpsAdminFull", KnownUserTags); //PRIVACY TEST USER - this is used for a test to see if user info leaks into the logs await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, "TEST_PRIVACY_USER_ACCOUNT", "TEST_PRIVACY_USER_ACCOUNT", KnownUserTags); //TEST NOT ACTIVE - this is used for a test to see if inactive user can login await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, false, "TEST_INACTIVE", "TEST_INACTIVE", 0, KnownUserTags); //Alternate translation users for each stock translation await GenSeedUserAsync(log, 1, AuthorizationRoles.All, UserType.Administrator, true, "de", "de", await TranslationBiz.TranslationNameToIdStaticAsync("de"), KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.All, UserType.Administrator, true, "es", "es", await TranslationBiz.TranslationNameToIdStaticAsync("es"), KnownUserTags); await GenSeedUserAsync(log, 1, AuthorizationRoles.All, UserType.Administrator, true, "fr", "fr", await TranslationBiz.TranslationNameToIdStaticAsync("fr"), KnownUserTags); } catch { throw; } } /// /// Generate seed user with active=true /// (override to save typing) /// public static async Task GenSeedUserAsync(ILogger log, int count, AuthorizationRoles roles, UserType userType, string login, string password, List tags = null) { try { await GenSeedUserAsync(log, count, roles, userType, true, login, password, 0, tags); } catch { throw; } } public static async Task GenSeedUserAsync(ILogger log, int count, AuthorizationRoles roles, UserType userType, bool active = true, string login = null, string password = null, long translationId = 0, List tags = null) { if (translationId == 0) translationId = ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID; //Don't do the following commented out, it's slower // using (var ct = ServiceProviderProvider.DBContext) // { // UserBiz Biz = UserBiz.GetBiz(ct); Faker Fake = new Faker(); for (int x = 0; x < count; x++) { User u = new User(); u.Active = active; var p = new Bogus.Person(); u.Name = Uniquify(p.FullName); if (login != null) { u.Login = login; u.Name += " - " + login; } else u.Login = p.FirstName; if (password != null) u.Password = password; else u.Password = u.Login; u.Roles = roles; u.UserType = userType; u.EmployeeNumber = "A-" + (454 + SeededUserCount).ToString() + "-Y"; u.Notes = Fake.Lorem.Sentence();//Fake.Lorem.Paragraph(2); //TODO: After have USER and HEADOFFICE and VENDOR, if usertype is subcontractor or client or headoffice it needs to set a corresponding user's parent org record id to go with it //use provided tags or generate them if (tags == null) u.Tags = RandomTags(Fake); else u.Tags = tags; //Children and relations u.UserOptions = new UserOptions(); u.UserOptions.TranslationId = translationId; u.UserOptions.EmailAddress = p.Email.Replace("gmail.com", "helloayanova.com").Replace("hotmail.com", "helloayanova.com").Replace("yahoo.com", "helloayanova.com"); u.UserOptions.Hour12 = true; u.UserOptions.CurrencyName = "USD"; u.UserOptions.UiColor = Fake.Internet.Color(); //this seems wrong but is actually faster!? UserBiz Biz = UserBiz.GetBiz(ServiceProviderProvider.DBContext); //allow creation of not entirely ready users (missing client id or subcontractor vendor id etc) Biz.SeedOrImportRelaxedRulesMode = true; var NewObject = await Biz.CreateAsync(u); if (NewObject == null) { log.LogError($"Seeder::GenSeedUser error creating user {u.Name}\r\n" + Biz.GetErrorsAsString()); throw new System.Exception("Seeder::GenSeedUser error creating user\r\n" + Biz.GetErrorsAsString()); } //} } SeededUserCount += count; } ////////////////////////////////////////////////////// //Seed widget for testing // public static async Task GenSeedWidgetAsync(ILogger log, int count) { //this is 4 times slower than doing it inside the loop below //seems counterintuitive but maybe it's to do with the db context not being refreshed? // WidgetBiz biz = WidgetBiz.GetBiz(ServiceProviderProvider.DBContext); var f = new Bogus.Faker(); //RANDOM ROLES Array values = Enum.GetValues(typeof(UserType)); Random random = new Random(); DateTime seedStartWindow = DateTime.Now.AddYears(-1); DateTime seedEndWindow = DateTime.Now.AddYears(1); for (int x = 0; x < count; x++) { Widget o = new Widget(); o.Name = Uniquify(f.Commerce.ProductName()); o.Active = true; // o.StartDate = f.Date.Between(DateTime.Now, DateTime.Now.AddMinutes(60)).ToUniversalTime(); // o.EndDate = f.Date.Between(DateTime.Now.AddMinutes(90), DateTime.Now.AddHours(5)).ToUniversalTime(); // o.StartDate = DateTime.Now.ToUniversalTime(); // o.EndDate = DateTime.Now.AddMinutes(60).ToUniversalTime(); DateTime dtSeed = f.Date.Between(seedStartWindow, seedEndWindow).ToUniversalTime(); o.StartDate = dtSeed; o.EndDate = dtSeed.AddMinutes(60).ToUniversalTime(); o.DollarAmount = Convert.ToDecimal(f.Commerce.Price()); //Random but valid enum UserType randomUserType = (UserType)values.GetValue(random.Next(values.Length)); o.UserType = randomUserType; o.Notes = f.Lorem.Sentence(); o.WikiContent=@" ## Example Markdown for Wiki pages Headings # Heading 1st level ## Heading 2nd level ### Heading 3rd level #### Heading 4th level ##### Heading 5th level ###### Heading 6th level Emphasis text styles *Italic* **Bold** Hyperlink [Link](https://ayanova.com) Image ![Image](https://www.ayanova.com/images/AyaNovaIcon256.png) Quote block > ""Morbi eget dapibus felis. Vivamus venenatis porttitor tortor sit amet rutrum. Pellentesque aliquet quam enim, eu volutpat urna rutrum a. Nam vehicula nunc mauris, a ultricies libero efficitur sed. *Class aptent* taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed molestie imperdiet consectetur."" > The overriding design goal for Markdown's > formatting syntax is to make it as readable > as possible. The idea is that a > Markdown-formatted document should be > publishable as-is, as plain text, without > looking like it's been marked up with tags >> or formatting instructions. Unordered list * List * List * List Ordered list 1. One 2. Two 3. Three Nested lists * First * subitem One * subitem Two * Second 1. sub one 2. sub two Horizontal rule --- `Inline block` with backticks ``` # Multi-line block print '3 backticks or' print 'indent 4 spaces' ``` Add Tasks - [ ] task one - [x] task two (completed) Markdown Quick Reference ======================== This guide is a very brief overview, with examples, of the syntax that [Markdown] supports. It is itself written in Markdown and you can copy the samples over to the left-hand pane for experimentation. It's shown as *text* and not *rendered HTML*. [Markdown]: http://daringfireball.net/projects/markdown/ Simple Text Formatting ====================== First thing is first. You can use *stars* or _underscores_ for italics. **Double stars** and __double underscores__ do bold. ***Three together*** do ___both___. Paragraphs are pretty easy too. Just have a blank line between chunks of text. > This chunk of text is in a block quote. Its multiple lines will all be > indended a bit from the rest of the text. > > > Multiple levels of block quotes also work. Sometimes you want to include some code, such as when you are explaining how `

` HTML tags work, or maybe you are a programmer and you are discussing `someMethod()`. If you want to include some code and have newlines preserved, indent the line with a tab or at least four spaces. Extra spaces work here too. This is also called preformatted text and it is useful for showing examples. The text will stay as text, so any *markdown* or HTML you add will not show up formatted. This way you can show markdown examples in a markdown document. > You can also use preformatted text with your blockquotes > as long as you add at least five spaces. Headings ======== There are a couple of ways to make headings. Using three or more equals signs on a line under a heading makes it into an ""h1"" style. Three or more hyphens under a line makes it \""h2\"" (slightly smaller). You can also use multiple pound symbols before and after a heading. Pounds after the title are ignored. Here's some examples: This is H1 ========== This is H2 ---------- # This is H1 ## This is H2 ### This is H3 with some extra pounds ### #### You get the idea #### ##### I don't need extra pounds at the end ###### H6 is the max Links ===== Let's link to a few sites. First, let's use the bare URL, like . Great for text, but ugly for HTML. Next is an inline link to [Google](http://www.google.com). A little nicer. This is a reference-style link to [Wikipedia] [1]. Lastly, here's a pretty link to [Yahoo]. The reference-style and pretty links both automatically use the links defined below, but they could be defined *anywhere* in the markdown and are removed from the HTML. The names are also case insensitive, so you can use [YaHoO] and have it link properly. [1]: http://www.wikipedia.org/ [Yahoo]: http://www.yahoo.com/ Title attributes may be added to links by adding text after a link. This is the [inline link](http://www.bing.com \""Bing\"") with a \""Bing\"" title. You can also go to [W3C] [2] and maybe visit a [friend]. [2]: http://w3c.org (The W3C puts out specs for web-based things) [Friend]: http://facebook.com/ \""Facebook!\"" Email addresses in plain text are not linked: test@example.com. Email addresses wrapped in angle brackets are linked: . They are also obfuscated so that email harvesting spam robots hopefully won't get them. Lists ===== * This is a bulleted list * Great for shopping lists - You can also use hyphens + Or plus symbols The above is an \""unordered\"" list. Now, on for a bit of order. 1. Numbered lists are also easy 2. Just start with a number 3738762. However, the actual number doesn't matter when converted to HTML. 1. This will still show up as 4. You might want a few advanced lists: - This top-level list is wrapped in paragraph tags - This generates an extra space between each top-level item. - You do it by adding a blank line - This nested list also has blank lines between the list items. - How to create nested lists 1. Start your regular list 2. Indent nested lists with four spaces 3. Further nesting means you should indent with four more spaces * This line is indented with eight spaces. - List items can be quite lengthy. You can keep typing and either continue them on the next line with no indentation. - Alternately, if that looks ugly, you can also indent the next line a bit for a prettier look. - You can put large blocks of text in your list by just indenting with four spaces. This is formatted the same as code, but you can inspect the HTML and find that it's just wrapped in a `

` tag and *won't* be shown as preformatted text. You can keep adding more and more paragraphs to a single list item by adding the traditional blank line and then keep on indenting the paragraphs with four spaces. You really need to only indent the first line, but that looks ugly. - Lists support blockquotes > Just like this example here. By the way, you can > nest lists inside blockquotes! > - Fantastic! - Lists support preformatted text You just need to indent eight spaces. Even More ========= Horizontal Rule --------------- If you need a horizontal rule you just need to put at least three hyphens, asterisks, or underscores on a line by themselves. You can also even put spaces between the characters. --- **************************** _ _ _ _ _ _ _ Those three all produced horizontal lines. Keep in mind that three hyphens under any text turns that text into a heading, so add a blank like if you use hyphens. Images ------ Images work exactly like links, but they have exclamation points in front. They work with references and titles too. ![Google Logo](http://www.google.com/images/errors/logo_sm.gif) and ![Happy]. [Happy]: http://www.wpclipart.com/smiley/simple_smiley/smiley_face_simple_green_small.png (\""Smiley face\"") Inline HTML ----------- If markdown is too limiting, you can just insert your own crazy HTML. Span-level HTML can *still* use markdown. Block level elements must be separated from text by a blank line and must not have any spaces before the opening and closing HTML.

It is a pity, but markdown does **not** work in here for most markdown parsers. [Marked] handles it pretty well.
"; o.Tags = RandomTags(f); o.UserId = f.Random.Int(1, SeededUserCount); //RANDOM CUSTOM FIELD DATA var c1 = DateUtil.UniversalISO8661Format(f.Date.Between(DateTime.Now.AddYears(-1), DateTime.Now.AddYears(1))); var c2 = f.Lorem.Sentence(); var c3 = f.Random.Int(1, 99999999); var c4 = f.Random.Bool().ToString().ToLowerInvariant(); var c5 = f.Random.Decimal(); o.CustomFields = $@"{{c1:""{c1}"",c2:""{c2}"",c3:{c3},c4:{c4},c5:{c5}}}"; //This seems wrong to do in a loop but is 4 times faster this way ?!? WidgetBiz biz = WidgetBiz.GetBiz(ServiceProviderProvider.DBContext); var NewObject = await biz.CreateAsync(o); if (NewObject == null) { log.LogError($"Seeder::GenSeedWidget error creating widget {o.Name}\r\n" + biz.GetErrorsAsString()); throw new System.Exception("Seeder::GenSeedWidget error creating widget\r\n" + biz.GetErrorsAsString()); } } } }//eoc }//eons