static void Main(string[] args) { var tabledata = RowCollection.Create(exambledatalong); var header = RowConf.Create(headerdata).PresetTH(); tabledata.Import(0, header); tabledata.Settings.Border.Enabled = true; tabledata.Settings.Border.HorizontalLineBody = BorderConf.HorizontalLineAlwaysOnFunc; using (var writer = new CoExHtmlWriter() { Title = "A fancy demo" }) { CoEx.WriteTitleLarge("A fancy demo"); CoEx.WriteLine(); CoEx.WriteTable(tabledata); var target = Path.Combine(AppContext.BaseDirectory, "lastoutput.html"); if (File.Exists(target)) { File.Delete(target); } writer.SaveAs(target); CoEx.WriteLine(); CoEx.WriteLine("Output file: {0}", target); } CoEx.WriteLine(); CoEx.PressAnyKey(); }
protected static void DemoIntro() { CoEx.WriteTitleLarge("Welcome to PerrysNetConsole Demo Application"); CoEx.WriteTitleLarge("Code & Concept by Christian Blechert"); CoEx.WriteLine(); CoEx.WriteLine("This is the introduction for a highlighted text"); CoEx.WriteHl("Highlighted text"); CoEx.WriteLine(); CoEx.WriteLine("And here comes text highlighting in multiple columns"); CoEx.WriteLine(); CoEx.WriteHl("Highlighted text", "in multiple columns"); CoEx.WriteLine(); CoEx.WriteHl("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); CoEx.WriteHl("10", "11", "12", "13", "14", "15", "16", "17", "18", "19"); CoEx.WriteLine(); CoEx.WriteHl("And what happens when the text is too long?", "The really really really really really really really really really really long text will be wrapped. Is this quite cool? Yes, it is. :-)"); CoEx.WriteLine(); CoEx.WriteLine(); CoEx.WriteLine("Oh and normal text of couse, nevermind, go ahead!"); }
static void Main(string[] args) { if (!(args.Length > 2 && args[0].StartsWith("http") && args[1].EndsWith(".json") && args[2].EndsWith(".html"))) { CoEx.WriteLine("Usage: ForumParser.exe http(s)://... output.json output.html"); return; } CoEx.ForcedBufferWidth = 135; /* * var url = @"https://forum.netcup.de/sonstiges/smalltalk/1051-das-l%C3%A4ngste-thema/"; * var path = @"H:\laengstes.json"; * var htmloutfile = @"H:\laengstes.html"; */ var forcecrawl = args.Length > 3 && args[3] == "--force"; var url = args[0]; var path = args[1]; var htmloutfile = args[2]; if (File.Exists(path) == false || forcecrawl) { Crawl(url, path); } PrintStats(path, htmloutfile); }
protected static void DemoTimeout() { CoEx.WriteTitleLarge("Basic timeout"); CoEx.WriteLine(); CoEx.Timeout(3); }
protected static void DemoAlignment() { rc2.Settings.Align = delegate(RowConf me, int colindex, string s) { switch (colindex) { case 0: return(RowCollectionSettings.ALIGN.RIGHT); case 1: return(null); case 2: return(RowCollectionSettings.ALIGN.RIGHT); case 3: return(RowCollectionSettings.ALIGN.CENTER); default: return(RowCollectionSettings.ALIGN.LEFT); } }; rc2.Settings.Border.HorizontalLineBody = delegate(RowConf r) { return(true); }; CoEx.WriteTitleLarge("Tables - Chapter 5"); CoEx.WriteLine(); CoEx.WriteTitle("Column alignment"); CoEx.WriteTable(rc2); }
protected static void BasicTableLongText() { CoEx.WriteTitleLarge("Tables - Chapter 2"); CoEx.WriteLine(); CoEx.WriteTitle("The basic table with long text"); rc2.Import(0, header); CoEx.WriteTable(rc2); }
/// <summary> /// Show current setup /// </summary> /// <param name="config">Config</param> /// <param name="args">CMD Args</param> /// <param name="provider">Provider</param> /// <returns>Exit code</returns> private static int ShowInfo(Data.MailiasConfig config, Args args, Data.IProvider provider) { CoEx.WriteLine($"Used Provider: {config.Provider}"); CoEx.WriteLine($"Mail Domain: {config.MailDomain}"); CoEx.WriteLine($"Alias Target: {config.TargetAddress}"); CoEx.WriteLine($"Prefix: {config.Prefix}"); CoEx.WriteLine($"Code Length: {config.UniqeIdLength}"); return(0); }
protected static void DemoProgressBarMessages() { Tuple <Message.LEVEL, string>[] states = new Tuple <Message.LEVEL, string>[] { new Tuple <Message.LEVEL, string>(Message.LEVEL.INFO, "Initialize"), new Tuple <Message.LEVEL, string>(Message.LEVEL.INFO, "This is a message on INFO level"), new Tuple <Message.LEVEL, string>(Message.LEVEL.DEBUG, "Debugging message"), new Tuple <Message.LEVEL, string>(Message.LEVEL.WARN, "Serious warning, be careful!"), new Tuple <Message.LEVEL, string>(Message.LEVEL.ERROR, "Fatal error, its too late to be careful..."), new Tuple <Message.LEVEL, string>(Message.LEVEL.SUCCESS, "Finally done with this demo!") }; CoEx.WriteTitleLarge("Progress bar with status messages"); CoEx.WriteLine(); // Example without using() { } Progress prog = new Progress(); prog.Start(); int perc = 0; for (int i = 0; i < states.Length; i++) { // Post message prog.Message(states[i].Item1, states[i].Item2); // Increase by 20 percent in small steps if (states[i].Item1 == Message.LEVEL.DEBUG) { prog.Message(Message.LEVEL.DEBUG, "Ooops, dont know how long this will take, please wait..."); prog.IsWaiting = true; System.Threading.Thread.Sleep(20000); prog.IsWaiting = false; } else { for (int j = 0; j < 100 / states.Length; j++) { perc++; prog.Update(perc); System.Threading.Thread.Sleep(20); } } // wait 2 seconds System.Threading.Thread.Sleep(2000); } prog.Update(100); prog.Stop(); System.Threading.Thread.Sleep(2000); }
protected static void DemoScrolltext() { var width = CoEx.WindowWidth; var height = CoEx.WindowHeight; var bheight = CoEx.BufferHeight; CoEx.WindowHeight = 20; CoEx.BufferWidth = CoEx.WindowWidth = 40; CoEx.BufferHeight = ScrollText.Length + (2 * CoEx.WindowHeight); ulong pos = CoEx.RealCursorY; for (int i = 0; i < CoEx.WindowHeight; i++) { CoEx.WriteLine(); } foreach (var line in ScrollText) { var temp = line; var istitle = temp.StartsWith(":"); if (istitle) { temp = temp.Substring(1); } var row = RowConf.Create(new string[] { temp }).SetAlignment(RowConf.ALIGNCENTER); if (istitle) { row = row.PresetTitle(); } CoEx.WriteColumns(row); } int length = (int)(CoEx.RealCursorY - pos); for (int i = 0; i < CoEx.WindowHeight; i++) { CoEx.WriteLine(); } CoEx.Scroll(0, (int)(pos - CoEx.RealCursorY)); for (int i = 0; i < length; i++) { CoEx.BufferViewportY++; System.Threading.Thread.Sleep(600); } System.Threading.Thread.Sleep(2000); CoEx.WindowWidth = CoEx.BufferWidth = width; CoEx.WindowHeight = height; CoEx.BufferHeight = bheight; }
public static int Main(string[] args) { // Console output settings CoEx.ForcedBufferWidth = 100; // Load settings var config = LoadConfiguration(); if (string.IsNullOrWhiteSpace(config.Provider)) { CoEx.WriteLine($"No provider configured in '{GetConfigPath()}'"); return(99); } // Load provider var provider = CreateProvider(config); if (provider == null) { CoEx.WriteLine("No valid provider was loaded."); return(99); } // Parse arguments int returnCode = 99; Parser.Default.ParseArguments <Args>(args).WithParsed(o => { if (o.Info) { // Show current info returnCode = ShowInfo(config, o, provider); } if (!string.IsNullOrWhiteSpace(o.CreateName)) { // Create a new alias returnCode = CreateAlias(config, o, provider).WaitForValue <int>(); } if (!string.IsNullOrWhiteSpace(o.DeleteAlias)) { // Delete an existing alias returnCode = DeleteAlias(config, o, provider).WaitForValue <int>(); } else if (o.List) { // List existing aliases returnCode = ListAliases(config, o, provider).WaitForValue <int>(); } }); return(returnCode); }
protected static void DemoLoadIndicator() { CoEx.WriteTitleLarge("Loading indicator"); CoEx.WriteLine(); LoadIndicator indicator = new LoadIndicator() { Message = "Load the awesomeness" }; indicator.Start(); System.Threading.Thread.Sleep(5000); indicator.Stop(); }
protected static void DemoBasicColumns() { CoEx.WriteTitleLarge("Basic column system, no custom column length, no custom formatting"); CoEx.WriteLine(); // loop both exampledata arrays foreach (var data in new string[][][] { exambledata, exambledatalong }) { // print single row from one of the exampledata arrays foreach (var row in data) { CoEx.WriteColumns(row); // print the row } } }
protected static void DemoProgressBar() { CoEx.WriteTitleLarge("Simple Progressbar"); CoEx.WriteLine(); // using() { } is optional, see Start(), Stop() and Clear() using (Progress simpr = new Progress()) { simpr.Start(); do { simpr.Percentage += 0.20; System.Threading.Thread.Sleep(5); }while (simpr.Percentage < 100); System.Threading.Thread.Sleep(1000); } }
protected static void DemoBasicTable() { CoEx.WriteTitleLarge("Tables - Chapter 1"); CoEx.WriteLine(); CoEx.WriteTitle("The basic table"); rc1.Settings.StretchHorizontal = false; CoEx.WriteTable(rc1); rc1.Settings.StretchHorizontal = true; CoEx.WriteLine(); CoEx.WriteTitle("The basic table with header, stretched to window width"); rc1.Import(0, header); CoEx.WriteTable(rc1); CoEx.WriteLine(); CoEx.WriteTitle("The basic table, border disabled"); rc1.Settings.Border.Enabled = false; // override default CoEx.WriteTable(rc1); }
protected static void DemoTableSynchronized() { var length = RowCollection.Create().Import(rc1).Import(rc2).Length; rc1.Length = length; rc1.Settings.Border.Enabled = true; // enable border again rc1.Settings.Border.HorizontalLineBody = BorderConf.HorizontalLineAfterHeaderFunc; rc2.Length = length; rc2.Settings.Border.Enabled = true; CoEx.WriteTitleLarge("Tables - Chapter 3"); CoEx.WriteLine(); CoEx.WriteTitle("Synchonize column length over multiple tables"); CoEx.WriteLine(); CoEx.WriteTitle("The basic table, border header-only"); CoEx.WriteTable(rc1); CoEx.WriteLine(); CoEx.WriteTitle("The basic table with long text"); CoEx.WriteTable(rc2); }
public static void DemoGraph() { CoEx.WriteTitleLarge("Simple Graph"); CoEx.WriteLine(); var graph = new SimpleGraph() { Height = 10 }; var list = new List <double>() { 2, 1, 8, 16, 42, 0, 50, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 0, 10, 20, 30, 40, 50, 40, 30, 20, 10 }; var i = 0; var temp = DateTime.Now.Date; var dateList = list.ToDictionary(key => temp.AddDays(i++).ToString("ddMMM"), value => value); graph.Draw(dateList /*.Select(v => v * 42).ToList()*/ /*.OrderBy(v => v).ToList()*/); }
/// <summary> /// List existing aliases /// </summary> /// <param name="config">Config</param> /// <param name="args">CMD Args</param> /// <param name="provider">Provider</param> /// <returns>Exit Code</returns> private static async Task <int> ListAliases(Data.MailiasConfig config, Args args, Data.IProvider provider) { // Table header var header = new string[] { "Source", "Targets" }; // Convert the alias items into table rows var aliases = (await provider.GetAliases(config)) .Select(a => a.SourceAddress) .ToList(); if (aliases.Count > 0) { CoEx.WriteLine(string.Join(Environment.NewLine, aliases)); return(0); } else { // no aliases found return(1); } }
protected static void DemoPrompt() { Prompt prompt = new Prompt() { AllowEmpty = false, Prefix = "Choose your choice", Default = "okay", ValidateChoices = true, ChoicesText = new Dictionary <string, string>() { { "yes", "Yea, lets go" }, { "sure", "Sure, why not" }, { "okay", "Okay, go for it" }, { "yay", "Yay, kay, thx, bye" } } }; CoEx.WriteTitleLarge("Welcome, lets get started with a basic prompt"); CoEx.WriteLine(); prompt.DoPrompt(); }
/// <summary> /// Delete an existing alias /// </summary> /// <param name="config">Config</param> /// <param name="args">CMD Args</param> /// <param name="provider">Provider</param> /// <returns>Exit Code</returns> private static async Task <int> DeleteAlias(Data.MailiasConfig config, Args args, Data.IProvider provider) { var exists = (await provider.GetAliases(config)) .Any(alias => alias.SourceAddress == args.DeleteAlias); if (exists) { if (args.Force || YesNo("Really delete alias '" + args.DeleteAlias + "'?")) { var result = await provider.DeleteAliasAddress(args.DeleteAlias); if (result == Data.DeleteResult.Success) { return(0); } if (!args.Silent) { CoEx.WriteLine("Delete failed."); } return(1); } else { return(0); } } else { if (!args.Silent) { CoEx.WriteLine("Alias not found or didn't match the format of an alias which was created with this application."); } return(1); } }
/// <summary> /// The "Continue (Enter)" dialog /// </summary> private static void Continue() { CoEx.WriteLine(); CoEx.PressAnyKey(); CoEx.Clear(); }
static void Main(string[] args) { /** * Console defaults */ CoEx.WindowHeight = CoEx.WindowHeightMax - 10; CoEx.BufferHeight = 256; /** * Table defaults */ RowCollection.DefaultSettings.Border.Enabled = true; // enable bordering RowCollection.DefaultSettings.Border.HorizontalLineBody = BorderConf.HorizontalLineAlwaysOnFunc; // line between the rows header = RowConf.Create(headerdata).PresetTH(); rc1 = RowCollection.Create(exambledata); rc2 = RowCollection.Create(exambledatalong); /** * Demo parts */ Continue(); DemoLoadIndicator(); CoEx.Clear(); DemoGraph(); Continue(); CoEx.Clear(); DemoProgressBar(); CoEx.Clear(); DemoTimeout(); DemoScrolltext(); CoEx.Clear(); DemoProgressBarMessages(); CoEx.Clear(); DemoPrompt(); CoEx.Clear(); DemoIntro(); Continue(); DemoBasicColumns(); Continue(); DemoBasicTable(); Continue(); BasicTableLongText(); Continue(); DemoTableSynchronized(); Continue(); DemoConditionalStyles(); Continue(); DemoAlignment(); /** * The End */ CoEx.WriteLine(); CoEx.WriteHl("Program finished."); CoEx.Confirm("Exit?"); }
protected static void DemoConditionalStyles() { CoEx.WriteTitleLarge("Tables - Chapter 4"); CoEx.WriteLine(); CoEx.WriteTitle("Colorize cells by conditions"); // colorize column by index rc1.Settings.Color = delegate(RowConf me, int colindex, string s) { var temp = new ColorScheme(null, ConsoleColor.Blue); switch (colindex) { case 0: temp.Background = ConsoleColor.Cyan; break; case 1: temp.Background = ConsoleColor.White; break; case 2: temp.Background = ConsoleColor.Magenta; break; case 3: temp.Background = ConsoleColor.Red; break; default: temp.Background = ConsoleColor.Gray; break; } return(temp); }; // Highlight padding every second column rc1.Settings.IsHighlightPadding = delegate(RowConf me, int colindex, string s) { return((colindex % 2) == 0); }; CoEx.WriteLine(); CoEx.WriteTitle("Background color based on column index"); CoEx.WriteTable(rc1); // if hours>10 is column 2 yellow and column 0 red rc2.Settings.Color = delegate(RowConf me, int colindex, string s) { double hours; bool success = double.TryParse(me.Data[2], out hours); if (colindex == 2 && success && hours >= 10) { return(new ColorScheme(ConsoleColor.Yellow, ConsoleColor.Blue)); } else if (colindex == 0 && success && hours >= 10) { return(new ColorScheme(ConsoleColor.Red, null)); } else { return(null); } }; // highlight padding in column 0 rc2.Settings.IsHighlightPadding = delegate(RowConf me, int colindex, string s) { return(colindex == 0); }; CoEx.WriteLine(); CoEx.WriteTitle("Background color based on hours value"); CoEx.WriteTable(rc2); }
/// <summary> /// Create an alias /// </summary> /// <param name="config">Config</param> /// <param name="args">CMD Args</param> /// <param name="provider">Provider</param> /// <returns>Exit code</returns> private static async Task <int> CreateAlias(Data.MailiasConfig config, Args args, Data.IProvider provider) { // Create a new random id var chars = "abcdefghijklmnopqrstuvwxyz0123456789"; var builder = new StringBuilder(); var random = new Random(); while (builder.Length < config.UniqeIdLength) { var index = random.Next(0, chars.Length - 1); builder.Append(chars[index]); } // Build the email address var prefix = string.IsNullOrWhiteSpace(config.Prefix) ? "" : config.Prefix + "."; var key = builder.ToString(); var mailAddress = $"{prefix}{args.CreateName}.{key}@{config.MailDomain}"; // Check if the email address already exists var exists = (await provider.GetAliases(config)) .FirstOrDefault(a => a.SourceAddress.StartsWith($"{prefix}{args.CreateName}.")); if (exists != null && args.Force == false) { if (!YesNo($"There is already an alias ({exists.SourceAddress}) with this name. Proceed?")) { return(1); } } // Delete the existing alias if (exists != null && args.DeleteExisting) { if (args.Force || YesNo($"Dou you want do delete the existing alias '{exists.SourceAddress}', before creating a new one?")) { var deleteExistingResult = await provider.DeleteAliasAddress(exists.SourceAddress); if (deleteExistingResult != Data.DeleteResult.Success && !args.Silent) { CoEx.WriteLine("Deleting existing alias failed."); return(1); } } } // Create the new alias var result = await provider.CreateAlias(mailAddress, config.TargetAddress); if (result == Data.CreateResult.Success || result == Data.CreateResult.AlreadyExists) { CoEx.WriteLine(mailAddress); return(0); } else { if (!args.Silent) { CoEx.WriteLine("Creation failed."); } return(1); } }
private static void PrintStats(string path, string htmloutfile) { using (var thread = Load(path)) using (var writer = new CoExHtmlWriter() { Title = "Das Längste" }) { CoEx.WriteTitleLarge($"Statistics for {thread.StartpageUrl}"); CoEx.WriteLine(); CoEx.WriteLine(new string[] { "If you don't want to appear in this statistic, ", "just add the keyword 'donottrack' to your signature." }); CoEx.WriteLine(); //--> Filter functions bool filteroptoutpost(ThreadPost post) { return((post?.User?.SignatureHtml?.RemoveHtml().Contains("donottrack", StringComparison.InvariantCultureIgnoreCase) ?? false) == false); } bool filteroptoutuser(ForumUser user) { return((user?.SignatureHtml?.RemoveHtml().Contains("donottrack", StringComparison.InvariantCultureIgnoreCase) ?? false) == false); } //--> Default filters var cleanposts = thread.Posts.Where(v => filteroptoutpost(v)); var cleanusers = thread.Users.Users .Where(v => filteroptoutuser(v)) .Intersect(cleanposts.Select(u => u.User).Distinct()); //--> Current status var curstats = new string[][] { new string[] { "JSON file:", Path.GetFileName(path) }, new string[] { "JSON file size:", (new FileInfo(path)).Length.HumanBytes() }, new string[] { "Crawl date:", thread.CrawlTimestamp.ToString("yyyy-MM-dd HH:mm:ss") }, new string[] { "Post count:", cleanposts.Count().ToString("#,###") }, new string[] { "Users involved:", cleanusers.Count().ToString("#,###") }, new string[] { "Banned users:", cleanusers.Where(u => u.IsBanned).Count().ToString("#,###") }, new string[] { "Opt-Out users:", thread.Users.Users.Where(v => filteroptoutuser(v) == false).Count().ToString("#,##0") }, new string[] { "Total likes:", cleanposts.Sum(v => v.LikeCount).ToString("#,##0") }, new string[] { "Total dislikes:", cleanposts.Sum(v => v.DislikeCount).ToString("#,##0") }, new string[] { "Posts with likes:", cleanposts.Where(v => v.LikeCount > 0).Count().ToString("#,##0") }, new string[] { "Posts with dislikes:", cleanposts.Where(v => v.DislikeCount > 0).Count().ToString("#,##0") }, new string[] { "First post:", cleanposts.Min(v => v.Date).ToString("yyyy-MM-dd HH:mm:ss") }, new string[] { "Last post:", cleanposts.Max(v => v.Date).ToString("yyyy-MM-dd HH:mm:ss") }, new string[] { "Thread age:", (cleanposts.Max(v => v.Date) - cleanposts.Min(v => v.Date)).PrettyPrint() }, new string[] { "First user:"******"{v.Key.Username} ({v.Count()})").First() }, new string[] { "Newest user:"******"{v.Key.Username} ({v.Count()})").First() } }; CoEx.WriteTitle("Current status"); CoEx.WriteTable(RowCollection.Create(curstats)); CoEx.WriteLine(); //--> User statistics var userstats = cleanposts .Where(v => v.User != null) .GroupBy(v => v.User) .OrderByDescending(v => v.Count()) .Take(30) .Select(v => new string[] { v.Key.Username, v.Key.PostCount.ToString("#,###"), v.Count().ToString("#,###"), (100.0 * v.Count() / v.Key.PostCount).ToString("0.0") + "%", (100.0 * v.Count() / cleanposts.Count()).ToString("0.0") + "%", v.Min(j => j.Date).ToString("yyyy-MM-dd HH:mm"), v.Max(j => j.Date).ToString("yyyy-MM-dd HH:mm"), v.Key.MemberSince.ToString("yyyy-MM-dd") }) .Prepend(new string[] { "Username", "All Posts", "Thread Posts", "User %", "Thread %", "First Post", "Last Post", "Member since" }); var userrows = RowCollection.Create(userstats.ToArray()); userrows.Settings.Border.Enabled = true; userrows.Settings.Align = (conf, colidx, s) => { switch (colidx) { case 1: case 2: case 3: case 4: return(RowCollectionSettings.ALIGN.RIGHT); default: return(RowCollectionSettings.ALIGN.LEFT); } }; CoEx.WriteTitle("Statistics by user"); CoEx.WriteTable(userrows); CoEx.WriteLine(); //--> User like statistics var userlikes = cleanposts .Where(v => v.User != null) .GroupBy(v => v.User) .Where(v => v.Sum(j => j.LikeCount) > 0) .OrderByDescending(v => v.Sum(j => j.LikeCount)) .Take(10) .Select(v => new string[] { v.Key.Username, v.Sum(j => j.LikeCount).ToString("#,##0"), v.Where(j => j.LikeCount > 0).Min(j => j.Date).ToString("yyyy-MM-dd HH:mm"), v.Where(j => j.LikeCount > 0).Max(j => j.Date).ToString("yyyy-MM-dd HH:mm") }) .Prepend(new string[] { "Username", "Likes", "First like received", "Last like received" }); var userlikerows = RowCollection.Create(userlikes.ToArray()); userlikerows.Settings.Border.Enabled = true; userlikerows.Settings.Align = (conf, colidx, s) => { switch (colidx) { case 1: return(RowCollectionSettings.ALIGN.RIGHT); default: return(RowCollectionSettings.ALIGN.LEFT); } }; CoEx.WriteTitle("Statistics by likes"); CoEx.WriteTable(userlikerows); CoEx.WriteLine(); //--> User title statistics var titlestats = cleanposts .Where(v => v.User != null) .GroupBy(v => v.User.Title) .OrderByDescending(v => v.Count()) .Select(v => new string[] { v.Key, v.Count().ToString("#,###"), v.Min(j => j.Date).ToString("yyyy-MM-dd HH:mm"), v.Max(j => j.Date).ToString("yyyy-MM-dd HH:mm") }) .Prepend(new string[] { "Title", "Posts", "First Post", "Last Post" }); var titlerows = RowCollection.Create(titlestats.ToArray()); titlerows.Settings.Border.Enabled = true; titlerows.Settings.Align = (conf, colidx, s) => { switch (colidx) { case 1: return(RowCollectionSettings.ALIGN.RIGHT); default: return(RowCollectionSettings.ALIGN.LEFT); } }; CoEx.WriteTitle("Statistics by user title"); CoEx.WriteTable(titlerows); CoEx.WriteLine(); //--> Post likes var likeposts = cleanposts .Where(v => v.User != null) .OrderByDescending(v => v.LikeCount) .Take(10) .Select(v => new string[] { v.Date.ToString("yyyy-MM-dd"), v.LikeCount.ToString("#,###"), v.User.Username, v.Url }) .Prepend(new string[] { "Date", "Likes", "Author", "Post Url" }); var likepostrows = RowCollection.Create(likeposts.ToArray()); likepostrows.Settings.Border.Enabled = true; likepostrows.Settings.Align = (conf, colidx, s) => { switch (colidx) { case 1: return(RowCollectionSettings.ALIGN.RIGHT); default: return(RowCollectionSettings.ALIGN.LEFT); } }; CoEx.WriteTitle("Most liked posts"); CoEx.WriteTable(likepostrows); CoEx.WriteLine(); //--> Year statistics var yearmaxlength = cleanposts .GroupBy(v => v.Date.Year) .Select(v => v.Count().ToString().Length) .Max(); var yearstats = cleanposts .GroupBy(v => v.Date.Year) .Select(v => new string[] { v.Key.ToString(), v.Min(j => j.Date).ToString("MM-dd HH:mm"), v.Max(j => j.Date).ToString("MM-dd HH:mm"), v.GroupBy(j => j.User).Count().ToString("#,###"), v.Count().ToString("#,###"), string.Join(", ", v.GroupBy(j => j.User).OrderByDescending(j => j.Count()).Take(3).Select(j => $"{j.Key?.Username??"Guest"} ({j.Count()})")), v.Max(j => j.MessageHtml.RemoveHtml().Length).ToString("#,###") }) .Prepend(new string[] { "Year", "First Post", "Last Post", "Active", "Posts", "Top Users", "Largest Msg" }); var yeargraphstats = cleanposts .GroupBy(v => v.Date.Year) .Select(v => new dynamic[] { v.Key.ToString(), v.Count(), }) .OrderByDescending(v => (string)v[0]) .Take((int)((CoEx.ForcedBufferWidth.Value - yearmaxlength - 4) / 2)) .OrderBy(v => (string)v[0]) .ToDictionary(key => (string)key[0], value => (double)value[1]); var yearrows = RowCollection.Create(yearstats.ToArray()); yearrows.Settings.Border.Enabled = true; yearrows.Settings.Align = (conf, colidx, s) => { switch (colidx) { case 3: case 4: case 6: return(RowCollectionSettings.ALIGN.RIGHT); default: return(RowCollectionSettings.ALIGN.LEFT); } }; CoEx.WriteTitle("Statistics by year"); var graph = new SimpleGraph() { Height = 15 }; graph.Draw(yeargraphstats); CoEx.WriteLine(); CoEx.WriteTable(yearrows); CoEx.WriteLine(); //--> Month statistics var monthstats = cleanposts .GroupBy(v => v.Date.GetMonth()) .Select(v => new string[] { v.Key.ToString("yyyy-MM"), v.Min(j => j.Date).ToString("dd HH:mm"), v.Max(j => j.Date).ToString("dd HH:mm"), v.GroupBy(j => j.User).Count().ToString("#,###"), v.Count().ToString("#,###"), string.Join(", ", v.GroupBy(j => j.User).OrderByDescending(j => j.Count()).Take(3).Select(j => $"{j.Key?.Username??"Guest"} ({j.Count()})")), v.Max(j => j.MessageHtml.RemoveHtml().Length).ToString("#,###") }) .Prepend(new string[] { "Month", "First Post", "Last Post", "Active", "Posts", "Top Users", "Largest Msg" }); var monthmaxlength = cleanposts .GroupBy(v => v.Date.GetMonth()) .Select(v => v.Count().ToString().Length) .Max(); var monthgraphstats = cleanposts .GroupBy(v => v.Date.GetMonth()) .Select(v => new dynamic[] { v.Key.ToString("yyMM"), v.Count(), }) .OrderByDescending(v => (string)v[0]) .Take((int)((CoEx.ForcedBufferWidth.Value - monthmaxlength - 4) / 2)) .OrderBy(v => (string)v[0]) .ToDictionary(key => (string)key[0], value => (double)value[1]); var monthrows = RowCollection.Create(monthstats.ToArray()); monthrows.Settings.Border.Enabled = true; monthrows.Settings.Align = (conf, colidx, s) => { switch (colidx) { case 3: case 4: case 6: return(RowCollectionSettings.ALIGN.RIGHT); default: return(RowCollectionSettings.ALIGN.LEFT); } }; CoEx.WriteTitle("Statistics by month"); graph.Draw(monthgraphstats); CoEx.WriteLine(); CoEx.WriteTable(monthrows); CoEx.WriteLine(); writer.SaveAs(htmloutfile); } }