From 23658e478e077f80d7abe803c1ea3b1171446b62 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 6 Nov 2025 11:38:57 +0100 Subject: [PATCH 01/39] CharGrid --- CSharpBible/CharGrid/CharGrid.csproj | 112 +---- CSharpBible/CharGrid/Program.cs | 37 +- .../CharGrid/Properties/AssemblyInfo.cs | 7 - .../CharGrid/Properties/Resources.Designer.cs | 2 +- .../Visual/FrmCharGridMain.Designer.cs | 393 ------------------ .../CharGrid/Visual/FrmCharGridMain.cs | 33 -- .../CharGrid/Visual/FrmCharGridMain.resx | 123 ------ 7 files changed, 41 insertions(+), 666 deletions(-) delete mode 100644 CSharpBible/CharGrid/Visual/FrmCharGridMain.Designer.cs delete mode 100644 CSharpBible/CharGrid/Visual/FrmCharGridMain.cs delete mode 100644 CSharpBible/CharGrid/Visual/FrmCharGridMain.resx diff --git a/CSharpBible/CharGrid/CharGrid.csproj b/CSharpBible/CharGrid/CharGrid.csproj index e4ab464f1..7e8c47fb8 100644 --- a/CSharpBible/CharGrid/CharGrid.csproj +++ b/CSharpBible/CharGrid/CharGrid.csproj @@ -1,92 +1,22 @@ - - - - Debug - AnyCPU - {C9207149-1178-4D9E-818E-36C4DA31047C} - WinExe - CSharpBible.CharGrid - CharGrid - ..\..\bin\$(MSBuildProjectName)\ - ..\..\obj\$(MSBuildProjectName)\ - ..\..\obj\$(MSBuildProjectName)\ - v4.7.2 - 512 - true - true - - - - AnyCPU - true - full - false - ..\..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - ..\..\bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - Form - - - FrmCharGridMain.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - FrmCharGridMain.cs - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - + + + + WinExe + AnyCPU + {C9207149-1178-4D9E-818E-36C4DA31047C} + CSharpBible.CharGrid + net8.0-windows + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/CSharpBible/CharGrid/Program.cs b/CSharpBible/CharGrid/Program.cs index 78722a718..ec2f5c970 100644 --- a/CSharpBible/CharGrid/Program.cs +++ b/CSharpBible/CharGrid/Program.cs @@ -1,36 +1,37 @@ // *********************************************************************** -// Assembly : CharGrid -// Author : Mir -// Created : 12-19-2021 +// Assembly : CharGrid +// Author : Mir +// Created :12-19-2021 // -// Last Modified By : Mir -// Last Modified On : 02-29-2020 -// *********************************************************************** -// -// Copyright © JC-Soft 2020 -// -// +// Last Modified By : GitHub Copilot +// Last Modified On :2025-11-05 // *********************************************************************** using System; using System.Windows.Forms; -using CSharpBible.CharGrid.Visual; +using CSharpBible.CharGrid.Views; +using CSharpBible.CharGrid.Services; +using CSharpBible.CharGrid.ViewModels.Interfaces; +using CSharpBible.CharGrid.ViewModels; +using Microsoft.Extensions.DependencyInjection; namespace CSharpBible.CharGrid { - /// - /// Class Program. - /// static class Program { - /// - /// Der Haupteinstiegspunkt für die Anwendung. - /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new FrmCharGridMain()); + + var services = new ServiceCollection() + .AddSingleton() + .AddSingleton() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); + + Application.Run(services.GetRequiredService()); } } } diff --git a/CSharpBible/CharGrid/Properties/AssemblyInfo.cs b/CSharpBible/CharGrid/Properties/AssemblyInfo.cs index 2febf5445..0308a676b 100644 --- a/CSharpBible/CharGrid/Properties/AssemblyInfo.cs +++ b/CSharpBible/CharGrid/Properties/AssemblyInfo.cs @@ -17,12 +17,7 @@ // Allgemeine Informationen über eine Assembly werden über die folgenden // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die einer Assembly zugeordnet sind. -[assembly: AssemblyTitle("CharGrid")] [assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("JC-Soft")] -[assembly: AssemblyProduct("CharGrid")] -[assembly: AssemblyCopyright("Copyright © JC-Soft 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -44,5 +39,3 @@ // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, // indem Sie "*" wie unten gezeigt eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharpBible/CharGrid/Properties/Resources.Designer.cs b/CSharpBible/CharGrid/Properties/Resources.Designer.cs index e150e90bb..8a45b64cf 100644 --- a/CSharpBible/CharGrid/Properties/Resources.Designer.cs +++ b/CSharpBible/CharGrid/Properties/Resources.Designer.cs @@ -20,7 +20,7 @@ namespace CSharpBible.CharGrid.Properties { /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. /// // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // -Klasse über ein Tool wie ResGen oder Views Studio automatisch generiert. // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] diff --git a/CSharpBible/CharGrid/Visual/FrmCharGridMain.Designer.cs b/CSharpBible/CharGrid/Visual/FrmCharGridMain.Designer.cs deleted file mode 100644 index a52cb383c..000000000 --- a/CSharpBible/CharGrid/Visual/FrmCharGridMain.Designer.cs +++ /dev/null @@ -1,393 +0,0 @@ -// *********************************************************************** -// Assembly : CharGrid -// Author : Mir -// Created : 12-19-2021 -// -// Last Modified By : Mir -// Last Modified On : 12-24-2021 -// *********************************************************************** -// -// Copyright © JC-Soft 2020 -// -// -// *********************************************************************** -namespace CSharpBible.CharGrid.Visual -{ - /// - /// Class FrmCharGridMain. - /// Implements the - /// - /// - partial class FrmCharGridMain - { - /// - /// Erforderliche Designervariable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Verwendete Ressourcen bereinigen. - /// - /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Vom Windows Form-Designer generierter Code - - /// - /// Erforderliche Methode für die Designerunterstützung. - /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. - /// - private void InitializeComponent() - { - this.menuStrip1 = new System.Windows.Forms.MenuStrip(); - this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.pnlTop = new System.Windows.Forms.Panel(); - this.textBox1 = new System.Windows.Forms.TextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.checkBox1 = new System.Windows.Forms.CheckBox(); - this.lblFont = new System.Windows.Forms.Label(); - this.comboBox1 = new System.Windows.Forms.ComboBox(); - this.pnlBottom = new System.Windows.Forms.Panel(); - this.button4 = new System.Windows.Forms.Button(); - this.button3 = new System.Windows.Forms.Button(); - this.button2 = new System.Windows.Forms.Button(); - this.textBox2 = new System.Windows.Forms.TextBox(); - this.button1 = new System.Windows.Forms.Button(); - this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.textBox3 = new System.Windows.Forms.TextBox(); - this.menuStrip1.SuspendLayout(); - this.pnlTop.SuspendLayout(); - this.pnlBottom.SuspendLayout(); - this.tableLayoutPanel1.SuspendLayout(); - this.SuspendLayout(); - // - // menuStrip1 - // - this.menuStrip1.GripMargin = new System.Windows.Forms.Padding(2, 2, 0, 2); - this.menuStrip1.ImageScalingSize = new System.Drawing.Size(24, 24); - this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileToolStripMenuItem, - this.helpToolStripMenuItem}); - this.menuStrip1.Location = new System.Drawing.Point(0, 0); - this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(1183, 36); - this.menuStrip1.TabIndex = 0; - this.menuStrip1.Text = "menuStrip1"; - // - // fileToolStripMenuItem - // - this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.exitToolStripMenuItem}); - this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - this.fileToolStripMenuItem.Size = new System.Drawing.Size(54, 29); - this.fileToolStripMenuItem.Text = "&File"; - // - // exitToolStripMenuItem - // - this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; - this.exitToolStripMenuItem.Size = new System.Drawing.Size(141, 34); - this.exitToolStripMenuItem.Text = "E&xit"; - // - // helpToolStripMenuItem - // - this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.aboutToolStripMenuItem}); - this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; - this.helpToolStripMenuItem.Size = new System.Drawing.Size(65, 29); - this.helpToolStripMenuItem.Text = "&Help"; - // - // aboutToolStripMenuItem - // - this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; - this.aboutToolStripMenuItem.Size = new System.Drawing.Size(181, 34); - this.aboutToolStripMenuItem.Text = "&About ..."; - // - // pnlTop - // - this.pnlTop.Controls.Add(this.textBox1); - this.pnlTop.Controls.Add(this.label1); - this.pnlTop.Controls.Add(this.checkBox1); - this.pnlTop.Controls.Add(this.lblFont); - this.pnlTop.Controls.Add(this.comboBox1); - this.pnlTop.Dock = System.Windows.Forms.DockStyle.Top; - this.pnlTop.Location = new System.Drawing.Point(0, 36); - this.pnlTop.Name = "pnlTop"; - this.pnlTop.Size = new System.Drawing.Size(1183, 45); - this.pnlTop.TabIndex = 1; - // - // textBox1 - // - this.textBox1.Location = new System.Drawing.Point(480, 8); - this.textBox1.Name = "textBox1"; - this.textBox1.Size = new System.Drawing.Size(285, 26); - this.textBox1.TabIndex = 4; - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(410, 10); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(51, 20); - this.label1.TabIndex = 3; - this.label1.Text = "label1"; - // - // checkBox1 - // - this.checkBox1.AutoSize = true; - this.checkBox1.Location = new System.Drawing.Point(265, 10); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(113, 24); - this.checkBox1.TabIndex = 2; - this.checkBox1.Text = "checkBox1"; - this.checkBox1.UseVisualStyleBackColor = true; - // - // lblFont - // - this.lblFont.AutoSize = true; - this.lblFont.Location = new System.Drawing.Point(10, 10); - this.lblFont.Name = "lblFont"; - this.lblFont.Size = new System.Drawing.Size(46, 20); - this.lblFont.TabIndex = 1; - this.lblFont.Text = "Font:"; - // - // comboBox1 - // - this.comboBox1.FormattingEnabled = true; - this.comboBox1.Location = new System.Drawing.Point(65, 8); - this.comboBox1.Name = "comboBox1"; - this.comboBox1.Size = new System.Drawing.Size(183, 28); - this.comboBox1.TabIndex = 0; - // - // pnlBottom - // - this.pnlBottom.Controls.Add(this.button4); - this.pnlBottom.Controls.Add(this.button3); - this.pnlBottom.Controls.Add(this.button2); - this.pnlBottom.Controls.Add(this.textBox2); - this.pnlBottom.Controls.Add(this.button1); - this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom; - this.pnlBottom.Location = new System.Drawing.Point(0, 553); - this.pnlBottom.Name = "pnlBottom"; - this.pnlBottom.Size = new System.Drawing.Size(1183, 79); - this.pnlBottom.TabIndex = 2; - // - // button4 - // - this.button4.Location = new System.Drawing.Point(589, 10); - this.button4.Name = "button4"; - this.button4.Size = new System.Drawing.Size(110, 60); - this.button4.TabIndex = 4; - this.button4.Text = "button4"; - this.button4.UseVisualStyleBackColor = true; - // - // button3 - // - this.button3.Location = new System.Drawing.Point(398, 10); - this.button3.Name = "button3"; - this.button3.Size = new System.Drawing.Size(110, 60); - this.button3.TabIndex = 3; - this.button3.Text = "button3"; - this.button3.UseVisualStyleBackColor = true; - // - // button2 - // - this.button2.Location = new System.Drawing.Point(263, 10); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(110, 60); - this.button2.TabIndex = 2; - this.button2.Text = "button2"; - this.button2.UseVisualStyleBackColor = true; - // - // textBox2 - // - this.textBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.textBox2.Location = new System.Drawing.Point(10, 10); - this.textBox2.Name = "textBox2"; - this.textBox2.Size = new System.Drawing.Size(80, 53); - this.textBox2.TabIndex = 1; - // - // button1 - // - this.button1.Location = new System.Drawing.Point(110, 10); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(110, 60); - this.button1.TabIndex = 0; - this.button1.Text = "button1"; - this.button1.UseVisualStyleBackColor = true; - // - // tableLayoutPanel1 - // - this.tableLayoutPanel1.ColumnCount = 32; - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 35F)); - this.tableLayoutPanel1.Controls.Add(this.textBox3, 0, 0); - this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 81); - this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 8; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(1183, 472); - this.tableLayoutPanel1.TabIndex = 3; - // - // textBox3 - // - this.textBox3.Cursor = System.Windows.Forms.Cursors.Hand; - this.textBox3.Location = new System.Drawing.Point(3, 3); - this.textBox3.Name = "textBox3"; - this.textBox3.ReadOnly = true; - this.textBox3.Size = new System.Drawing.Size(47, 26); - this.textBox3.TabIndex = 0; - // - // FrmCharGridMain - // - this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1183, 632); - this.Controls.Add(this.tableLayoutPanel1); - this.Controls.Add(this.pnlBottom); - this.Controls.Add(this.pnlTop); - this.Controls.Add(this.menuStrip1); - this.MainMenuStrip = this.menuStrip1; - this.Name = "FrmCharGridMain"; - this.Text = "CharGrid"; - this.menuStrip1.ResumeLayout(false); - this.menuStrip1.PerformLayout(); - this.pnlTop.ResumeLayout(false); - this.pnlTop.PerformLayout(); - this.pnlBottom.ResumeLayout(false); - this.pnlBottom.PerformLayout(); - this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - /// - /// The menu strip1 - /// - private System.Windows.Forms.MenuStrip menuStrip1; - /// - /// The file tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; - /// - /// The exit tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; - /// - /// The help tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem; - /// - /// The about tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; - /// - /// The PNL top - /// - private System.Windows.Forms.Panel pnlTop; - /// - /// The text box1 - /// - private System.Windows.Forms.TextBox textBox1; - /// - /// The label1 - /// - private System.Windows.Forms.Label label1; - /// - /// The check box1 - /// - private System.Windows.Forms.CheckBox checkBox1; - /// - /// The label font - /// - private System.Windows.Forms.Label lblFont; - /// - /// The combo box1 - /// - private System.Windows.Forms.ComboBox comboBox1; - /// - /// The PNL bottom - /// - private System.Windows.Forms.Panel pnlBottom; - /// - /// The button4 - /// - private System.Windows.Forms.Button button4; - /// - /// The button3 - /// - private System.Windows.Forms.Button button3; - /// - /// The button2 - /// - private System.Windows.Forms.Button button2; - /// - /// The text box2 - /// - private System.Windows.Forms.TextBox textBox2; - /// - /// The button1 - /// - private System.Windows.Forms.Button button1; - /// - /// The table layout panel1 - /// - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; - /// - /// The text box3 - /// - private System.Windows.Forms.TextBox textBox3; - } -} - diff --git a/CSharpBible/CharGrid/Visual/FrmCharGridMain.cs b/CSharpBible/CharGrid/Visual/FrmCharGridMain.cs deleted file mode 100644 index d6e14c74d..000000000 --- a/CSharpBible/CharGrid/Visual/FrmCharGridMain.cs +++ /dev/null @@ -1,33 +0,0 @@ -// *********************************************************************** -// Assembly : CharGrid -// Author : Mir -// Created : 12-19-2021 -// -// Last Modified By : Mir -// Last Modified On : 12-24-2021 -// *********************************************************************** -// -// Copyright © JC-Soft 2020 -// -// -// *********************************************************************** -using System.Windows.Forms; - -namespace CSharpBible.CharGrid.Visual -{ - /// - /// Class FrmCharGridMain. - /// Implements the - /// - /// - public partial class FrmCharGridMain : Form - { - /// - /// Initializes a new instance of the class. - /// - public FrmCharGridMain() - { - InitializeComponent(); - } - } -} diff --git a/CSharpBible/CharGrid/Visual/FrmCharGridMain.resx b/CSharpBible/CharGrid/Visual/FrmCharGridMain.resx deleted file mode 100644 index 0f6d8eb42..000000000 --- a/CSharpBible/CharGrid/Visual/FrmCharGridMain.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - \ No newline at end of file From 97f0e3b9c8d7f0e41b2362220b66c3f889a93d95 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 6 Nov 2025 11:39:33 +0100 Subject: [PATCH 02/39] DataAnalysis.Core --- .../DataAnalysis.Core.csproj | 2 +- .../Export/ISyslogAnalysisExporter.cs | 16 -- .../Export/SyslogExcelExporter.cs | 207 +++++++++++------- .../Import/DelimitedTableParsingProfile.cs | 1 + .../Import/DelimitedTableReader.cs | 158 ++++++++----- .../Import/FieldExtractionRule.cs | 1 + .../Import/IDelimitedTableParsingProfile.cs | 20 -- .../Import/IFieldExtractionRule.cs | 13 -- .../DataAnalysis.Core/Import/ITableReader.cs | 8 - .../Models/AggregationResult.cs | 5 +- .../Models/AnalysisAggregateProfile.cs | 12 +- .../DataAnalysis.Core/Models/AnalysisModel.cs | 32 ++- .../DataAnalysis.Core/Models/AnalysisQuery.cs | 10 + .../Models/AnalysisResult.cs | 3 + .../DataAnalysis.Core/Models/DimensionKind.cs | 4 +- .../DataAnalysis.Core/Models/QueryBuilder.cs | 83 ++++++- .../Models/SyslogSeverity.cs | 19 +- .../Models/TableToEntryAdapter.cs | 1 + 18 files changed, 370 insertions(+), 225 deletions(-) delete mode 100644 CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/ISyslogAnalysisExporter.cs delete mode 100644 CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IDelimitedTableParsingProfile.cs delete mode 100644 CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IFieldExtractionRule.cs delete mode 100644 CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/ITableReader.cs diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj index 997818578..8307629de 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj @@ -2,7 +2,7 @@ Library - net9.0 + net9.0;net8.0 enable enable diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/ISyslogAnalysisExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/ISyslogAnalysisExporter.cs deleted file mode 100644 index 3c777e497..000000000 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/ISyslogAnalysisExporter.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using DataAnalysis.Core.Models; - -namespace DataAnalysis.Core.Export; - -/// -/// Abstraktion für den Export von Analyseergebnissen in beliebige Zielformate. -/// -public interface ISyslogAnalysisExporter -{ - /// - /// Exportiert das Analyseergebnis in ein Ziel (z. B. Datei) und liefert den Pfad/Bezeichner zurück. - /// - Task ExportAsync(AnalysisResult result, string inputPath, string? outputPath, CancellationToken cancellationToken); -} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs index 93d732e85..e9ee19979 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs @@ -1,9 +1,11 @@ using System; using System.IO; using System.Linq; +using System.Numerics; using System.Threading; using System.Threading.Tasks; using ClosedXML.Excel; +using DataAnalysis.Core.Export.Interfaces; using DataAnalysis.Core.Models; namespace DataAnalysis.Core.Export; @@ -13,97 +15,132 @@ namespace DataAnalysis.Core.Export; /// public sealed class SyslogExcelExporter : ISyslogAnalysisExporter { - public Task ExportAsync(AnalysisResult result, string inputPath, string? outputPath, CancellationToken cancellationToken) - { - outputPath ??= BuildDefaultOutputPath(inputPath); - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!); + public Task ExportAsync(AnalysisResult result, string inputPath, string? outputPath, CancellationToken cancellationToken) + { + outputPath ??= BuildDefaultOutputPath(inputPath); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!); - using var wb = new XLWorkbook(); + using var wb = new XLWorkbook(); - var wsInfo = wb.AddWorksheet("Übersicht"); - wsInfo.Cell(1, 1).Value = "Datei"; - wsInfo.Cell(1, 2).Value = Path.GetFileName(inputPath); - wsInfo.Cell(2, 1).Value = "Zeilen gesamt"; - wsInfo.Cell(2, 2).Value = result.TotalLines; - wsInfo.Cell(3, 1).Value = "Zeilen geparst"; - wsInfo.Cell(3, 2).Value = result.ParsedLines; - wsInfo.Cell(4, 1).Value = "Zeilen übersprungen"; - wsInfo.Cell(4, 2).Value = result.SkippedLines; - wsInfo.Cell(5, 1).Value = "Erster Zeitstempel"; - wsInfo.Cell(5, 2).Value = result.FirstTimestamp?.LocalDateTime; - wsInfo.Cell(6, 1).Value = "Letzter Zeitstempel"; - wsInfo.Cell(6, 2).Value = result.LastTimestamp?.LocalDateTime; - wsInfo.Columns().AdjustToContents(); + var wsInfo = wb.AddWorksheet("Übersicht"); + wsInfo.Cell(1,1).Value = "Datei"; + wsInfo.Cell(1,2).Value = Path.GetFileName(inputPath); + wsInfo.Cell(2,1).Value = "Zeilen gesamt"; + wsInfo.Cell(2,2).Value = result.TotalLines; + wsInfo.Cell(3,1).Value = "Zeilen geparst"; + wsInfo.Cell(3,2).Value = result.ParsedLines; + wsInfo.Cell(4,1).Value = "Zeilen übersprungen"; + wsInfo.Cell(4,2).Value = result.SkippedLines; + wsInfo.Cell(5,1).Value = "Erster Zeitstempel"; + wsInfo.Cell(5,2).Value = result.FirstTimestamp?.LocalDateTime; + wsInfo.Cell(6,1).Value = "Letzter Zeitstempel"; + wsInfo.Cell(6,2).Value = result.LastTimestamp?.LocalDateTime; + if (!string.IsNullOrWhiteSpace(result.GlobalFilterText)) + { + wsInfo.Cell(7,1).Value = "Globaler Filter"; + wsInfo.Cell(7,2).Value = result.GlobalFilterText; + wsInfo.Cell(7,2).Style.Font.Italic = true; + } + wsInfo.Columns().AdjustToContents(); - // Aggregationen exportieren - foreach (var agg in result.Aggregations) - { - var ws = wb.AddWorksheet(TrimSheetName(agg.Title)); - if (agg.Matrix is not null) - { - //2D Matrix: write headers from agg.Columns exactly as provided to preserve mapping - ws.Cell(1, 1).Value = "Schlüssel"; - var columns = agg.Columns ?? Array.Empty(); - for (int i = 0; i < columns.Count; i++) - ws.Cell(1, 2 + i).Value = columns[i]; + // Aggregationen exportieren + foreach (var agg in result.Aggregations) + { + var ws = wb.AddWorksheet(TrimSheetName(agg.Title)); + var hasLocalFilter = !string.IsNullOrWhiteSpace(agg.FilterText); + int headerRow = hasLocalFilter ?2 :1; - // Rotate column header text90° and center - if (columns.Count > 0) - { - var headerRange = ws.Range(1, 2, 1, 1 + columns.Count); - headerRange.Style.Alignment.TextRotation = 90; - headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; - headerRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center; - headerRange.Style.Font.Bold = true; - } + // Falls lokaler Filter vorhanden, Kommentarzeile oben einfügen + if (hasLocalFilter) + { + ws.Cell(1,1).Value = $"# Filter: {agg.FilterText}"; + ws.Cell(1,1).Style.Font.Italic = true; + ws.Cell(1,1).Style.Font.FontColor = XLColor.Gray; + } - int r = 2; - var keys = agg.Matrix.Keys.ToList(); - // Keep Summe as last row if present - keys.Sort((a, b) => string.Equals(a, "Summe", StringComparison.OrdinalIgnoreCase) ? 1 : string.Equals(b, "Summe", StringComparison.OrdinalIgnoreCase) ? -1 : string.Compare(a, b, StringComparison.OrdinalIgnoreCase)); - foreach (var rowKey in keys) - { - var row = agg.Matrix[rowKey]; - ws.Cell(r, 1).Value = rowKey; - for (int i = 0; i < columns.Count; i++) - { - var colKey = columns[i]; - ws.Cell(r, 2 + i).Value = row.TryGetValue(colKey, out var v) ? v : 0; - } - r++; - } - } - else if (agg.Series is not null) - { - //1D - ws.Cell(1, 1).Value = "Schlüssel"; - ws.Cell(1, 2).Value = "Anzahl"; - // Bold headers - ws.Range(1, 1, 1, 2).Style.Font.Bold = true; - int r = 2; - foreach (var kv in agg.Series.OrderByDescending(kv => kv.Value).ThenBy(kv => kv.Key)) - { ws.Cell(r, 1).Value = kv.Key; ws.Cell(r, 2).Value = kv.Value; r++; } - } - ws.Columns().AdjustToContents(); - } + if (agg.Matrix is not null) + { + //2D Matrix: write headers from agg.Columns exactly as provided to preserve mapping + ws.Cell(headerRow,1).Value = "Schlüssel"; + var cols = agg.Columns ?? Array.Empty(); + for (int i =0; i < cols.Count; i++) + ws.Cell(headerRow,2 + i).Value = cols[i]; - wb.SaveAs(outputPath); - return Task.FromResult(outputPath); - } + // Rotate column header text90° and center + if (cols.Count >0) + { + var headerRange = ws.Range(headerRow,2, headerRow,1 + cols.Count); + headerRange.Style.Alignment.TextRotation =90; + headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; + headerRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center; + headerRange.Style.Font.Bold = true; + } - private static string BuildDefaultOutputPath(string inputPath) - { - var dir = Path.GetDirectoryName(inputPath)!; - var name = Path.GetFileNameWithoutExtension(inputPath); - if (!name.EndsWith("_Auswertung", StringComparison.OrdinalIgnoreCase)) - name += "_Auswertung"; - return Path.Combine(dir, name + ".xlsx"); - } + int r = headerRow +1; + var keys = agg.Matrix.Keys.ToList(); + // Keep Summe as last row if present + keys.Sort((a, b) => string.Equals(a, "Summe", StringComparison.OrdinalIgnoreCase) ?1 : string.Equals(b, "Summe", StringComparison.OrdinalIgnoreCase) ? -1 : string.Compare(a, b, StringComparison.OrdinalIgnoreCase)); + foreach (var rowKey in keys) + { + var row = agg.Matrix[rowKey]; + ws.Cell(r,1).Value = rowKey; + for (int i =0; i < cols.Count; i++) + { + var colKey = cols[i]; + ws.Cell(r,2 + i).Value = row.TryGetValue(colKey, out var v) ? v :0; + } + r++; + } + } + else if (agg.Series is not null && agg.Dimensions.Count ==2) + { + //2D Punkte (Cluster) + ws.Cell(headerRow,1).Value = "X"; + ws.Cell(headerRow,2).Value = "Y"; + ws.Cell(headerRow,3).Value = "Anzahl"; + // Bold headers + ws.Range(headerRow,1, headerRow,3).Style.Font.Bold = true; + int r = headerRow +1; + foreach (var kv in agg.Series.OrderByDescending(kv => kv.Value)) + if (kv.Key is Vector2 p) + { + ws.Cell(r,1).Value = p.X; + ws.Cell(r,2).Value = p.Y; + ws.Cell(r,3).Value = kv.Value; + r++; + } + } + else if (agg.Series is not null) + { + //1D + ws.Cell(headerRow,1).Value = "Schlüssel"; + ws.Cell(headerRow,2).Value = "Anzahl"; + // Bold headers + ws.Range(headerRow,1, headerRow,2).Style.Font.Bold = true; + int r = headerRow +1; + foreach (var kv in agg.Series.OrderByDescending(kv => kv.Value).ThenBy(kv => kv.Key)) + { ws.Cell(r,1).Value = kv.Key.ToString(); ws.Cell(r,2).Value = kv.Value; r++; } + } + ws.Columns().AdjustToContents(); + } - private static string TrimSheetName(string name) - { - var invalid = Path.GetInvalidFileNameChars(); - var n = new string(name.Select(c => invalid.Contains(c) ? '_' : c).ToArray()); - return n.Length > 31 ? n[..31] : n; - } + wb.SaveAs(outputPath); + return Task.FromResult(outputPath); + } + + private static string BuildDefaultOutputPath(string inputPath) + { + var dir = Path.GetDirectoryName(inputPath)!; + var name = Path.GetFileNameWithoutExtension(inputPath); + if (!name.EndsWith("_Auswertung", StringComparison.OrdinalIgnoreCase)) + name += "_Auswertung"; + return Path.Combine(dir, name + ".xlsx"); + } + + private static string TrimSheetName(string name) + { + var invalid = Path.GetInvalidFileNameChars(); + var n = new string(name.Select(c => invalid.Contains(c) ? '_' : c).ToArray()); + return n.Length >31 ? n[..31] : n; + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs index 047961151..ba1847dde 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using DataAnalysis.Core.Import.Interfaces; using DataAnalysis.Core.Models; namespace DataAnalysis.Core.Import; diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs index 6ca8d29dc..fa3b3d76c 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs @@ -1,3 +1,7 @@ +using DataAnalysis.Core.Import.Interfaces; +using DataAnalysis.Core.Models; +using DocumentFormat.OpenXml.Drawing; +using DocumentFormat.OpenXml.Spreadsheet; using System; using System.Collections.Generic; using System.Data; @@ -9,7 +13,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using DataAnalysis.Core.Models; +using System.Xml.Linq; namespace DataAnalysis.Core.Import; @@ -28,38 +32,57 @@ public DelimitedTableReader(IDelimitedTableParsingProfile profile) public async Task ReadTableAsync(string inputPath, CancellationToken cancellationToken = default) { - if (string.IsNullOrWhiteSpace(inputPath)) throw new ArgumentException(ErrorPathEmpty, nameof(inputPath)); - if (!File.Exists(inputPath)) throw new FileNotFoundException(ErrorInputNotFound, inputPath); + if (string.IsNullOrWhiteSpace(inputPath)) + throw new ArgumentException(ErrorPathEmpty, nameof(inputPath)); + if (!File.Exists(inputPath)) + throw new FileNotFoundException(ErrorInputNotFound, inputPath); var dt = new DataTable(_profile.TableName); using var fs = new FileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using var reader = new StreamReader(fs, DetectEncoding(fs) ?? Encoding.UTF8, detectEncodingFromByteOrderMarks: true); // Header - Dictionary? headerMap = null; + Dictionary? headerMap = null; + var inclFields = new List(); + if (_profile.HasHeaderRow) { var headerLine = await reader.ReadLineAsync().ConfigureAwait(false); if (headerLine is not null) { var headers = SplitLine(headerLine, _profile.Delimiter, _profile.Quote, _profile.TrimWhitespace); - headerMap = headers.Select((h,i)=> new { Name = (h ?? string.Empty).Trim(), i}) - .GroupBy(x=>x.Name, StringComparer.OrdinalIgnoreCase) - .ToDictionary(g=>g.Key, g=>g.First().i, StringComparer.OrdinalIgnoreCase); + headerMap = headers.Select((h, i) => new { Name = (h ?? string.Empty).Trim(), i }) + .GroupBy(x => x.Name, StringComparer.OrdinalIgnoreCase) + .ToDictionary(g => g.Key, g => g.First().i, StringComparer.OrdinalIgnoreCase); + inclFields.AddRange(headerMap.Select((h, i) => i)); + foreach (var h in headerMap) + { + if (_profile.FixedColumns.Any((f) => f.Source == h.Key)) + inclFields.Remove(h.Value); + if (_profile.ExtractionRules.Any((r) => r.SourceColumn == h.Key)) + inclFields.Remove(h.Value); + } + } } // Ensure core columns exist foreach (var mapping in _profile.FixedColumns) { - EnsureColumn(dt, mapping.Target); + EnsureColumn(dt, mapping.Target, mapping.IsDateTime); + } + foreach (var i in inclFields) + { + var hmi = headerMap.Values.FirstOrDefault((v) => (v == i)); + EnsureColumn(dt, headerMap.Keys.ToArray()[hmi]); } string? line; while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) is not null) { cancellationToken.ThrowIfCancellationRequested(); - if (string.IsNullOrWhiteSpace(line)) continue; + if (string.IsNullOrWhiteSpace(line)) + continue; var fields = SplitLine(line, _profile.Delimiter, _profile.Quote, _profile.TrimWhitespace); // extraction rules @@ -77,19 +100,27 @@ public async Task ReadTableAsync(string inputPath, CancellationToken foreach (var mapping in _profile.FixedColumns) row[mapping.Target] = Extract(mapping, fields, headerMap, attributes); - + + foreach (var i in inclFields) + { + var hmi = headerMap.Values.FirstOrDefault((v) => (v == i)); + if (fields.Count>i) + row[headerMap.Keys.ToArray()[hmi]] = fields[i]; + } + foreach (var kv in attributes) - if (!_profile.FixedColumns.Any((f)=> f.Source == kv.Key)) - row[kv.Key] = kv.Value ?? string.Empty; + if (!_profile.FixedColumns.Any((f) => f.Source == kv.Key)) + row[kv.Key] = kv.Value ?? string.Empty; dt.Rows.Add(row); } return dt; } - private void ApplyExtractionRules(IReadOnlyList fields, Dictionary? headerMap, Dictionary attributes) + private void ApplyExtractionRules(IReadOnlyList fields, Dictionary? headerMap, Dictionary attributes) { - if (_profile.ExtractionRules.Count ==0) return; + if (_profile.ExtractionRules.Count == 0) + return; foreach (var rule in _profile.ExtractionRules) { var xFlag = true; @@ -115,13 +146,13 @@ private void ApplyExtractionRules(IReadOnlyList fields, Dictionary fields, Dictionary fields, Dictionary? headerMap, IReadOnlyDictionary? attributes = null) -{ - // Hilfsfunktionen - int? ResolveFieldIndex(string? selector) { - if (string.IsNullOrWhiteSpace(selector)) return null; - if (int.TryParse(selector, out var idx)) return idx; - if (headerMap is null) return null; - return headerMap.TryGetValue(selector, out var col) ? col : null; - } - - string? GetByIndex(int? idx) => idx is >= 0 && idx < fields.Count ? fields[idx.Value] : null; + // Hilfsfunktionen + int? ResolveFieldIndex(string? selector) + { + if (string.IsNullOrWhiteSpace(selector)) + return null; + if (int.TryParse(selector, out var idx)) + return idx; + if (headerMap is null) + return null; + return headerMap.TryGetValue(selector, out var col) ? col : null; + } - string? GetBySelector(string? selector, out int? usedIndex) - { - usedIndex = ResolveFieldIndex(selector); - if (usedIndex is not null) - return GetByIndex(usedIndex); + string? GetByIndex(int? idx) => idx is >= 0 && idx < fields.Count ? fields[idx.Value] : null; - if (!string.IsNullOrWhiteSpace(selector) && attributes is not null && attributes.TryGetValue(selector, out var value)) - return value; + string? GetBySelector(string? selector, out int? usedIndex) + { + usedIndex = ResolveFieldIndex(selector); + if (usedIndex is not null) + return GetByIndex(usedIndex); - return null; - } + if (!string.IsNullOrWhiteSpace(selector) && attributes is not null && attributes.TryGetValue(selector, out var value)) + return value; - // Werte über fields (Index/Header) ODER attributes extrahieren - var tsRaw = GetBySelector(mapping.Source, out var _); + return null; + } - // Normalisierung/Parsing - if (mapping.IsDateTime) - tsRaw = ParseTimestamp(tsRaw)?.ToString(TimestampFormatRoundTrip) ?? ""; - + // Werte über fields (Index/Header) ODER attributes extrahieren + var tsRaw = GetBySelector(mapping.Source, out var _); - return tsRaw; -} + return tsRaw; + } private DateTimeOffset? ParseTimestamp(string? raw) { - if (string.IsNullOrWhiteSpace(raw)) return null; + if (string.IsNullOrWhiteSpace(raw)) + return null; raw = raw.Trim().Trim('"').Trim('[', ']'); foreach (var cultureName in _profile.Cultures) { var culture = string.IsNullOrEmpty(cultureName) ? CultureInfo.InvariantCulture : CultureInfo.GetCultureInfo(cultureName); - if (_profile.TimestampFormats.Count >0 && DateTimeOffset.TryParseExact(raw, _profile.TimestampFormats.ToArray(), culture, DateTimeStyles.AssumeLocal, out var dto)) + if (_profile.TimestampFormats.Count > 0 && DateTimeOffset.TryParseExact(raw, _profile.TimestampFormats.ToArray(), culture, DateTimeStyles.AssumeLocal, out var dto)) + return dto; + if (DateTimeOffset.TryParse(raw, culture, DateTimeStyles.AssumeLocal, out dto)) return dto; - if (DateTimeOffset.TryParse(raw, culture, DateTimeStyles.AssumeLocal, out dto)) return dto; } return null; } @@ -195,14 +227,15 @@ private string Extract( var sb = new StringBuilder(); bool inQuotes = false; char q = quote ?? '\0'; - for (int i=0; i bom = stackalloc byte[4]; - fs.Position =0; + fs.Position = 0; int read = fs.Read(bom); fs.Position = original; - if (read >=3 && bom[0] ==0xEF && bom[1] ==0xBB && bom[2] ==0xBF) return Encoding.UTF8; - if (read >=2 && bom[0] ==0xFF && bom[1] ==0xFE) return Encoding.Unicode; - if (read >=2 && bom[0] ==0xFE && bom[1] ==0xFF) return Encoding.BigEndianUnicode; - if (read >=4 && bom[0] ==0xFF && bom[1] ==0xFE && bom[2] ==0x00 && bom[3] ==0x00) return Encoding.UTF32; - if (read >=4 && bom[0] ==0x00 && bom[1] ==0x00 && bom[2] ==0xFE && bom[3] ==0xFF) return new UTF32Encoding(bigEndian: true, byteOrderMark: true); + if (read >= 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) + return Encoding.UTF8; + if (read >= 2 && bom[0] == 0xFF && bom[1] == 0xFE) + return Encoding.Unicode; + if (read >= 2 && bom[0] == 0xFE && bom[1] == 0xFF) + return Encoding.BigEndianUnicode; + if (read >= 4 && bom[0] == 0xFF && bom[1] == 0xFE && bom[2] == 0x00 && bom[3] == 0x00) + return Encoding.UTF32; + if (read >= 4 && bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xFE && bom[3] == 0xFF) + return new UTF32Encoding(bigEndian: true, byteOrderMark: true); return null; } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs index d8b24580b..c762ef4a8 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs @@ -1,3 +1,4 @@ +using DataAnalysis.Core.Import.Interfaces; using System.Collections.Generic; using System.Text.RegularExpressions; diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IDelimitedTableParsingProfile.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IDelimitedTableParsingProfile.cs deleted file mode 100644 index b89fd4ed4..000000000 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IDelimitedTableParsingProfile.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace DataAnalysis.Core.Import; - -public interface IDelimitedTableParsingProfile -{ - IList Cultures { get; init; } - IList TimestampFormats { get; init; } - - // Fixed mappings: Source (name or index as string) -> Target field name - IList FixedColumns { get; init; } - - // Header/format settings - bool HasHeaderRow { get; init; } - string TableName { get; init; } - char Delimiter { get; init; } - char? Quote { get; init; } - bool TrimWhitespace { get; init; } - - // Extraction and mapping - IList ExtractionRules { get; init; } -} \ No newline at end of file diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IFieldExtractionRule.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IFieldExtractionRule.cs deleted file mode 100644 index 4bd7c2c55..000000000 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IFieldExtractionRule.cs +++ /dev/null @@ -1,13 +0,0 @@ - -using System.Text.RegularExpressions; - -namespace DataAnalysis.Core.Import; - -public interface IFieldExtractionRule -{ - string? SourceColumn { get; init; } - string RegexPattern { get; init; } - IDictionary? GroupMap { get; init; } - RegexOptions Options { get; init; } - bool Multible { get; } -} \ No newline at end of file diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/ITableReader.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/ITableReader.cs deleted file mode 100644 index d064739dc..000000000 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/ITableReader.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Data; - -namespace DataAnalysis.Core.Import; - -public interface ITableReader -{ - Task ReadTableAsync(string inputPath, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs index 8305362f4..cc4d97766 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs @@ -7,8 +7,11 @@ public sealed class AggregationResult public required string Title { get; init; } public required IReadOnlyList Dimensions { get; init; } + // Optional: Textuelle Beschreibung des lokalen Filters + public string? FilterText { get; init; } + // For1D aggregations - public IReadOnlyDictionary? Series { get; init; } + public IReadOnlyDictionary? Series { get; init; } // For2D aggregations public IReadOnlyList? Columns { get; init; } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs index a9d2d0c17..62d21f842 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs @@ -8,6 +8,9 @@ namespace DataAnalysis.Core.Models; /// public sealed class AnalysisAggregateProfile { + // Optional: globaler Filter, der für alle Queries gilt + public FilterDefinition? GlobalFilter { get; init; } + public required IReadOnlyList Queries { get; init; } public static AnalysisAggregateProfile Default => new() @@ -15,12 +18,13 @@ public sealed class AnalysisAggregateProfile Queries = new List { new AnalysisQuery { Title = "Severity", Dimensions = new [] { DimensionKind.Severity } }, - new AnalysisQuery { Title = "Source", Dimensions = new [] { DimensionKind.Source } }, - new AnalysisQuery { Title = "Hour x Source", Dimensions = new [] { DimensionKind.Hour, DimensionKind.Source } }, + new AnalysisQuery { Title = "Source", Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, Dimensions = new [] { DimensionKind.Source }, TopN =300 }, + new AnalysisQuery { Title = "Hour x Source", Dimensions = new [] { DimensionKind.Hour, DimensionKind.Source },Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, }, new AnalysisQuery { Title = "Top-Events", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =100 }, - new AnalysisQuery { Title = "Top-Events2", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =10 }, + new AnalysisQuery { Title = "Top-Events2", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =30, Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, }, new AnalysisQuery { Title = "Severity x Source", Dimensions = new [] { DimensionKind.Source, DimensionKind.Severity }, Columns = Enum.GetValues().Select(s => s.ToString()).ToArray() }, - new AnalysisQuery { Title = "Events x Source (Top50)", Dimensions = new [] { DimensionKind.Source, DimensionKind.MessageNormalized }}, + new AnalysisQuery { Title = "Events x Source (Top50)", Dimensions = new [] { DimensionKind.Source, DimensionKind.MessageNormalized }, TopN =300}, + new AnalysisQuery { Title = "Koordinate Cluster", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 30, IsDBScan = true, DbEps = 10.0, DbMinPts = 3, Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, } } }; } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs index ac130ac80..2d3d5e398 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs @@ -2,6 +2,7 @@ using System.Text; using System.Text.RegularExpressions; using DataAnalysis.Core.Export; +using DataAnalysis.Core.Export.Interfaces; using DataAnalysis.Core.Import; namespace DataAnalysis.Core.Models; @@ -33,6 +34,7 @@ public async Task AnalyzeAsync(string inputPath, CancellationTok ParsedLines = meta.ParsedLines, FirstTimestamp = meta.First, LastTimestamp = meta.Last, + GlobalFilterText = FilterTextFormatter.Describe(_profile.GlobalFilter), Aggregations = aggregations }; } @@ -68,12 +70,36 @@ private static IReadOnlyList BuildAggregations(SyslogReadResu { var list = new List(); if (profile.Queries.Count ==0) return list; - var builders = profile.Queries.Select(q => new QueryBuilder(q)).ToArray(); + + var globalPredicate = FilterCompiler.Compile(profile.GlobalFilter); + + var items = profile.Queries + .Select(q => + { + var qb = new QueryBuilder(q) { FilterText = FilterTextFormatter.Describe(q.Filter) }; + return new { Builder = qb, Predicate = Combine(globalPredicate, FilterCompiler.Compile(q.Filter)) }; + }) + .ToArray(); + foreach (var e in read.Entries) { - foreach (var b in builders) b.Observe(e); + foreach (var it in items) + { + if (it.Predicate is null || it.Predicate(e)) + { + it.Builder.Observe(e); + } + } } - foreach (var b in builders) list.Add(b.Build()); + + foreach (var it in items) list.Add(it.Builder.Build()); return list; } + + private static Func? Combine(Func? a, Func? b) + { + if (a is null) return b; + if (b is null) return a; + return e => a(e) && b(e); + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs index f88762c12..9d62fbf87 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs @@ -18,4 +18,14 @@ public sealed class AnalysisQuery // Optional: TopN pro Zeile für2D-Kreuztabellen public int? RowTopN { get; init; } + + // Optional: DBSCAN-Clustering der Punkte (bei2D X/Y) + public bool IsDBScan { get; init; } = false; + // Optional: Radius (Epsilon) für DBSCAN (gleiche Einheit wie X/Y) + public double? DbEps { get; init; } + // Optional: Mindestanzahl Punkte pro Cluster + public int? DbMinPts { get; init; } + + // Optional: Auswertungsspezifischer Filter (DTO, serialisierbar) + public FilterDefinition? Filter { get; init; } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs index cec05a54c..95454b74b 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs @@ -9,6 +9,9 @@ public sealed class AnalysisResult public DateTimeOffset? FirstTimestamp { get; init; } public DateTimeOffset? LastTimestamp { get; init; } + // Optional: Textuelle Beschreibung des globalen Filters + public string? GlobalFilterText { get; init; } + // Alle Auswertungen als Aggregationen public IReadOnlyList Aggregations { get; init; } = Array.Empty(); } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs index 60989b831..30f2a0758 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs @@ -5,5 +5,7 @@ public enum DimensionKind Severity, Source, Hour, // Hour bucket of timestamp - MessageNormalized + MessageNormalized, + X, + Y } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs index 3e95281ee..488353515 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs @@ -1,3 +1,8 @@ +using DataAnalysis.Core.Utilities; +using System.Drawing; +using System.Globalization; +using System.Numerics; + namespace DataAnalysis.Core.Models; internal sealed class QueryBuilder @@ -7,10 +12,19 @@ internal sealed class QueryBuilder private readonly Dictionary? _series; private readonly Dictionary>? _matrix; + // For DBSearch: collect raw points + private readonly List? _points; + + public string? FilterText { get; set; } + public QueryBuilder(AnalysisQuery q) { _q = q; - if (q.Dimensions.Count ==1) + if (q.IsDBScan && q.Dimensions.Count ==2 && q.Dimensions[0] == DimensionKind.X && q.Dimensions[1] == DimensionKind.Y) + { + _points = new List(); + } + else if (q.Dimensions.Count ==1) { _series = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -26,7 +40,11 @@ public QueryBuilder(AnalysisQuery q) public void Observe(SyslogEntry e) { - if (_series is not null) + if (_points is not null) + { + if (TryGetCoordinate(e, _q, out var pt)) _points.Add(pt); + } + else if (_series is not null) { var key = GetKey(e, _q.Dimensions[0]); if (key is null) return; @@ -44,6 +62,36 @@ public void Observe(SyslogEntry e) public AggregationResult Build() { + if (_points is not null) + { + var eps = _q.DbEps ??10.0; // default radius + var minPts = _q.DbMinPts ??3; + var clusters = DbScan.Cluster(_points, eps, minPts); + var series = new Dictionary(); + for (int i =0; i < clusters.Count; i++) + { + var c = clusters[i]; + if (c.Count ==0) continue; + float cx =0, cy =0; + foreach (var p in c) + { + cx += p.X; + cy += p.Y; + } + cx /= c.Count; cy /= c.Count; + var key =new Vector2(cx,cy); + series[key] = c.Count; + } + if (_q.TopN is int n) + { + series = series.OrderByDescending(kv => kv.Value).Take(n).ToDictionary(kv => kv.Key, kv => kv.Value); + } + return new AggregationResult { + Title = _q.Title, + Dimensions = _q.Dimensions, + FilterText = this.FilterText, + Series = series }; + } if (_series is not null) { var data = _series; @@ -51,7 +99,13 @@ public AggregationResult Build() { data = data.OrderByDescending(kv => kv.Value).ThenBy(kv => kv.Key).Take(n).ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.OrdinalIgnoreCase); } - return new AggregationResult { Title = _q.Title, Dimensions = _q.Dimensions, Series = data }; + return new AggregationResult + { + Title = _q.Title, + Dimensions = _q.Dimensions, + FilterText = this.FilterText, + Series = data.ToObjectKeyDictionary() + }; } else { @@ -86,7 +140,7 @@ public AggregationResult Build() [TotalRowKey] = totalRow }; - return new AggregationResult { Title = _q.Title, Dimensions = _q.Dimensions, Columns = columns, Matrix = finalMatrix }; + return new AggregationResult { Title = _q.Title, Dimensions = _q.Dimensions, FilterText = this.FilterText, Columns = columns, Matrix = finalMatrix }; } } @@ -98,8 +152,29 @@ public AggregationResult Build() DimensionKind.Source => string.IsNullOrWhiteSpace(e.Source) ? "(unbekannt)" : e.Source.Trim(), DimensionKind.Hour => e.Timestamp is DateTimeOffset ts ? new DateTimeOffset(ts.Year, ts.Month, ts.Day, ts.Hour,0,0, ts.Offset).LocalDateTime.ToString("yyyy-MM-dd HH:00") : null, DimensionKind.MessageNormalized => AnalysisModel.NormalizeMessage(e.Message), + DimensionKind.X => TryGetAttributeAsDouble(e, "X", out var x) ? x.ToString(CultureInfo.InvariantCulture) : null, + DimensionKind.Y => TryGetAttributeAsDouble(e, "Y", out var y) ? y.ToString(CultureInfo.InvariantCulture) : null, _ => null }; } + + private static bool TryGetCoordinate(SyslogEntry e, AnalysisQuery q, out Vector2 pt) + { + pt = default; + if (!TryGetAttributeAsDouble(e, "X", out var x)) return false; + if (!TryGetAttributeAsDouble(e, "Y", out var y)) return false; + pt = new Vector2((float)x, (float)y); + return true; + } + + private static bool TryGetAttributeAsDouble(SyslogEntry e, string key, out double value) + { + value =0; + if (e.Attributes.TryGetValue(key, out var s) && double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var d)) + { + value = d; return true; + } + return false; + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs index d0c5fbe93..160be6d57 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs @@ -2,13 +2,14 @@ namespace DataAnalysis.Core.Models; public enum SyslogSeverity { - - Trace = 7, - Debug = 6, - Notice = 5, - Info = 4, - Warn = 3, - Error = 1, - Fatal = 0, - Unknown = -1 + Trace = 8, + Debug = 7, + Notice = 5, + Info = 6, + Warn = 4, + Error = 3, + Critical = 2, + Alert = 1, + Fatal = 0, + Unknown = -1 } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs index 73fa12fa0..9abbe22a0 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using DataAnalysis.Core.Import; +using DataAnalysis.Core.Import.Interfaces; namespace DataAnalysis.Core.Models; From 3008bb6bd847673e7a706441f0fcba18e5818726 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 6 Nov 2025 11:39:34 +0100 Subject: [PATCH 03/39] DataAnalysis.WPF --- .../DataAnalysis/DataAnalysis.WPF/App.xaml.cs | 6 ++- .../ViewModels/AnalysisResultViewModel.cs | 9 +++- .../ViewModels/MainViewModel.cs | 6 +-- .../ViewModels/MatrixAggregationViewModel.cs | 4 +- .../ViewModels/SeriesAggregationViewModel.cs | 9 +++- .../Views/Controls/MatrixAggregationView.xaml | 23 ++++++++- .../Controls/MatrixAggregationView.xaml.cs | 8 +-- .../Views/Controls/SeriesAggregationView.xaml | 49 +++++++------------ .../Controls/SeriesAggregationView.xaml.cs | 8 +-- .../DataAnalysis.WPF/Views/MainWindow.xaml | 46 +++++++---------- .../DataAnalysis.WPF/Views/MainWindow.xaml.cs | 8 +-- 11 files changed, 95 insertions(+), 81 deletions(-) diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/App.xaml.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/App.xaml.cs index 41ddae3d1..66740d660 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/App.xaml.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/App.xaml.cs @@ -5,6 +5,8 @@ using DataAnalysis.Core.Models; using DataAnalysis.Core.Export; using DataAnalysis.Core.Import; +using DataAnalysis.Core.Export.Interfaces; +using DataAnalysis.Core.Import.Interfaces; namespace DataAnalysis.WPF; @@ -33,12 +35,12 @@ public App() { new FixedColumnMapping { Source = "eventdatetime", Target = "Timestamp" , IsDateTime = true}, new FixedColumnMapping { Source = "eventlevel", Target = "Severity" }, - new FixedColumnMapping { Source = "eventhostname", Target = "Source" }, + new FixedColumnMapping { Source = "eventhostname", Target = "Source" }, new FixedColumnMapping { Source = "EventName", Target = "Message" }, }, ExtractionRules = { - new FieldExtractionRule { SourceColumn = "eventmessage", RegexPattern = "-\\s*(?.+?)(?X=.*|;.*|$)" }, + new FieldExtractionRule { SourceColumn = "eventmessage", RegexPattern = "^(.+? +- +)?(?.+?)(?X=.*|;.*|$)" }, new FieldExtractionRule { SourceColumn = "Rest", RegexPattern = "(?[A-Za-z][A-Za-z0-9_]*)=(?[^;]*)(;(?.*)|$)", Multible=true } } }); diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/AnalysisResultViewModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/AnalysisResultViewModel.cs index dd780cd9c..5fa9c6e61 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/AnalysisResultViewModel.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/AnalysisResultViewModel.cs @@ -14,7 +14,14 @@ public AnalysisResultViewModel(AnalysisResult result) foreach (var agg in result.Aggregations) { if (agg.Series is not null) - Aggregations.Add(new SeriesAggregationViewModel(agg)); + { + // decide if this is cluster (DbScan.Vector2 keys) or normal series + var firstKey = agg.Series.Keys.FirstOrDefault(); + if (firstKey is not null && firstKey.GetType().Name.Contains("Vector2")) + Aggregations.Add(new ClusterAggregationViewModel(agg)); + else + Aggregations.Add(new SeriesAggregationViewModel(agg)); + } else if (agg.Matrix is not null) Aggregations.Add(new MatrixAggregationViewModel(agg)); } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MainViewModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MainViewModel.cs index f8af71ef8..357ab54f0 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MainViewModel.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MainViewModel.cs @@ -6,10 +6,10 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using DataAnalysis.Core.Models; -using DataAnalysis.Core.Import; using Microsoft.Win32; using DataAnalysis.WPF.Properties; -using DataAnalysis.Core.Export; +using DataAnalysis.Core.Export.Interfaces; +using DataAnalysis.Core.Import.Interfaces; namespace DataAnalysis.WPF.ViewModels; @@ -73,7 +73,7 @@ private async Task Preview() try { var table = await _tableReader.ReadTableAsync(InputPath!, CancellationToken.None).ConfigureAwait(false); - var limited = table.AsEnumerable().Take(200).CopyToDataTableOrEmpty(); + var limited = table.AsEnumerable().CopyToDataTableOrEmpty(); await App.Current.Dispatcher.InvokeAsync(() => { PreviewTable = limited.DefaultView; }); Status = string.Format(Resources.StatusPreviewFmt, limited.Rows.Count); } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs index d63c41efc..d5caeeaed 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs @@ -1,4 +1,4 @@ -using System.Data; +using System.Data; using CommunityToolkit.Mvvm.ComponentModel; using DataAnalysis.Core.Models; @@ -15,7 +15,7 @@ public MatrixAggregationViewModel(AggregationResult agg) var dt = new DataTable(); dt.Columns.Add("Key", typeof(string)); var columns = agg.Columns ?? Array.Empty(); - foreach (var col in columns) dt.Columns.Add(col, typeof(int)); + foreach (var col in columns) dt.Columns.Add(col.Replace(".", "˳"), typeof(int)); if (agg.Matrix is not null) { var rows = agg.Matrix.Keys.ToList(); diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/SeriesAggregationViewModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/SeriesAggregationViewModel.cs index 037852b71..9327dd8e2 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/SeriesAggregationViewModel.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/SeriesAggregationViewModel.cs @@ -10,6 +10,7 @@ public sealed class Item { public string Category { get; init; } = string.Empty; public int Value { get; init; } + public double Percentage { get; init; } //0..100 } public ObservableCollection Items { get; } = new(); @@ -30,15 +31,21 @@ private void Rebuild(AggregationResult agg) { Items.Clear(); int max = 0; + var temp = new List(); if (agg.Series is not null) { foreach (var kv in agg.Series) { - Items.Add(new Item { Category = kv.Key, Value = kv.Value }); + temp.Add(new Item { Category = kv.Key.ToString(), Value = kv.Value }); if (kv.Value > max) max = kv.Value; } } MaxValue = max; BarScale = max > 0 ? 300.0 / max : 1.0; + foreach (var it in temp) + { + var pct = max > 0 ? (double)it.Value / max * 100.0 : 0.0; + Items.Add(new Item { Category = it.Category, Value = it.Value, Percentage = pct }); + } } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml index 86c75370a..6b2d18f9e 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml @@ -4,12 +4,33 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> + + + - + + + \ No newline at end of file diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml.cs index 26d2c3fea..122f7f73c 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/MatrixAggregationView.xaml.cs @@ -4,8 +4,8 @@ namespace DataAnalysis.WPF.Views.Controls; public partial class MatrixAggregationView : UserControl { - public MatrixAggregationView() - { - InitializeComponent(); - } + public MatrixAggregationView() + { + InitializeComponent(); + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml index e546fefe8..e65fdec40 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml @@ -1,33 +1,20 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="clr-namespace:DataAnalysis.WPF.Views.Controls" + mc:Ignorable="d"> + + + + + + + + + + + + \ No newline at end of file diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml.cs index ffdf93abe..d0432d923 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/SeriesAggregationView.xaml.cs @@ -4,8 +4,8 @@ namespace DataAnalysis.WPF.Views.Controls; public partial class SeriesAggregationView : UserControl { - public SeriesAggregationView() - { - InitializeComponent(); - } + public SeriesAggregationView() + { + InitializeComponent(); + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml index 069493db8..7fd735720 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml @@ -15,6 +15,9 @@ + + + @@ -33,36 +36,23 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml.cs index ee6349c4a..c2ec0bb05 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/MainWindow.xaml.cs @@ -4,8 +4,8 @@ namespace DataAnalysis.WPF.Views; public partial class MainWindow : Window { - public MainWindow() - { - InitializeComponent(); - } + public MainWindow() + { + InitializeComponent(); + } } From d27b07910ebdbe522210ddfae3690f4259784761 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 6 Nov 2025 11:39:34 +0100 Subject: [PATCH 04/39] DataAnalysis.WPF.TestHarness --- .../MainWindow.xaml | 3 ++- .../MainWindow.xaml.cs | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml index a7ab8521e..284034174 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml @@ -11,7 +11,8 @@