From e5dbf4e1cc263cdc719859d12579dd9286ea90ad Mon Sep 17 00:00:00 2001 From: James Friel Date: Tue, 11 Feb 2025 13:47:07 +0000 Subject: [PATCH] add secondary constraint --- .../Data/PrimaryContraint.cs | 6 +- .../Data/SecondaryConstraint.cs | 71 ++++++++++ .../Data/SecondaryConstraintArgument.cs | 47 +++++++ .../up/091_AddAnalysisTools.sql | 31 +++++ ...ogueAnalysisExecutionControlUI.Designer.cs | 131 +++++++++++++++++- .../CatalogueAnalysisExecutionControlUI.cs | 43 +++++- .../CatalogueAnalysisExecutionControlUI.resx | 24 +++- 7 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraint.cs create mode 100644 Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraintArgument.cs diff --git a/Rdmp.Core/CatalogueAnalysisTools/Data/PrimaryContraint.cs b/Rdmp.Core/CatalogueAnalysisTools/Data/PrimaryContraint.cs index 8c320a52ed..bde490871f 100644 --- a/Rdmp.Core/CatalogueAnalysisTools/Data/PrimaryContraint.cs +++ b/Rdmp.Core/CatalogueAnalysisTools/Data/PrimaryContraint.cs @@ -49,18 +49,18 @@ public PrimaryContraint(DQERepository repository, DbDataReader r): base(reposito _result = (ConstraintResults)int.Parse(r["Result"].ToString()); } - public PrimaryContraint(DQERepository repository, ColumnInfo columnInfo, Contraints contraint, ConstraintResults result) + public PrimaryContraint(DQERepository repository, ColumnInfo columnInfo, Contraints constraint, ConstraintResults result) { _DQERepository = repository; _columnInfo = columnInfo; - _constraint = contraint; + _constraint = constraint; _result = result; _DQERepository.InsertAndHydrate(this, new Dictionary { { "ColumnInfo_ID", columnInfo.ID }, - { "Constraint", (int)contraint}, + { "Constraint", (int)constraint}, { "Result", (int)result} }); } diff --git a/Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraint.cs b/Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraint.cs new file mode 100644 index 0000000000..fa94f328d5 --- /dev/null +++ b/Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraint.cs @@ -0,0 +1,71 @@ +using Rdmp.Core.Curation.Data; +using Rdmp.Core.MapsDirectlyToDatabaseTable; +using Rdmp.Core.Repositories; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static Rdmp.Core.CatalogueAnalysisTools.Data.PrimaryContraint; + +namespace Rdmp.Core.CatalogueAnalysisTools.Data +{ + public class SecondaryConstraint: DatabaseEntity + { + public enum Constraints + { + BOUND, + BOUNDDATE, + NOTNULL, + PREDICTION, + REFERENTIALINTEGRITYCONSTRAINT, + REGULAREXPRESSION + } + + public enum Consequences + { + WRONG, + MISSING, + INVALIDATESROW + } + + private DQERepository _DQERepository { get; set; } + private ColumnInfo _columnInfo; + private Constraints _constraint; + private Consequences _consequence; + private SecondaryConstraintArgument[] _arguments; + + public ColumnInfo ColumnInfo { get => _columnInfo; private set => SetField(ref _columnInfo, value); } + public Consequences Consequence { get => _consequence; set => SetField(ref _consequence, value); } + public Constraints Constraint{ get => _constraint; set => SetField(ref _constraint, value); } + + public SecondaryConstraintArgument[] Arguments { get => _arguments; set => SetField(ref _arguments, value); } + + public SecondaryConstraint() { } + + public SecondaryConstraint(DQERepository repository, DbDataReader r) : base(repository, r) + { + _DQERepository = repository; + _columnInfo = _DQERepository.CatalogueRepository.GetObjectByID(int.Parse(r["ColumnInfo_ID"].ToString())); + _constraint = (Constraints)int.Parse(r["Constraint"].ToString()); + _consequence = (Consequences)int.Parse(r["Consequence"].ToString()); + _arguments = _DQERepository.GetAllObjectsWhere("SecondaryConstraint_ID", int.Parse(r["ID"].ToString())); + } + + public SecondaryConstraint(DQERepository repository, ColumnInfo columnInfo, Constraints constraint, Consequences consequence) { + _DQERepository = repository; + _columnInfo = columnInfo; + _consequence = consequence; + _constraint = constraint; + _DQERepository.InsertAndHydrate(this, new Dictionary + { + { "ColumnInfo_ID", columnInfo.ID }, + { "Constraint", (int)constraint}, + { "Consequence", (int)consequence} + }); + + } + } +} diff --git a/Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraintArgument.cs b/Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraintArgument.cs new file mode 100644 index 0000000000..dac36f9486 --- /dev/null +++ b/Rdmp.Core/CatalogueAnalysisTools/Data/SecondaryConstraintArgument.cs @@ -0,0 +1,47 @@ +using Rdmp.Core.Curation.Data; +using Rdmp.Core.Repositories; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Rdmp.Core.CatalogueAnalysisTools.Data +{ + public class SecondaryConstraintArgument: DatabaseEntity + { + + private string _key; + private string _value; + private DQERepository _repository; + private SecondaryConstraint _constraint; + + public string Key { get => _key; set => SetField(ref _key, value); } + public string Value { get => _value; set => SetField(ref _value, value); } + public SecondaryConstraint Constraint { get => _constraint; private set => SetField(ref _constraint, value); } + + public SecondaryConstraintArgument(DQERepository repository, DbDataReader r): base(repository, r) + { + _repository = repository; + _key = r["Key"].ToString(); + _value = r["Value"].ToString(); + _constraint = _repository.GetObjectByID(int.Parse(r["SecondaryConstraint_ID"].ToString())); + } + + public SecondaryConstraintArgument(DQERepository repository, string key, string value, SecondaryConstraint constraint) + { + _repository = repository; + _key = key; + _value = value; + _constraint = constraint; + + _repository.InsertAndHydrate(this, new Dictionary + { + {"Key",key }, + {"Value",value }, + {"SecondaryConstraint_ID",constraint.ID } + }); + } + } +} diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/091_AddAnalysisTools.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/091_AddAnalysisTools.sql index cf223e942e..2c75f25c93 100644 --- a/Rdmp.Core/Databases/CatalogueDatabase/up/091_AddAnalysisTools.sql +++ b/Rdmp.Core/Databases/CatalogueDatabase/up/091_AddAnalysisTools.sql @@ -51,4 +51,35 @@ CONSTRAINT [PK_CatalogueValidationResult] PRIMARY KEY CLUSTERED )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] END +GO + +if not exists (select 1 from sys.tables where name = 'SecondaryConstraint') +BEGIN +CREATE TABLE [dbo].[SecondaryConstraint]( + [ID] [int] IDENTITY(1,1) NOT NULL, + [ColumnInfo_ID] [int] NOT NULL, + [Constraint] [int] NOT NULL, + [Consequence] [int] NOT NULL, +CONSTRAINT [PK_SecondaryConstraint] PRIMARY KEY CLUSTERED +( + [ID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +END +GO + +if not exists (select 1 from sys.tables where name = 'SecondaryConstraintArgument') +BEGIN +CREATE TABLE [dbo].[SecondaryConstraint]( + [ID] [int] IDENTITY(1,1) NOT NULL, + [SecondaryConstraint_ID] [int] NOT NULL, + [Key] [nvarchar](max) NOT NULL, + [Value] [nvarchar](max) NOT NULL, + FOREIGN KEY ([SecondaryConstraint_ID]) REFERENCES [dbo].[SecondaryConstraint_ID](ID) ON DELETE CASCADE, +CONSTRAINT [PK_SecondaryConstraintArgument] PRIMARY KEY CLUSTERED +( + [ID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +END GO \ No newline at end of file diff --git a/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.Designer.cs b/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.Designer.cs index 69fbb9388b..bb6bde47f0 100644 --- a/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.Designer.cs +++ b/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.Designer.cs @@ -33,6 +33,13 @@ private void InitializeComponent() tabPage1 = new System.Windows.Forms.TabPage(); timePeriodicityChart1 = new CatalogueSummary.DataQualityReporting.TimePeriodicityChart(); tabPage2 = new System.Windows.Forms.TabPage(); + groupBox4 = new System.Windows.Forms.GroupBox(); + pictureBox1 = new System.Windows.Forms.PictureBox(); + SecondaryConstrainsTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + label7 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label5 = new System.Windows.Forms.Label(); + label6 = new System.Windows.Forms.Label(); button1 = new System.Windows.Forms.Button(); groupBox3 = new System.Windows.Forms.GroupBox(); cbPivot = new System.Windows.Forms.ComboBox(); @@ -44,9 +51,13 @@ private void InitializeComponent() label3 = new System.Windows.Forms.Label(); label2 = new System.Windows.Forms.Label(); label1 = new System.Windows.Forms.Label(); + button2 = new System.Windows.Forms.Button(); tabControl1.SuspendLayout(); tabPage1.SuspendLayout(); tabPage2.SuspendLayout(); + groupBox4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit(); + SecondaryConstrainsTableLayoutPanel.SuspendLayout(); groupBox3.SuspendLayout(); groupBox2.SuspendLayout(); groupBox1.SuspendLayout(); @@ -84,6 +95,7 @@ private void InitializeComponent() // // tabPage2 // + tabPage2.Controls.Add(groupBox4); tabPage2.Controls.Add(button1); tabPage2.Controls.Add(groupBox3); tabPage2.Controls.Add(groupBox2); @@ -96,11 +108,94 @@ private void InitializeComponent() tabPage2.Text = "Configuration"; tabPage2.UseVisualStyleBackColor = true; // + // groupBox4 + // + groupBox4.AutoSize = true; + groupBox4.Controls.Add(button2); + groupBox4.Controls.Add(pictureBox1); + groupBox4.Controls.Add(SecondaryConstrainsTableLayoutPanel); + groupBox4.Location = new System.Drawing.Point(453, 6); + groupBox4.Name = "groupBox4"; + groupBox4.Size = new System.Drawing.Size(431, 100); + groupBox4.TabIndex = 5; + groupBox4.TabStop = false; + groupBox4.Text = "Secondary Constraints"; + // + // pictureBox1 + // + pictureBox1.Cursor = System.Windows.Forms.Cursors.Hand; + pictureBox1.Image = (System.Drawing.Image)resources.GetObject("pictureBox1.Image"); + pictureBox1.Location = new System.Drawing.Point(130, -1); + pictureBox1.Name = "pictureBox1"; + pictureBox1.Size = new System.Drawing.Size(22, 21); + pictureBox1.TabIndex = 6; + pictureBox1.TabStop = false; + pictureBox1.Click += pictureBox1_Click; + // + // SecondaryConstrainsTableLayoutPanel + // + SecondaryConstrainsTableLayoutPanel.AutoSize = true; + SecondaryConstrainsTableLayoutPanel.ColumnCount = 4; + SecondaryConstrainsTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + SecondaryConstrainsTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + SecondaryConstrainsTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + SecondaryConstrainsTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + SecondaryConstrainsTableLayoutPanel.Controls.Add(label7, 3, 0); + SecondaryConstrainsTableLayoutPanel.Controls.Add(label4, 2, 0); + SecondaryConstrainsTableLayoutPanel.Controls.Add(label5, 1, 0); + SecondaryConstrainsTableLayoutPanel.Controls.Add(label6, 0, 0); + SecondaryConstrainsTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Top; + SecondaryConstrainsTableLayoutPanel.Location = new System.Drawing.Point(3, 19); + SecondaryConstrainsTableLayoutPanel.Name = "SecondaryConstrainsTableLayoutPanel"; + SecondaryConstrainsTableLayoutPanel.RowCount = 2; + SecondaryConstrainsTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50F)); + SecondaryConstrainsTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + SecondaryConstrainsTableLayoutPanel.Size = new System.Drawing.Size(425, 50); + SecondaryConstrainsTableLayoutPanel.TabIndex = 1; + SecondaryConstrainsTableLayoutPanel.Paint += tableLayoutPanel1_Paint; + // + // label7 + // + label7.AutoSize = true; + label7.Location = new System.Drawing.Point(227, 0); + label7.Name = "label7"; + label7.Size = new System.Drawing.Size(66, 15); + label7.TabIndex = 5; + label7.Text = "Arguments"; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(127, 0); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(94, 15); + label4.TabIndex = 4; + label4.Text = "Validation Result"; + // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(59, 0); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(62, 15); + label5.TabIndex = 3; + label5.Text = "Constraint"; + label5.Click += label5_Click; + // + // label6 + // + label6.AutoSize = true; + label6.Location = new System.Drawing.Point(3, 0); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(50, 15); + label6.TabIndex = 2; + label6.Text = "Column"; + // // button1 // button1.Image = (System.Drawing.Image)resources.GetObject("button1.Image"); button1.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; - button1.Location = new System.Drawing.Point(787, 3); + button1.Location = new System.Drawing.Point(1103, 116); button1.Name = "button1"; button1.Size = new System.Drawing.Size(121, 42); button1.TabIndex = 4; @@ -111,7 +206,7 @@ private void InitializeComponent() // groupBox3 // groupBox3.Controls.Add(cbPivot); - groupBox3.Location = new System.Drawing.Point(453, 61); + groupBox3.Location = new System.Drawing.Point(890, 61); groupBox3.Name = "groupBox3"; groupBox3.Size = new System.Drawing.Size(334, 49); groupBox3.TabIndex = 3; @@ -129,7 +224,7 @@ private void InitializeComponent() // groupBox2 // groupBox2.Controls.Add(cbTime); - groupBox2.Location = new System.Drawing.Point(453, 6); + groupBox2.Location = new System.Drawing.Point(890, 6); groupBox2.Name = "groupBox2"; groupBox2.Size = new System.Drawing.Size(334, 49); groupBox2.TabIndex = 2; @@ -191,7 +286,7 @@ private void InitializeComponent() // label3 // label3.AutoSize = true; - label3.Location = new System.Drawing.Point(171, 0); + label3.Location = new System.Drawing.Point(127, 0); label3.Name = "label3"; label3.Size = new System.Drawing.Size(94, 15); label3.TabIndex = 4; @@ -202,9 +297,9 @@ private void InitializeComponent() label2.AutoSize = true; label2.Location = new System.Drawing.Point(59, 0); label2.Name = "label2"; - label2.Size = new System.Drawing.Size(106, 15); + label2.Size = new System.Drawing.Size(62, 15); label2.TabIndex = 3; - label2.Text = "Primary Constraint"; + label2.Text = "Constraint"; // // label1 // @@ -216,6 +311,17 @@ private void InitializeComponent() label1.Text = "Column"; label1.Click += label1_Click; // + // button2 + // + button2.Dock = System.Windows.Forms.DockStyle.Bottom; + button2.Location = new System.Drawing.Point(3, 74); + button2.Name = "button2"; + button2.Size = new System.Drawing.Size(425, 23); + button2.TabIndex = 7; + button2.Text = "Save"; + button2.UseVisualStyleBackColor = true; + button2.Click += button2_Click; + // // CatalogueAnalysisExecutionControlUI // AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); @@ -227,6 +333,11 @@ private void InitializeComponent() tabPage1.ResumeLayout(false); tabPage2.ResumeLayout(false); tabPage2.PerformLayout(); + groupBox4.ResumeLayout(false); + groupBox4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit(); + SecondaryConstrainsTableLayoutPanel.ResumeLayout(false); + SecondaryConstrainsTableLayoutPanel.PerformLayout(); groupBox3.ResumeLayout(false); groupBox2.ResumeLayout(false); groupBox1.ResumeLayout(false); @@ -253,5 +364,13 @@ private void InitializeComponent() private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.ComboBox cbTime; private System.Windows.Forms.Button button1; + private System.Windows.Forms.GroupBox groupBox4; + private System.Windows.Forms.TableLayoutPanel SecondaryConstrainsTableLayoutPanel; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Button button2; } } diff --git a/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.cs b/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.cs index 2e99f996e4..c88c785702 100644 --- a/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.cs +++ b/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.cs @@ -23,6 +23,8 @@ public partial class CatalogueAnalysisExecutionControlUI : CatalogueAnalysisExec private Catalogue _catalogue; private DQERepository _dqeRepository; private List _primaryConstraints; + + //private List _secondaryConstraints; private void SetupPrimaryConstrainsConfiguration() { foreach (var ci in _catalogue.CatalogueItems.Select(ci => ci.ColumnInfo)) @@ -86,8 +88,8 @@ public override void SetDatabaseObject(IActivateItems activator, Catalogue datab } } - - + + } @@ -152,7 +154,7 @@ private void btnSavePrimaryConstraints_Click(object sender, EventArgs e) private void button1_Click(object sender, EventArgs e) { - if(cbPivot.SelectedItem is null || cbTime.SelectedItem is null) + if (cbPivot.SelectedItem is null || cbTime.SelectedItem is null) { throw new Exception("No time or pivot column selected"); } @@ -163,6 +165,41 @@ cbPivot.SelectedItem as ColumnInfo ); validation.GenerateValidationReports(); } + + private void label5_Click(object sender, EventArgs e) + { + + } + + private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e) + { + + } + + private void AddSecondaryConstraint() + { + var columnsCb = new ComboBox(); + columnsCb.Items.AddRange(_catalogue.CatalogueItems.Select(ci => ci.ColumnInfo).ToArray()); + + var constraintCb = new ComboBox(); + constraintCb.Items.AddRange(Enum.GetNames(typeof(SecondaryConstraint.Constraints)).ToArray()); + + var consequencesCb = new ComboBox(); + consequencesCb.Items.AddRange(Enum.GetNames(typeof(SecondaryConstraint.Consequences)).ToArray()); + + SecondaryConstrainsTableLayoutPanel.RowCount++; + SecondaryConstrainsTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 50F)); + SecondaryConstrainsTableLayoutPanel.Controls.Add(columnsCb, 0, SecondaryConstrainsTableLayoutPanel.RowCount - 1); + SecondaryConstrainsTableLayoutPanel.Controls.Add(constraintCb, 1, SecondaryConstrainsTableLayoutPanel.RowCount - 1); + SecondaryConstrainsTableLayoutPanel.Controls.Add(consequencesCb, 1, SecondaryConstrainsTableLayoutPanel.RowCount - 1); + } + + private void pictureBox1_Click(object sender, EventArgs e) + { + //_secondaryConstraints.Add(new SecondaryConstraint()); + AddSecondaryConstraint(); + } + } [TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider))] diff --git a/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.resx b/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.resx index 843707171f..4959e0039e 100644 --- a/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.resx +++ b/Rdmp.UI/CatalogueAnalysisUIs/CatalogueAnalysisExecutionControlUI.resx @@ -118,10 +118,32 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAABGdBTUEAAK/INwWK6QAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA6/AAAOvwE4BVMkAAAAB3RJ + TUUH4QURDyAsnYnScQAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMS41ZEdYUgAAA2ZJREFUOE91 + k8lK7EAUhisk3W3jwlXT4jP0whfwFdz1Mkjv3OhW8QkC4jwPBBGnqKggtuAQh4WzxnlCRRFtZ+NsFFP3 + P7mdoPdiwYFK1amvTv3nD/s+9vb23BB2d3fFnZ0daXt724fwb25u+jY2NqT19XVxbW1NWF1dZRS/DgLt + 7+8HAYoAJAOibG1taQDFAdIAUgCSAYkggr/CkqAwIFHM1bOzM+Pq6ipxe3tr3d3d8ZubG+vi4iJxcnJi + AKIuLi5Gl5eXw0tLS0lCciRBIVQSOzg40C8vL82Xlxf77e2NW5blxPv7O399feWPj4/26empCaA+MzMT + m5ubCyF+gAJUURJk02HAnMDtvLOzk3d1dXE8kX9+fjrw4+NjG1XpU1NT0YmJicDk5CRj0IfEjtDTqCKq + 5unpyYPV1dXxrKwsnp2d7QBpjWAfHx8cl5uAqCMjI5HR0VGBoSIRz5NJo+fnZ04wChdWWFjI8QCemZnp + wSi+vr5IR47nGgMDA/Lg4KDIAJLQMYXEJohpmvzw8JAbhuFEfn6+B6O5u045lIvzie7ubqW3t1diAJF/ + NOoWiXx0dMQbGhp4Xl6eExkZGR6M5u465VAu9LY6Ojo0VO1j8JAf/onDAk7H5ufneU5ODk9PT3ciNTXV + g9HcXaccyoXevLW1Nd7e3u5nAPnQJe36+tqiZ0IDXlRU5IielpbGU1JSPBjNaY32KIdycdZSVVVraWnx + MXxI0EBBAxLUAGjn2GF4eJgPDQ3x3NxcD0ZzWqM9yqHc2dnZRGNjo9Lc3CyxlZUVERsy2mzc3987LacK + YU4nCgoKPBi08bpJ9qAmAGzAPjI0FBneLSAiuEEl35DLv8N+swZpDI+ZTU1Nan19fQQwgeGXIGBA1/Xo + 9PS0ju7alOzCcAnHAY7WcxjbAZG/4HobOumVlZVR7AcAw50YcC8bHx8PwXgxuFnHs038fzbd/vDw4EDp + Zz8/PyegPTY2ZkJ0vaysLAZYCPEX5I6+vj7W398f1jQtChOqqNRAxQk0x6KuLSwsWKgmAbcb1dXVaklJ + SRSwMCJJ+GdAE9bT0xNsa2sjDeSamhoFB7WKiop4aWmpBoBSXFwsl5eXR6qqqoL/VfR9EAxVMRhQQKtF + CCvV1tb6APQD6ANEAlTEXACM/YQx9gdfXozt1tFcfAAAAABJRU5ErkJggg== + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - vgAADr4B6kKxwAAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xMzQDW3oAAAEaSURBVDhPnY/t + vAAADrwBlbxySQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xMzQDW3oAAAEaSURBVDhPnY/t KoVBFEbf/+IupMQ1iIiU3yIppaSkEAk5KR8pKUmJkgiRlJKUFIlEkgtQiBK5huU5pzPTNGO8OrvWNLP3 flZNkmRKyKJKCsEKChUFAoO/GMP9Qq5cSb7/a9AQCEy5kvz8b8EX33zwybvOV53PvPHES6rICu545IYH rrnnklvO9TrjilO9TriIiqxgn2N2OWKbQzY5YF2dNfZYZYdltlhig0V1XUkOc5ljhVmtTmt1UqsTLDDO