diff --git a/BismNormalizer/BismNormalizer.CommandLine/Program.cs b/BismNormalizer/BismNormalizer.CommandLine/Program.cs index 2e5a826..2ca33b5 100644 --- a/BismNormalizer/BismNormalizer.CommandLine/Program.cs +++ b/BismNormalizer/BismNormalizer.CommandLine/Program.cs @@ -168,7 +168,7 @@ namespace BismNormalizer.CommandLine throw new FileNotFoundException($"File not found {bsmnFile}"); } Console.WriteLine($"About to deserialize {bsmnFile}"); - ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile); + ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile, "BISM Normalizer Command Line"); Console.WriteLine(); if (comparisonInfo.ConnectionInfoSource.UseProject) @@ -198,19 +198,20 @@ namespace BismNormalizer.CommandLine Console.WriteLine("--Comparing ..."); if (credsProvided) { + comparisonInfo.CredsProvided = true; + comparisonInfo.SourceUsername = sourceUsername; + comparisonInfo.SourcePassword = sourcePassword; + comparisonInfo.TargetUsername = targetUsername; + comparisonInfo.TargetPassword = targetPassword; + if (!String.IsNullOrEmpty(workspaceServer)) { - _comparison = ComparisonFactory.CreateComparison(comparisonInfo, sourceUsername, sourcePassword, targetUsername, targetPassword, workspaceServer); - } - else - { - _comparison = ComparisonFactory.CreateComparison(comparisonInfo, sourceUsername, sourcePassword, targetUsername, targetPassword); + comparisonInfo.WorkspaceServerProvided = true; + comparisonInfo.WorkspaceServer = workspaceServer; } } - else - { - _comparison = ComparisonFactory.CreateComparison(comparisonInfo); - } + + _comparison = ComparisonFactory.CreateComparison(comparisonInfo); _comparison.ValidationMessage += HandleValidationMessage; _comparison.Connect(); _comparison.CompareTabularModels(); diff --git a/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs b/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs index 89dbee4..fc6fbe5 100644 --- a/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs +++ b/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.0.1.10")] -[assembly: AssemblyFileVersion("4.0.1.10")] +[assembly: AssemblyVersion("4.0.1.12")] +[assembly: AssemblyFileVersion("4.0.1.12")] diff --git a/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs b/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs index 64b77ea..d7978d0 100644 --- a/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs +++ b/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.0.1.10")] -[assembly: AssemblyFileVersion("4.0.1.10")] +[assembly: AssemblyVersion("4.0.1.12")] +[assembly: AssemblyFileVersion("4.0.1.12")] diff --git a/BismNormalizer/BismNormalizer.Tests/BismNormalizer.Tests.csproj b/BismNormalizer/BismNormalizer.Tests/BismNormalizer.Tests.csproj index d86e2b7..eca0398 100644 --- a/BismNormalizer/BismNormalizer.Tests/BismNormalizer.Tests.csproj +++ b/BismNormalizer/BismNormalizer.Tests/BismNormalizer.Tests.csproj @@ -40,20 +40,20 @@ 4 - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Core.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Core.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll diff --git a/BismNormalizer/BismNormalizer.Tests/packages.config b/BismNormalizer/BismNormalizer.Tests/packages.config index 250da1c..b958bff 100644 --- a/BismNormalizer/BismNormalizer.Tests/packages.config +++ b/BismNormalizer/BismNormalizer.Tests/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/BismNormalizer/BismNormalizer/ApplicationInsights.config b/BismNormalizer/BismNormalizer/ApplicationInsights.config index e69de29..d441a9b 100644 --- a/BismNormalizer/BismNormalizer/ApplicationInsights.config +++ b/BismNormalizer/BismNormalizer/ApplicationInsights.config @@ -0,0 +1,107 @@ + + + + + + + + + + + + + core.windows.net + core.chinacloudapi.cn + core.cloudapi.de + core.usgovcloudapi.net + + + Microsoft.Azure.EventHubs + Microsoft.Azure.ServiceBus + + + + + + + + + + + + + + + + + + + + + + + + 5 + Event + + + 5 + Event + + + + + + \ No newline at end of file diff --git a/BismNormalizer/BismNormalizer/BismNormalizer.IconSetup.exe b/BismNormalizer/BismNormalizer/BismNormalizer.IconSetup.exe new file mode 100644 index 0000000..ca134c6 Binary files /dev/null and b/BismNormalizer/BismNormalizer/BismNormalizer.IconSetup.exe differ diff --git a/BismNormalizer/BismNormalizer/BismNormalizer.csproj b/BismNormalizer/BismNormalizer/BismNormalizer.csproj index 855216d..b2cb4e5 100644 --- a/BismNormalizer/BismNormalizer/BismNormalizer.csproj +++ b/BismNormalizer/BismNormalizer/BismNormalizer.csproj @@ -99,20 +99,20 @@ ..\packages\Microsoft.ApplicationInsights.WindowsServer.2.8.1\lib\net45\Microsoft.AI.WindowsServer.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Core.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Core.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.dll - - ..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll + + ..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll ..\packages\Microsoft.ApplicationInsights.2.8.1\lib\net46\Microsoft.ApplicationInsights.dll @@ -208,7 +208,9 @@ + + @@ -226,11 +228,28 @@ + + + + + + + UserControl + + + ComparisonControl.cs + + + Form + + + Connections.cs + Form @@ -260,12 +279,6 @@ - - UserControl - - - ComparisonControl.cs - @@ -330,11 +343,11 @@ - + Form - - Connections.cs + + ConnectionsAlmt.cs @@ -418,6 +431,8 @@ + + true Always @@ -434,9 +449,7 @@ - - PreserveNewest - + Designer @@ -448,6 +461,8 @@ + + true Always @@ -470,11 +485,13 @@ ComparisonControl.cs - Designer Connections.cs + + ConnectionsAlmt.cs + Deployment.cs diff --git a/BismNormalizer/BismNormalizer/BismNormalizer.exe b/BismNormalizer/BismNormalizer/BismNormalizer.exe new file mode 100644 index 0000000..f939d24 Binary files /dev/null and b/BismNormalizer/BismNormalizer/BismNormalizer.exe differ diff --git a/BismNormalizer/BismNormalizer/BismNormalizer.targets b/BismNormalizer/BismNormalizer/BismNormalizer.targets index c8b8f99..b958827 100644 --- a/BismNormalizer/BismNormalizer/BismNormalizer.targets +++ b/BismNormalizer/BismNormalizer/BismNormalizer.targets @@ -114,7 +114,7 @@ - + $(MSBuildProjectDirectory)\bin\ReleaseObfusc diff --git a/BismNormalizer/BismNormalizer/DemoHarness.cs b/BismNormalizer/BismNormalizer/DemoHarness.cs index 97c2575..316d2d4 100644 --- a/BismNormalizer/BismNormalizer/DemoHarness.cs +++ b/BismNormalizer/BismNormalizer/DemoHarness.cs @@ -12,7 +12,7 @@ namespace BismNormalizer { public static void Main() { - using (Comparison c = ComparisonFactory.CreateComparison("C:\\TabularCompare1.bsmn")) + using (Comparison c = ComparisonFactory.CreateComparison("C:\\TabularCompare1.bsmn", "")) { c.Connect(); c.CompareTabularModels(); diff --git a/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs b/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs index 3da27c7..0c74525 100644 --- a/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs +++ b/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.0.1.10")] -[assembly: AssemblyFileVersion("4.0.1.10")] +[assembly: AssemblyVersion("4.0.1.12")] +[assembly: AssemblyFileVersion("4.0.1.12")] diff --git a/BismNormalizer/BismNormalizer/Resources.Designer.cs b/BismNormalizer/BismNormalizer/Resources.Designer.cs index 991bc39..2ffd298 100644 --- a/BismNormalizer/BismNormalizer/Resources.Designer.cs +++ b/BismNormalizer/BismNormalizer/Resources.Designer.cs @@ -140,7 +140,7 @@ namespace BismNormalizer { } /// - /// Looks up a localized string similar to BISM Normalizer Warning List. + /// Looks up a localized string similar to Warning List. /// internal static string ToolWindowTitle { get { diff --git a/BismNormalizer/BismNormalizer/Resources.resx b/BismNormalizer/BismNormalizer/Resources.resx index 9d9525c..af9737c 100644 --- a/BismNormalizer/BismNormalizer/Resources.resx +++ b/BismNormalizer/BismNormalizer/Resources.resx @@ -125,7 +125,7 @@ Resources\Progress.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - BISM Normalizer Warning List + Warning List Resources\LogoSmall.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/BismNormalizer/BismNormalizer/Resources/CalculationGroup.png b/BismNormalizer/BismNormalizer/Resources/CalculationGroup.png new file mode 100644 index 0000000..ebf676b Binary files /dev/null and b/BismNormalizer/BismNormalizer/Resources/CalculationGroup.png differ diff --git a/BismNormalizer/BismNormalizer/Resources/CalculationItem.png b/BismNormalizer/BismNormalizer/Resources/CalculationItem.png new file mode 100644 index 0000000..0cf7053 Binary files /dev/null and b/BismNormalizer/BismNormalizer/Resources/CalculationItem.png differ diff --git a/BismNormalizer/BismNormalizer/Resources/Model.png b/BismNormalizer/BismNormalizer/Resources/Model.png new file mode 100644 index 0000000..cafbbe7 Binary files /dev/null and b/BismNormalizer/BismNormalizer/Resources/Model.png differ diff --git a/BismNormalizer/BismNormalizer/Resources/RefreshPolicy.png b/BismNormalizer/BismNormalizer/Resources/RefreshPolicy.png new file mode 100644 index 0000000..179b7bb Binary files /dev/null and b/BismNormalizer/BismNormalizer/Resources/RefreshPolicy.png differ diff --git a/BismNormalizer/BismNormalizer/Settings.Designer.cs b/BismNormalizer/BismNormalizer/Settings.Designer.cs index 42ec418..8235a2b 100644 --- a/BismNormalizer/BismNormalizer/Settings.Designer.cs +++ b/BismNormalizer/BismNormalizer/Settings.Designer.cs @@ -12,10 +12,10 @@ namespace BismNormalizer { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.2.0.0")] + public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + public static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { @@ -286,5 +286,41 @@ namespace BismNormalizer { this["OptionHighDpiLocal"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OptionCompositeModelsOverride { + get { + return ((bool)(this["OptionCompositeModelsOverride"])); + } + set { + this["OptionCompositeModelsOverride"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OptionRetainPolicyPartitions { + get { + return ((bool)(this["OptionRetainPolicyPartitions"])); + } + set { + this["OptionRetainPolicyPartitions"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OptionRetainStorageMode { + get { + return ((bool)(this["OptionRetainStorageMode"])); + } + set { + this["OptionRetainStorageMode"] = value; + } + } } } diff --git a/BismNormalizer/BismNormalizer/Settings.cs b/BismNormalizer/BismNormalizer/Settings.cs index 8e2c10b..d433178 100644 --- a/BismNormalizer/BismNormalizer/Settings.cs +++ b/BismNormalizer/BismNormalizer/Settings.cs @@ -5,7 +5,7 @@ // The PropertyChanged event is raised after a setting's value is changed. // The SettingsLoaded event is raised after the setting values are loaded. // The SettingsSaving event is raised before the setting values are saved. - internal sealed partial class Settings { + public sealed partial class Settings { public Settings() { // // To add event handlers for saving and changing settings, uncomment the lines below: diff --git a/BismNormalizer/BismNormalizer/Settings.settings b/BismNormalizer/BismNormalizer/Settings.settings index 92e6537..2ba8921 100644 --- a/BismNormalizer/BismNormalizer/Settings.settings +++ b/BismNormalizer/BismNormalizer/Settings.settings @@ -68,5 +68,14 @@ True + + False + + + False + + + False + \ No newline at end of file diff --git a/BismNormalizer/BismNormalizer/TabularCompare/ComparisonFactory.cs b/BismNormalizer/BismNormalizer/TabularCompare/ComparisonFactory.cs index d32fe73..d4e5b8e 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/ComparisonFactory.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/ComparisonFactory.cs @@ -14,7 +14,9 @@ namespace BismNormalizer.TabularCompare { // Factory pattern: https://msdn.microsoft.com/en-us/library/orm-9780596527730-01-05.aspx - private static List _supportedCompatibilityLevels = new List() { 1100, 1103, 1200, 1400 }; + private static int _minCompatibilityLevel = 1100; + private static int _maxCompatibilityLevel = 1500; + private static List _supportedDataSourceVersions = new List { "PowerBI_V3" }; /// /// Uses factory design pattern to return an object of type Core.Comparison, which is instantiated using MultidimensionalMetadata.Comparison or TabularMeatadata.Comparison depending on SSAS compatibility level. Use this overload when running in Visual Studio. @@ -42,9 +44,9 @@ namespace BismNormalizer.TabularCompare /// /// Full path to the BSMN file. /// Core.Comparison object - public static Comparison CreateComparison(string bsmnFile) + public static Comparison CreateComparison(string bsmnFile, string appName) { - ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile); + ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile, appName); return CreateComparison(comparisonInfo); } @@ -59,57 +61,136 @@ namespace BismNormalizer.TabularCompare return CreateComparisonInitialized(comparisonInfo); } - /// - /// Uses factory design pattern to return an object of type Core.Comparison, which is instantiated using MultidimensionalMetadata.Comparison or TabularMeatadata.Comparison depending on SSAS compatibility level. - /// - /// ComparisonInfo object for the comparison. - /// Core.Comparison object - public static Comparison CreateComparison(ComparisonInfo comparisonInfo, string sourceUsername, string sourcePassword, string targetUsername, string targetPassword) - { - comparisonInfo.InitializeCompatibilityLevels(sourceUsername, sourcePassword, targetUsername, targetPassword); - return CreateComparisonInitialized(comparisonInfo); - } - - /// - /// Uses factory design pattern to return an object of type Core.Comparison, which is instantiated using MultidimensionalMetadata.Comparison or TabularMeatadata.Comparison depending on SSAS compatibility level. - /// - /// ComparisonInfo object for the comparison. - /// Core.Comparison object - public static Comparison CreateComparison(ComparisonInfo comparisonInfo, string sourceUsername, string sourcePassword, string targetUsername, string targetPassword, string workspaceServer) - { - comparisonInfo.InitializeCompatibilityLevels(sourceUsername, sourcePassword, targetUsername, targetPassword, workspaceServer); - return CreateComparisonInitialized(comparisonInfo); - } - private static Comparison CreateComparisonInitialized(ComparisonInfo comparisonInfo) { - Telemetry.TrackEvent("CreateComparisonInitialized", new Dictionary { { "App", "BismNormalizer" } }); + Telemetry.TrackEvent("CreateComparisonInitialized", new Dictionary { { "App", comparisonInfo.AppName.Replace(" ", "") } }); - if (comparisonInfo.SourceCompatibilityLevel != comparisonInfo.TargetCompatibilityLevel && !(comparisonInfo.SourceCompatibilityLevel == 1200 && comparisonInfo.TargetCompatibilityLevel == 1400)) + ////Currently can't source from PBIP to AS because AS doesn't work with inline data sources: + //if (comparisonInfo.ConnectionInfoSource.ServerName.StartsWith("powerbi://") && !comparisonInfo.ConnectionInfoTarget.ServerName.StartsWith("powerbi://")) + //{ + // throw new ConnectionException($"Source model is a Power BI dataset and the target is Analysis Services, which is not supported in the current version."); + //} + + //If composite models not allowed on AS, check DQ/Import at model level matches: + if (!comparisonInfo.ConnectionInfoSource.ServerName.StartsWith("powerbi://") && !Settings.Default.OptionCompositeModelsOverride && comparisonInfo.SourceDirectQuery != comparisonInfo.TargetDirectQuery) + { + throw new ConnectionException($"Mixed DirectQuery settings are not supported for AS skus.\nSource is {(comparisonInfo.SourceDirectQuery ? "On" : "Off")} and target is {(comparisonInfo.TargetDirectQuery ? "On" : "Off")}."); + } + + #region Data-source versions check + + //If Power BI, check the default datasource version + //Source + bool sourceDataSourceVersionRequiresUpgrade = false; + if (comparisonInfo.ConnectionInfoSource.ServerName.StartsWith("powerbi://") && !_supportedDataSourceVersions.Contains(comparisonInfo.SourceDataSourceVersion)) + { + sourceDataSourceVersionRequiresUpgrade = true; + } + //Target + bool targetDataSourceVersionRequiresUpgrade = false; + if (comparisonInfo.ConnectionInfoTarget.ServerName.StartsWith("powerbi://") && !_supportedDataSourceVersions.Contains(comparisonInfo.TargetDataSourceVersion)) + { + targetDataSourceVersionRequiresUpgrade = true; + } + //Check if user willing to upgrade the data-source version(s) + if (sourceDataSourceVersionRequiresUpgrade && targetDataSourceVersionRequiresUpgrade) + { + string message = $"The source and target are Power BI datasets have default data-source versions of {comparisonInfo.SourceDataSourceVersion} and {comparisonInfo.TargetDataSourceVersion} respectively, which are not supported for comparison."; + if (comparisonInfo.Interactive && System.Windows.Forms.MessageBox.Show( + message += $"\nDo you want to upgrade them both to {_supportedDataSourceVersions[0]} and allow the comparison?\n\nNOTE: this is a irreversible operation and you may not be able to download the PBIX file(s) to Power BI Desktop. You should only do this if you have the original PBIX as a backup.", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) != System.Windows.Forms.DialogResult.Yes) + { + throw new ConnectionException(message); + } + } + else if (sourceDataSourceVersionRequiresUpgrade) + { + string message = $"The source is a Power BI dataset with default data-source version of {comparisonInfo.SourceDataSourceVersion}, which is not supported for comparison."; + if (comparisonInfo.Interactive && System.Windows.Forms.MessageBox.Show( + message += $"\nDo you want to upgrade it to {_supportedDataSourceVersions[0]} and allow the comparison?\n\nNOTE: this is a irreversible operation and you may not be able to download the PBIX file(s) to Power BI Desktop. You should only do this if you have the original PBIX as a backup.", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) != System.Windows.Forms.DialogResult.Yes) + { + throw new ConnectionException(message); + } + } + else if (targetDataSourceVersionRequiresUpgrade) + { + string message = $"The target is a Power BI datasets with default data-source version of {comparisonInfo.TargetDataSourceVersion}, which is not supported for comparison."; + if (comparisonInfo.Interactive && System.Windows.Forms.MessageBox.Show( + message += $"\nDo you want to upgrade it to {_supportedDataSourceVersions[0]} and allow the comparison?\n\nNOTE: this is a irreversible operation and you may not be able to download the PBIX file(s) to Power BI Desktop. You should only do this if you have the original PBIX as a backup.", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) != System.Windows.Forms.DialogResult.Yes) + { + throw new ConnectionException(message); + } + } + + #endregion + + //Check if one of the supported compat levels: + if ( + !(comparisonInfo.SourceCompatibilityLevel >= _minCompatibilityLevel && comparisonInfo.SourceCompatibilityLevel <= _maxCompatibilityLevel && + comparisonInfo.TargetCompatibilityLevel >= _minCompatibilityLevel && comparisonInfo.TargetCompatibilityLevel <= _maxCompatibilityLevel + ) + ) { throw new ConnectionException($"This combination of mixed compatibility levels is not supported.\nSource is {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)} and target is {Convert.ToString(comparisonInfo.TargetCompatibilityLevel)}."); } - if (comparisonInfo.SourceDirectQuery != comparisonInfo.TargetDirectQuery) - { - throw new ConnectionException($"Mixed DirectQuery settings are not supported.\nSource is {(comparisonInfo.SourceDirectQuery ? "On" : "Off")} and target is {(comparisonInfo.TargetDirectQuery ? "On" : "Off")}."); - } + //Return the comparison object & offer upgrade of target if appropriate + Comparison returnComparison = null; - //We know both models have same compatibility level, but is it supported? - if (!_supportedCompatibilityLevels.Contains(comparisonInfo.SourceCompatibilityLevel)) + if (comparisonInfo.SourceCompatibilityLevel >= 1200 && comparisonInfo.TargetCompatibilityLevel >= 1200) { - throw new ConnectionException($"Models have compatibility level of {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)}, which is not supported by this version of BISM Normalizer.\nPlease check http://bism-normalizer.com/purchase for other versions."); - } + returnComparison = new TabularMetadata.Comparison(comparisonInfo); + TabularMetadata.Comparison returnTabularComparison = (TabularMetadata.Comparison)returnComparison; - if (comparisonInfo.SourceCompatibilityLevel >= 1200) - { - return new TabularMetadata.Comparison(comparisonInfo); + //Upgrade default DATA-SOURCE versions if required + if (sourceDataSourceVersionRequiresUpgrade) + { + returnTabularComparison.SourceTabularModel.Connect(); + returnTabularComparison.SourceTabularModel.TomDatabase.Model.DefaultPowerBIDataSourceVersion = Microsoft.AnalysisServices.Tabular.PowerBIDataSourceVersion.PowerBI_V3; + returnTabularComparison.SourceTabularModel.TomDatabase.Update(); + returnTabularComparison.Disconnect(); + } + if (targetDataSourceVersionRequiresUpgrade) + { + returnTabularComparison.TargetTabularModel.Connect(); + returnTabularComparison.TargetTabularModel.TomDatabase.Model.DefaultPowerBIDataSourceVersion = Microsoft.AnalysisServices.Tabular.PowerBIDataSourceVersion.PowerBI_V3; + returnTabularComparison.TargetTabularModel.TomDatabase.Update(); + returnTabularComparison.Disconnect(); + } + + //Check if source has a higher compat level than the target and offer upgrade if appropriate. + if (comparisonInfo.SourceCompatibilityLevel > comparisonInfo.TargetCompatibilityLevel) + { + string message = $"Source compatibility level is { Convert.ToString(comparisonInfo.SourceCompatibilityLevel) } and target is { Convert.ToString(comparisonInfo.TargetCompatibilityLevel) }, which is not supported for comparison.\n"; + + if (comparisonInfo.Interactive && + !comparisonInfo.ConnectionInfoTarget.UseProject && //Upgrade in SSDT not supported + System.Windows.Forms.MessageBox.Show( + message += $"\nDo you want to upgrade the target to {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)} and allow the comparison?", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes) + { + returnTabularComparison.TargetTabularModel.Connect(); + returnTabularComparison.TargetTabularModel.TomDatabase.CompatibilityLevel = comparisonInfo.SourceCompatibilityLevel; + returnTabularComparison.TargetTabularModel.TomDatabase.Update(); + returnTabularComparison.Disconnect(); + } + else + { + throw new ConnectionException(message + "\nUpgrade the target compatibility level and retry."); + } + } } else { - return new MultidimensionalMetadata.Comparison(comparisonInfo); + if (comparisonInfo.SourceCompatibilityLevel == comparisonInfo.TargetCompatibilityLevel) + { + returnComparison = new MultidimensionalMetadata.Comparison(comparisonInfo); + } + else + { + throw new ConnectionException($"This combination of mixed compatibility levels is not supported.\nSource is {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)} and target is {Convert.ToString(comparisonInfo.TargetCompatibilityLevel)}."); + } } - } + return returnComparison; + } } } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/ComparisonInfo.cs b/BismNormalizer/BismNormalizer/TabularCompare/ComparisonInfo.cs index 0dc0e94..0e98127 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/ComparisonInfo.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/ComparisonInfo.cs @@ -18,9 +18,20 @@ namespace BismNormalizer.TabularCompare private SkipSelectionCollection _skipSelectionCollection; private int _sourceCompatibilityLevel; private int _targetCompatibilityLevel; + private string _sourceDataSourceVersion; + private string _targetDataSourceVersion; private bool _sourceDirectQuery; private bool _targetDirectQuery; private bool _promptForDatabaseProcessing; + private bool _interactive = true; + private string _appName = ""; + private bool _credsProvided = false; + private string _sourceUsername; + private string _sourcePassword; + private string _targetUsername; + private string _targetPassword; + private bool _workspaceServerProvided = false; + private string _workspaceServer; /// /// Initializes a new instance of the ComparisonInfo class. @@ -73,17 +84,29 @@ namespace BismNormalizer.TabularCompare } /// - /// SSAS compatibility level for the source tabular model. + /// Compatibility level for the source tabular model. /// [XmlIgnore()] public int SourceCompatibilityLevel => _sourceCompatibilityLevel; /// - /// SSAS compatibility level for the target tabular model. + /// Compatibility level for the target tabular model. /// [XmlIgnore()] public int TargetCompatibilityLevel => _targetCompatibilityLevel; + /// + /// Default data source version for the source tabular model. + /// + [XmlIgnore()] + public string SourceDataSourceVersion => _sourceDataSourceVersion; + + /// + /// Default data source version for the target tabular model. + /// + [XmlIgnore()] + public string TargetDataSourceVersion => _targetDataSourceVersion; + /// /// Flag depending on whehter source tabular model is in DirectQuery mode. /// @@ -106,6 +129,96 @@ namespace BismNormalizer.TabularCompare set { _promptForDatabaseProcessing = value; } } + /// + /// Flag depending on whether running in interactive mode. Command line execution is not. + /// + [XmlIgnore()] + public bool Interactive + { + get { return _interactive; } + set { _interactive = value; } + } + + /// + /// Name of the app. For example, BISM Normalizer or ALM Toolkit. + /// + [XmlIgnore()] + public string AppName + { + get { return _appName; } + set { _appName = value; } + } + + /// + /// Flag depending on whether credentials provided to connect to AS/PBI. Used for command line mode/automated build. + /// + [XmlIgnore()] + public bool CredsProvided + { + get { return _credsProvided; } + set { _credsProvided = value; } + } + + /// + /// Username for source model for when CredsProvided = true. + /// + [XmlIgnore()] + public string SourceUsername + { + get { return _sourceUsername; } + set { _sourceUsername = value; } + } + + /// + /// Password for source model for when CredsProvided = true. + /// + [XmlIgnore()] + public string SourcePassword + { + get { return _sourcePassword; } + set { _sourcePassword = value; } + } + + /// + /// Username for target model for when CredsProvided = true. + /// + [XmlIgnore()] + public string TargetUsername + { + get { return _targetUsername; } + set { _targetUsername = value; } + } + + /// + /// Password for target model for when CredsProvided = true. + /// + [XmlIgnore()] + public string TargetPassword + { + get { return _targetPassword; } + set { _targetPassword = value; } + } + + /// + /// Flag depending on whether workspace server was provided. Used for command line mode/automated build. + /// + [XmlIgnore()] + public bool WorkspaceServerProvided + { + get { return _workspaceServerProvided; } + set { _workspaceServerProvided = value; } + } + + /// + /// Workspace server name for when WorkspaceServerProvided = true. Used for command line mode/automated build. + /// + [XmlIgnore()] + public string WorkspaceServer + { + get { return _workspaceServer; } + set { _workspaceServer = value; } + } + #endregion /// @@ -113,7 +226,7 @@ namespace BismNormalizer.TabularCompare /// /// BSMN file to be deserialized. /// Deserialized instance of ComparisonInfo. - public static ComparisonInfo DeserializeBsmnFile(string bsmnFile) + public static ComparisonInfo DeserializeBsmnFile(string bsmnFile, string appName) { if (!File.Exists(bsmnFile)) { @@ -121,58 +234,44 @@ namespace BismNormalizer.TabularCompare } XmlSerializer reader = new XmlSerializer(typeof(ComparisonInfo)); StreamReader file = new StreamReader(bsmnFile); - return (ComparisonInfo)reader.Deserialize(file); + ComparisonInfo returnComparisonInfo = (ComparisonInfo)reader.Deserialize(file); + returnComparisonInfo.AppName = appName; + return returnComparisonInfo; } /// /// Finds models' compatibility levels (and preps databases on workspace servers for comparison). This overload to be used when client is not Visual Studio - e.g. command line. /// + /// + /// public void InitializeCompatibilityLevels() { + if (_credsProvided) + { + ConnectionInfoSource.CredsProvided = true; + ConnectionInfoSource.Username = _sourceUsername; + ConnectionInfoSource.Password = _sourcePassword; + + ConnectionInfoTarget.CredsProvided = true; + ConnectionInfoTarget.Username = _targetUsername; + ConnectionInfoTarget.Password = _targetPassword; + + if (_workspaceServerProvided) + { + ConnectionInfoSource.WorkspaceServerProvided = true; + ConnectionInfoSource.WorkspaceServer = _workspaceServer; + + ConnectionInfoTarget.WorkspaceServerProvided = true; + ConnectionInfoTarget.WorkspaceServer = _workspaceServer; + } + } + ConnectionInfoSource.InitializeCompatibilityLevel(); ConnectionInfoTarget.InitializeCompatibilityLevel(); PopulateDatabaseProperties(); } - /// - /// Finds models' compatibility levels (and preps databases on workspace servers for comparison). This overload to be used when client is not Visual Studio - e.g. command line. - /// - public void InitializeCompatibilityLevels(string sourceUsername, string sourcePassword, string targetUsername, string targetPassword) - { - ConnectionInfoSource.CredsProvided = true; - ConnectionInfoSource.Username = sourceUsername; - ConnectionInfoSource.Password = sourcePassword; - ConnectionInfoSource.InitializeCompatibilityLevel(); - - ConnectionInfoTarget.CredsProvided = true; - ConnectionInfoTarget.Username = targetUsername; - ConnectionInfoTarget.Password = targetPassword; - ConnectionInfoTarget.InitializeCompatibilityLevel(); - - PopulateDatabaseProperties(); - } - - /// - /// Finds models' compatibility levels (and preps databases on workspace servers for comparison). This overload to be used when client is not Visual Studio - e.g. command line. - /// - public void InitializeCompatibilityLevels(string sourceUsername, string sourcePassword, string targetUsername, string targetPassword, string workspaceServer) - { - - - ConnectionInfoSource.CredsProvided = true; - ConnectionInfoSource.Username = sourceUsername; - ConnectionInfoSource.Password = sourcePassword; - ConnectionInfoSource.InitializeCompatibilityLevel(workspaceServer: workspaceServer); - - ConnectionInfoTarget.CredsProvided = true; - ConnectionInfoTarget.Username = targetUsername; - ConnectionInfoTarget.Password = targetPassword; - ConnectionInfoTarget.InitializeCompatibilityLevel(workspaceServer: workspaceServer); - - PopulateDatabaseProperties(); - } - /// /// Finds model compatibility levels (and preps databases on workspace servers for comparison). This overload to be used when running in Visual Studio. Allows user to cancel if doesn't want to close .bim file(s). /// @@ -202,6 +301,9 @@ namespace BismNormalizer.TabularCompare _sourceCompatibilityLevel = ConnectionInfoSource.CompatibilityLevel; _targetCompatibilityLevel = ConnectionInfoTarget.CompatibilityLevel; + _sourceDataSourceVersion = ConnectionInfoSource.DataSourceVersion; + _targetDataSourceVersion = ConnectionInfoTarget.DataSourceVersion; + _sourceDirectQuery = ConnectionInfoSource.DirectQuery; _targetDirectQuery = ConnectionInfoTarget.DirectQuery; } @@ -250,7 +352,7 @@ namespace BismNormalizer.TabularCompare filesToClose += $"\n- {projectItemToClose.ContainingProject.Name.Replace(".smproj", "")}\\{projectItemToClose.Name}"; } - if (MessageBox.Show($"BISM Normalizer needs to close the following file(s) that are\nopen in Visual Studio. Do you want to continue?{filesToClose}", "BISM Normalizer", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) + if (MessageBox.Show($"{_appName} needs to close the following file(s) that are\nopen in Visual Studio. Do you want to continue?{filesToClose}", _appName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) { userCancelled = true; } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/ConnectionInfo.cs b/BismNormalizer/BismNormalizer/TabularCompare/ConnectionInfo.cs index eee288c..d186515 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/ConnectionInfo.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/ConnectionInfo.cs @@ -24,6 +24,7 @@ namespace BismNormalizer.TabularCompare private string _projectName; private string _projectFile; private int _compatibilityLevel; + private string _dataSourceVersion; private bool _directQuery; private string _bimFileFullName; private EnvDTE.Project _project; @@ -34,11 +35,11 @@ namespace BismNormalizer.TabularCompare private bool _credsProvided = false; private string _username; private string _password; + private bool _workspaceServerProvided = false; + private string _workspaceServer; #endregion - #region Properties - /// /// Initializes a new instance of the ConnectionInfo class. /// @@ -92,11 +93,17 @@ namespace BismNormalizer.TabularCompare } /// - /// The SSAS compatibility level for the connection. + /// Compatibility level for the connection. /// [XmlIgnore()] public int CompatibilityLevel => _compatibilityLevel; + /// + /// Default data source version for the connection. + /// + [XmlIgnore()] + public string DataSourceVersion => _dataSourceVersion; + /// /// A Boolean specifying whether the tabular model for the connection is running in DirectQuery mode. /// @@ -167,7 +174,25 @@ namespace BismNormalizer.TabularCompare set { _password = value; } } - #endregion + /// + /// Flag depending on whether workspace server was provided. Used for command line mode/automated build. + /// + [XmlIgnore()] + public bool WorkspaceServerProvided + { + get { return _workspaceServerProvided; } + set { _workspaceServerProvided = value; } + } + + /// + /// Workspace server name for when WorkspaceServerProvided = true. Used for command line mode/automated build. + /// + [XmlIgnore()] + public string WorkspaceServer + { + get { return _workspaceServer; } + set { _workspaceServer = value; } + } private void ReadSettingsFile() { @@ -336,7 +361,7 @@ namespace BismNormalizer.TabularCompare /// This method ensures the tabular model is online and populates the CompatibilityLevel property. /// /// A Boolean specifying if the user cancelled the comparison. For the case where running in Visual Studio, the user has the option of cancelling if the project BIM file is open. - public void InitializeCompatibilityLevel(bool closedBimFile = false, string workspaceServer = null) + public void InitializeCompatibilityLevel(bool closedBimFile = false) { if (UseProject) { @@ -360,9 +385,9 @@ namespace BismNormalizer.TabularCompare ReadProjectFile(); //Overwrite the server if a workspace server provided - if (!String.IsNullOrEmpty(workspaceServer)) + if (_workspaceServerProvided) { - this.ServerName = workspaceServer; + this.ServerName = _workspaceServer; } } @@ -549,7 +574,8 @@ $@"{{ throw new ConnectionException($"Can not load/find database {this.DatabaseName}."); } _compatibilityLevel = tabularDatabase.CompatibilityLevel; - _directQuery = ((tabularDatabase.Model != null && tabularDatabase.Model.DefaultMode == Microsoft.AnalysisServices.Tabular.ModeType.DirectQuery) || + _dataSourceVersion = tabularDatabase.Model.DefaultPowerBIDataSourceVersion.ToString(); + _directQuery = ((tabularDatabase.Model != null && tabularDatabase.Model.DefaultMode == Microsoft.AnalysisServices.Tabular.ModeType.DirectQuery) || tabularDatabase.DirectQueryMode == DirectQueryMode.DirectQuery || tabularDatabase.DirectQueryMode == DirectQueryMode.InMemoryWithDirectQuery || tabularDatabase.DirectQueryMode == DirectQueryMode.DirectQueryWithInMemory); } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/Core/Comparison.cs b/BismNormalizer/BismNormalizer/TabularCompare/Core/Comparison.cs index b3b58a5..340eadd 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/Core/Comparison.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/Core/Comparison.cs @@ -237,7 +237,7 @@ namespace BismNormalizer.TabularCompare.Core /// Generate Excel report of differences. /// /// - public void ReportDifferences(ProgressBar progBar) + public void ReportDifferences(ToolStripProgressBar progBar) { try { @@ -250,7 +250,7 @@ namespace BismNormalizer.TabularCompare.Core //Wb.Sheets[1].Delete(); Excel.Worksheet Ws = default(Excel.Worksheet); Ws = Wb.ActiveSheet; - Ws.Name = "Bism Normalizer Report"; + Ws.Name = "Comparison Report"; int row = 1, lastDataSourceRow = -1, lastTableRow = -1; // set up headers @@ -360,7 +360,7 @@ namespace BismNormalizer.TabularCompare.Core } } - private void PopulateExcelRow(Excel.Worksheet Ws, ref int row, ref int lastDataSourceRow, ref int lastTableRow, ComparisonObject comparisonObject, ProgressBar progBar) + private void PopulateExcelRow(Excel.Worksheet Ws, ref int row, ref int lastDataSourceRow, ref int lastTableRow, ComparisonObject comparisonObject, ToolStripProgressBar progBar) { progBar.PerformStep(); row += 1; @@ -380,6 +380,9 @@ namespace BismNormalizer.TabularCompare.Core //Type column switch (comparisonObject.ComparisonObjectType) { + case ComparisonObjectType.Model: + Ws.Cells[row, 1].Value = "Model"; + break; case ComparisonObjectType.DataSource: Ws.Cells[row, 1].Value = "Data Source"; break; @@ -404,6 +407,12 @@ namespace BismNormalizer.TabularCompare.Core Ws.Cells[row, 2].InsertIndent(3); Ws.Cells[row, 5].InsertIndent(3); break; + case ComparisonObjectType.CalculationItem: + Ws.Cells[row, 1].Value = "Calculation Item"; + Ws.Cells[row, 1].InsertIndent(3); + Ws.Cells[row, 2].InsertIndent(3); + Ws.Cells[row, 5].InsertIndent(3); + break; case ComparisonObjectType.Perspective: Ws.Cells[row, 1].Value = "Perspective"; break; diff --git a/BismNormalizer/BismNormalizer/TabularCompare/Enums.cs b/BismNormalizer/BismNormalizer/TabularCompare/Enums.cs index 2fbf385..9dd01a3 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/Enums.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/Enums.cs @@ -4,7 +4,7 @@ namespace BismNormalizer.TabularCompare /// /// Type of object that a validation message relates to. For example, Table, Measure, MeasureCalculationDependency, etc. /// - public enum ValidationMessageType { DataSource, Table, Relationship, Measure, Kpi, Perspective, Culture, Role, Expression, Action, MeasureCalculationDependency, General }; //General used for command line only + public enum ValidationMessageType { Model, DataSource, Table, Relationship, Measure, Kpi, CalculationGroup, CalculationItem, Perspective, Culture, Role, Expression, Action, MeasureCalculationDependency, AggregationDependency, General }; //General used for command line only /// /// Status for a validation message, such as Informational and Warning. @@ -14,7 +14,7 @@ namespace BismNormalizer.TabularCompare /// /// Type of comparison object. For example, Table, Measure, Relationship, etc. /// - public enum ComparisonObjectType { DataSource, Table, Relationship, Measure, Kpi, Perspective, Culture, Role, Expression, Action, Connection }; //Need connection for backwards compatibility when deserializing from xml. Set to data source. + public enum ComparisonObjectType { Model, DataSource, Table, Relationship, Measure, Kpi, CalculationItem, Perspective, Culture, Role, Expression, Action, Connection }; //Need connection for backwards compatibility when deserializing from xml. Set to data source. /// /// Status of comparison object, such as Same Definition, Different Definitions and Missing In Target. @@ -40,4 +40,5 @@ namespace BismNormalizer.TabularCompare /// Type of dependency. For example, DataSource, Partition, Expression. /// public enum CalcDependencyObjectType { DataSource, Partition, Expression }; + } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/MultidimensionalMetadata/TabularModel.cs b/BismNormalizer/BismNormalizer/TabularCompare/MultidimensionalMetadata/TabularModel.cs index e957d4f..950504a 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/MultidimensionalMetadata/TabularModel.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/MultidimensionalMetadata/TabularModel.cs @@ -2712,7 +2712,7 @@ namespace BismNormalizer.TabularCompare.MultidimensionalMetadata // Show row count for each table foreach (ProcessingTable table in _tablesToProcess) { - int rowCount = _connectionInfo.DirectQuery ? 0 : Core.Comparison.FindRowCount(_amoServer, table.Name, _amoDatabase.Name); + int rowCount = Core.Comparison.FindRowCount(_amoServer, table.Name, _amoDatabase.Name); _parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, "Success. " + String.Format("{0:#,###0}", rowCount) + " rows transferred.", DeploymentStatus.Success)); } _parentComparison.OnDeploymentComplete(new DeploymentCompleteEventArgs(DeploymentStatus.Success, null)); @@ -2779,7 +2779,7 @@ namespace BismNormalizer.TabularCompare.MultidimensionalMetadata PartitionRowCounter partition = table.FindPartition(partitionIdNodeList[0].InnerText); partition.RowCount = e.IntegerData; - _parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, "Retreived " + String.Format("{0:#,###0}", table.GetRowCount()) + " rows ...", DeploymentStatus.Deploying)); + _parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, "Retrieved " + String.Format("{0:#,###0}", table.GetRowCount()) + " rows ...", DeploymentStatus.Deploying)); } } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/OptionsInfo.cs b/BismNormalizer/BismNormalizer/TabularCompare/OptionsInfo.cs index 8cd80dc..242de52 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/OptionsInfo.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/OptionsInfo.cs @@ -20,6 +20,8 @@ namespace BismNormalizer.TabularCompare private bool _optionActions; private bool _optionPartitions; private bool _optionRetainPartitions; + private bool _optionRetainPolicyPartitions; + private bool _optionRetainStorageMode; private bool _optionMeasureDependencies; private ProcessingOption _optionProcessingOption; private bool _optionTransaction; @@ -38,6 +40,8 @@ namespace BismNormalizer.TabularCompare _optionActions = Settings.Default.OptionActions; _optionPartitions = Settings.Default.OptionPartitions; _optionRetainPartitions = Settings.Default.OptionRetainPartitions; + _optionRetainPolicyPartitions = Settings.Default.OptionRetainPolicyPartitions; + _optionRetainStorageMode = Settings.Default.OptionRetainStorageMode; _optionMeasureDependencies = Settings.Default.OptionMeasureDependencies; _optionProcessingOption = (ProcessingOption)Enum.Parse(typeof(ProcessingOption), Settings.Default.OptionProcessingOption); _optionTransaction = Settings.Default.OptionTransaction; @@ -117,6 +121,24 @@ namespace BismNormalizer.TabularCompare set { _optionRetainPartitions = value; } } + /// + /// A Boolean specifying whether to retain refresh-policy partitions for table updates. + /// + public bool OptionRetainPolicyPartitions + { + get { return _optionRetainPolicyPartitions; } + set { _optionRetainPolicyPartitions = value; } + } + + /// + /// A Boolean specifying whether to retain storage for table updates on composite models. + /// + public bool OptionRetainStorageMode + { + get { return _optionRetainStorageMode; } + set { _optionRetainStorageMode = value; } + } + /// /// A Boolean specifying whether to display warnings for missing measure dependencies. /// @@ -168,6 +190,8 @@ namespace BismNormalizer.TabularCompare Settings.Default.OptionActions = _optionActions; Settings.Default.OptionPartitions = _optionPartitions; Settings.Default.OptionRetainPartitions = _optionRetainPartitions; + Settings.Default.OptionRetainPolicyPartitions = _optionRetainPolicyPartitions; + Settings.Default.OptionRetainStorageMode = _optionRetainStorageMode; Settings.Default.OptionMeasureDependencies = _optionMeasureDependencies; Settings.Default.OptionProcessingOption = _optionProcessingOption.ToString(); Settings.Default.OptionTransaction = _optionTransaction; diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/CalculationItem.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/CalculationItem.cs new file mode 100644 index 0000000..71e79da --- /dev/null +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/CalculationItem.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Globalization; +using Microsoft.AnalysisServices.Tabular; +using Tom=Microsoft.AnalysisServices.Tabular; + +namespace BismNormalizer.TabularCompare.TabularMetadata +{ + /// + /// Abstraction of a tabular model calculationItem with properties and methods for comparison purposes. + /// + public class CalculationItem : TabularObject + { + private Table _parentTable; + private Tom.CalculationItem _tomCalculationItem; + + /// + /// Initializes a new instance of the CalculationItem class using multiple parameters. + /// + /// Table object that the calculationItem belongs to. + /// Tabular Object Model CalculationItem object abtstracted by the CalculationItem class. + /// Indicates whether the calculationItem is a KPI. + public CalculationItem(Table parentTable, Tom.CalculationItem tomCalculationItem) : base(tomCalculationItem) + { + _parentTable = parentTable; + _tomCalculationItem = tomCalculationItem; + } + + /// + /// Table object that the Relationship oject belongs to. + /// + public Table ParentTable => _parentTable; + + /// + /// Tabular Object Model CalculationItem object abtstracted by the CalculationItem class. + /// + public Tom.CalculationItem TomCalculationItem => _tomCalculationItem; + + /// + /// Name of the table that the CalculationItem oject belongs to. + /// + public string TableName => _tomCalculationItem.CalculationGroup.Table.Name; + + public override string ToString() => this.GetType().FullName; + + /// + /// Find missing calculation dependencies by inspecting the DAX expression for the calculationItem and iterating columns and other calculationItems in the tabular model for validity of the expression. + /// + /// List of missing dependencies to be displayed or logged as warnings. + public List FindMissingCalculationItemDependencies() + { + List dependencies = new List(); + + using (StringReader lines = new StringReader(_tomCalculationItem.Expression)) + { + string line = string.Empty; + while ((line = lines.ReadLine()) != null) + { + if (line.TrimStart().Length > 1 && line.TrimStart().Substring(0, 2) != "--") //Ignore comments + { + //Todo2: still need to parse for /* blah */ type comments. Currently can show missing dependency that doesn't apply if within a comment + + string whatsRemainingOfLine = line; + + while (whatsRemainingOfLine.Contains('[') && whatsRemainingOfLine.Contains(']')) + { + int openSquareBracketPosition = whatsRemainingOfLine.IndexOf('[', 0); + //brilliant person at microsoft has ]] instead of ] + int closeSquareBracketPosition = whatsRemainingOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1); + + if (openSquareBracketPosition < closeSquareBracketPosition - 1) + { + string potentialDependency = whatsRemainingOfLine.Substring(openSquareBracketPosition + 1, closeSquareBracketPosition - openSquareBracketPosition - 1); + if (!potentialDependency.Contains('"') && + !_tomCalculationItem.Expression.Contains($"\"{potentialDependency}\"") && //it's possible the calculationItem itself is deriving the column name from an ADDCOLUMNS for example + !dependencies.Contains(potentialDependency)) + { + //unbelievable: some genius at m$ did a replace on ] with ]] + dependencies.Add(potentialDependency); + } + } + + whatsRemainingOfLine = whatsRemainingOfLine.Substring(closeSquareBracketPosition + 1); + } + } + } + } + + List missingDependencies = new List(); + foreach (string dependency in dependencies) + { + bool foundDependency = false; + + foreach (Table table in _parentTable.ParentTabularModel.Tables) + { + //Check if another calculationItem or column has same name + if (table.CalculationItems.ContainsNameCaseInsensitive(dependency) || table.ColumnsContainsNameCaseInsensitive(dependency)) + { + foundDependency = true; + break; + } + } + + if (!foundDependency) + { + missingDependencies.Add(dependency); + } + } + + return missingDependencies; + } + } +} diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/CalculationItemCollection.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/CalculationItemCollection.cs new file mode 100644 index 0000000..0f2353e --- /dev/null +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/CalculationItemCollection.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; + +namespace BismNormalizer.TabularCompare.TabularMetadata +{ + /// + /// Represents a collection of CalculationItem objects. + /// + public class CalculationItemCollection : List + { + /// + /// Find an object in the collection by name. + /// + /// + /// CalculationItem object if found. Null if not found. + public CalculationItem FindByName(string name) + { + foreach (CalculationItem calculationItem in this) + { + if (calculationItem.Name == name) + { + return calculationItem; + } + } + return null; + } + + /// + /// A Boolean specifying whether the collection contains object by name. + /// + /// + /// True if the object is found, or False if it's not found. + public bool ContainsName(string name) + { + foreach (CalculationItem calculationItem in this) + { + if (calculationItem.Name == name) + { + return true; + } + } + return false; + } + + /// + /// A Boolean specifying whether the collection contains object by name searching without case sensitivity. + /// + /// + /// True if the object is found, or False if it's not found. + public bool ContainsNameCaseInsensitive(string name) + { + foreach (CalculationItem calculationItem in this) + { + if (calculationItem.Name.ToUpper() == name.ToUpper()) + { + return true; + } + } + return false; + } + + /// + /// Returns a collection of CalculationItem objects filtered by the parent table's name. + /// + /// + /// CalculationItemCollection + public CalculationItemCollection FilterByTableName(string tableName) + { + CalculationItemCollection returnCalculationItems = new CalculationItemCollection(); + foreach (CalculationItem calculationItem in this) + { + if (calculationItem.TableName == tableName) + { + returnCalculationItems.Add(calculationItem); + } + } + return returnCalculationItems; + } + + /// + /// Removes an object from the collection by its name. + /// + /// + /// True if the object was removed, or False if was not found. + public bool RemoveByName(string name) + { + foreach (CalculationItem calculationItem in this) + { + if (calculationItem.Name == name) + { + this.Remove(calculationItem); + return true; + } + } + return false; + } + } +} diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Comparison.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Comparison.cs index 9ef27fe..f739962 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Comparison.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Comparison.cs @@ -72,6 +72,29 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { _comparisonObjectCount = 0; + #region Model + + if (_comparisonInfo.TargetCompatibilityLevel >= 1460) //Target compat level is always >= source one. + { + // check if Model object definition is different + ComparisonObject comparisonObjectModel; + if (_sourceTabularModel.Model.ObjectDefinition != _targetTabularModel.Model.ObjectDefinition) + { + comparisonObjectModel = new ComparisonObject(ComparisonObjectType.Model, ComparisonObjectStatus.DifferentDefinitions, _sourceTabularModel.Model, _targetTabularModel.Model, MergeAction.Update); + _comparisonObjects.Add(comparisonObjectModel); + _comparisonObjectCount += 1; + } + else + { + // they are equal, ... + comparisonObjectModel = new ComparisonObject(ComparisonObjectType.Model, ComparisonObjectStatus.SameDefinition, _sourceTabularModel.Model, _targetTabularModel.Model, MergeAction.Skip); + _comparisonObjects.Add(comparisonObjectModel); + _comparisonObjectCount += 1; + } + } + + #endregion + #region DataSources foreach (DataSource dataSourceSource in _sourceTabularModel.DataSources) @@ -155,6 +178,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } #endregion + + #region CalculationItems for Table that is Missing in Target + + foreach (CalculationItem calculationItemSource in tblSource.CalculationItems.FilterByTableName(tblSource.Name)) + { + ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem; + ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInTarget, calculationItemSource, null, MergeAction.Create); + comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem); + _comparisonObjectCount += 1; + } + + #endregion } else { @@ -269,6 +304,53 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } #endregion + + #region CalculationItems (table in source and target) + + // see if matching calculationItem in source and target + foreach (CalculationItem calculationItemSource in tblSource.CalculationItems.FilterByTableName(tblSource.Name)) + { + ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem; + + if (tblTarget.CalculationItems.FilterByTableName(tblTarget.Name).ContainsName(calculationItemSource.Name)) + { + //CalculationItem in source and target, so check definition + CalculationItem calculationItemTarget = tblTarget.CalculationItems.FilterByTableName(tblTarget.Name).FindByName(calculationItemSource.Name); + if (calculationItemSource.ObjectDefinition == calculationItemTarget.ObjectDefinition) + { + //CalculationItem has same definition + ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.SameDefinition, calculationItemSource, calculationItemTarget, MergeAction.Skip); + comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem); + _comparisonObjectCount += 1; + } + else + { + //CalculationItem has different definition + ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.DifferentDefinitions, calculationItemSource, calculationItemTarget, MergeAction.Update); + comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem); + _comparisonObjectCount += 1; + } + } + else + { + ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInTarget, calculationItemSource, null, MergeAction.Create); + comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem); + _comparisonObjectCount += 1; + } + } + //now check if target contains calculationItems Missing in Source + foreach (CalculationItem calculationItemTarget in tblTarget.CalculationItems.FilterByTableName(tblTarget.Name)) + { + ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem; + if (!tblSource.CalculationItems.FilterByTableName(tblSource.Name).ContainsName(calculationItemTarget.Name)) + { + ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInSource, null, calculationItemTarget, MergeAction.Delete); + comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem); + _comparisonObjectCount += 1; + } + } + + #endregion } } @@ -298,12 +380,24 @@ namespace BismNormalizer.TabularCompare.TabularMetadata foreach (Measure measureTarget in tblTarget.Measures.FilterByTableName(tblTarget.Name)) { ComparisonObjectType comparisonObjectType = measureTarget.IsKpi ? ComparisonObjectType.Kpi : ComparisonObjectType.Measure; - ComparisonObject comparisonObjectMeasure = new ComparisonObject(ComparisonObjectType.Measure, ComparisonObjectStatus.MissingInSource, null, measureTarget, MergeAction.Delete); + ComparisonObject comparisonObjectMeasure = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInSource, null, measureTarget, MergeAction.Delete); comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectMeasure); _comparisonObjectCount += 1; } #endregion + + #region CalculationItems for Table that is Missing in Source + + foreach (CalculationItem calculationItemTarget in tblTarget.CalculationItems.FilterByTableName(tblTarget.Name)) + { + ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem; + ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInSource, null, calculationItemTarget, MergeAction.Delete); + comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem); + _comparisonObjectCount += 1; + } + + #endregion } } @@ -538,7 +632,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { #region Refresh/reconnect source and target dbs to check if server definition has changed - if (_uncommitedChanges) + bool reconnect = false; + try + { + _sourceTabularModel.TomDatabase.Refresh(); + _targetTabularModel.TomDatabase.Refresh(); + } + catch (Exception) + { + reconnect = true; + } + + if (reconnect || _uncommitedChanges) { // Reconnect to re-initialize _sourceTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoSource, _comparisonInfo); @@ -546,11 +651,6 @@ namespace BismNormalizer.TabularCompare.TabularMetadata _targetTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoTarget, _comparisonInfo); _targetTabularModel.Connect(); } - else - { - _sourceTabularModel.TomDatabase.Refresh(); - _targetTabularModel.TomDatabase.Refresh(); - } if (!_sourceTabularModel.ConnectionInfo.UseProject && _sourceTabularModel.TomDatabase.LastSchemaUpdate > _lastSourceSchemaUpdate) { @@ -614,6 +714,17 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion + #region Model1 + + //Doing before tables in case need to set DiscourageImplicitMeasures=true to create calc groups downstream + bool updatedModel = false; + foreach (ComparisonObject comparisonObject in _comparisonObjects) + { + updatedModel = UpdateModel(comparisonObject, true); + } + + #endregion + #region Tables // do deletions first to minimize chance of conflict @@ -663,6 +774,19 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion _targetTabularModel.CleanUpVariations(); + + #region Model2 + + //Doing model after tables in case there are calc group tables created so cannot set DisableImplictMeasures=false + if (!updatedModel) + { + foreach (ComparisonObject comparisonObject in _comparisonObjects) + { + UpdateModel(comparisonObject, false); + } + } + + #endregion #region Measures / KPIs @@ -692,6 +816,34 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion + #region CalculationItems + + foreach (ComparisonObject comparisonObject in _comparisonObjects) + { + foreach (ComparisonObject childComparisonObject in comparisonObject.ChildComparisonObjects) + { + DeleteCalculationItem(childComparisonObject); //CalculationItem + } + } + + foreach (ComparisonObject comparisonObject in _comparisonObjects) + { + foreach (ComparisonObject childComparisonObject in comparisonObject.ChildComparisonObjects) + { + CreateCalculationItem(childComparisonObject, comparisonObject.SourceObjectName); //CalculationItem, Table + } + } + + foreach (ComparisonObject comparisonObject in _comparisonObjects) + { + foreach (ComparisonObject childComparisonObject in comparisonObject.ChildComparisonObjects) + { + UpdateCalculationItem(childComparisonObject, comparisonObject.SourceObjectName); //CalculationItem, Table + } + } + + #endregion + #region Perspectives //Restore perspectives that were backed up earlier. Having done this there won't be any dependency issues, so can start comparison changes. @@ -759,6 +911,8 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion + _targetTabularModel.CleanUpAggregations(); + #region Cultures //Restore cultures that were backed up earlier. Having done this there won't be any dependency issues, so can start comparison changes. @@ -1026,7 +1180,56 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion - //DataSources + #region Model + + private bool UpdateModel(ComparisonObject comparisonObject, bool beforeTables) + { + if (comparisonObject.ComparisonObjectType == ComparisonObjectType.Model && comparisonObject.MergeAction == MergeAction.Update) + { + Model sourceModel = _sourceTabularModel.Model; + Model targetModel = _targetTabularModel.Model; + + bool targetHasCalcGroups = false; + foreach (Table table in _targetTabularModel.Tables) + { + if (table.IsCalculationGroup) + { + targetHasCalcGroups = true; + break; + } + } + + if (beforeTables) + { + //In this case, may need to create calc groups downstream, so may need to set DiscourageImplicitMeasures to true + if (!targetHasCalcGroups && sourceModel.TomModel.DiscourageImplicitMeasures) + { + _targetTabularModel.UpdateModel(sourceModel, targetModel); + OnValidationMessage(new ValidationMessageEventArgs($"Update model.", ValidationMessageType.Model, ValidationMessageStatus.Informational)); + return true; + } + } + else + { + //In this case, have already had chance to create/delete calc groups, so OK to disable implicit measures if able + if (targetHasCalcGroups && sourceModel.TomModel.DiscourageImplicitMeasures == false) + { + OnValidationMessage(new ValidationMessageEventArgs($"Unable to update model because (considering changes) the target has calculation group(s) and the source has DiscourageImplicitMeasures set to false.", ValidationMessageType.Model, ValidationMessageStatus.Warning)); + } + else + { + _targetTabularModel.UpdateModel(sourceModel, targetModel); + OnValidationMessage(new ValidationMessageEventArgs($"Update model.", ValidationMessageType.Model, ValidationMessageStatus.Informational)); + return true; + } + } + } + return false; + } + + #endregion + + #region DataSources private void DeleteDataSource(ComparisonObject comparisonObject) { @@ -1118,7 +1321,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } } - //Expressions + #endregion + + #region Expressions private void DeleteExpression(ComparisonObject comparisonObject) { @@ -1134,7 +1339,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata else { string message = $"Unable to delete expression {comparisonObject.TargetObjectName} because the following objects depend on it: {String.Join(", ", warningObjectList)}."; - if (_comparisonInfo.OptionsInfo.OptionRetainPartitions) + if (_comparisonInfo.OptionsInfo.OptionRetainPartitions && !_comparisonInfo.OptionsInfo.OptionRetainPolicyPartitions) { message += " Note: the option to retain partitions is on, which may be affecting this."; } @@ -1203,14 +1408,19 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } } - //Tables + #endregion + + #region Tables private void DeleteTable(ComparisonObject comparisonObject) { if (comparisonObject.ComparisonObjectType == ComparisonObjectType.Table && comparisonObject.MergeAction == MergeAction.Delete) { + Table targetTable = _targetTabularModel.Tables.FindByName(comparisonObject.TargetObjectName); + bool isCalculationGroup = false; + if (targetTable != null) isCalculationGroup = targetTable.IsCalculationGroup; _targetTabularModel.DeleteTable(comparisonObject.TargetObjectName); - OnValidationMessage(new ValidationMessageEventArgs($"Delete table '{comparisonObject.TargetObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational)); + OnValidationMessage(new ValidationMessageEventArgs($"Delete {(isCalculationGroup ? "calculation group" : "table")} '{comparisonObject.TargetObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational)); } } @@ -1223,25 +1433,43 @@ namespace BismNormalizer.TabularCompare.TabularMetadata bool fromDependencies = false; bool nonStructuredDataSourceLocal = false; - foreach (Partition partition in sourceTable.TomTable.Partitions) + if (!sourceTable.IsCalculationGroup) { - //Check any objects in source that this partition depends on are also going to be created if not already in target - if (HasBlockingFromDependenciesInSource(sourceTable.Name, partition.Name, CalcDependencyObjectType.Partition, ref warningObjectList, out bool nonStructuredDataSource)) + foreach (Partition partition in sourceTable.TomTable.Partitions) { - fromDependencies = true; - if (nonStructuredDataSource) - nonStructuredDataSourceLocal = true; - } + //Check any objects in source that this partition depends on are also going to be created if not already in target + if (HasBlockingFromDependenciesInSource(sourceTable.Name, partition.Name, CalcDependencyObjectType.Partition, ref warningObjectList, out bool nonStructuredDataSource)) + { + fromDependencies = true; + if (nonStructuredDataSource) + nonStructuredDataSourceLocal = true; + } - //For old non-M partitions, check if data source references exist - if (HasBlockingOldPartitionDependency(partition, ref warningObjectList)) - fromDependencies = true; //Need if clause in case last of n partitions has no dependencies and sets back to true + //For old non-M partitions, check if data source references exist + if (HasBlockingOldPartitionDependency(partition, ref warningObjectList)) + fromDependencies = true; //Need if clause in case last of n partitions has no dependencies and sets back to true + } } if (!fromDependencies) { - _targetTabularModel.CreateTable(sourceTable); - OnValidationMessage(new ValidationMessageEventArgs($"Create table '{comparisonObject.SourceObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational)); + if (sourceTable.IsCalculationGroup) + { + if (_targetTabularModel.Model.TomModel.DiscourageImplicitMeasures != true) + { + OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation group {comparisonObject.SourceObjectName} because the target model doesn't have DiscourageImplicitMeasures set to true.", ValidationMessageType.Table, ValidationMessageStatus.Warning)); + } + else + { + _targetTabularModel.CreateTable(sourceTable); + OnValidationMessage(new ValidationMessageEventArgs($"Create calculation group '{comparisonObject.SourceObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational)); + } + } + else + { + _targetTabularModel.CreateTable(sourceTable); + OnValidationMessage(new ValidationMessageEventArgs($"Create table '{comparisonObject.SourceObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational)); + } } else { @@ -1288,8 +1516,15 @@ namespace BismNormalizer.TabularCompare.TabularMetadata if (!fromDependencies) { - _targetTabularModel.UpdateTable(tableSource, tableTarget, out string retainPartitionsMessage); - OnValidationMessage(new ValidationMessageEventArgs($"Update table '{comparisonObject.TargetObjectName}'. {retainPartitionsMessage}", ValidationMessageType.Table, ValidationMessageStatus.Informational)); + if (tableSource.IsCalculationGroup != tableTarget.IsCalculationGroup) + { + OnValidationMessage(new ValidationMessageEventArgs($"Unable to update table {comparisonObject.TargetObjectName} because either source or target is a calculation group (but not both).", (tableSource.IsCalculationGroup ? ValidationMessageType.CalculationGroup : ValidationMessageType.Table), ValidationMessageStatus.Warning)); + } + else + { + _targetTabularModel.UpdateTable(tableSource, tableTarget, out string retainPartitionsMessage); + OnValidationMessage(new ValidationMessageEventArgs($"Update {(tableSource.IsCalculationGroup ? "calculation group" : "table")} '{comparisonObject.TargetObjectName}'. {retainPartitionsMessage}", ValidationMessageType.Table, ValidationMessageStatus.Informational)); + } } else { @@ -1305,7 +1540,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } } - //Relationships + #endregion + + #region Relationships private void DeleteRelationship(ComparisonObject comparisonObject) { @@ -1369,7 +1606,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } } - //Measures / KPIs + #endregion + + #region Measures / KPIs private void DeleteMeasure(ComparisonObject comparisonObject) { @@ -1440,6 +1679,84 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion + #region CalculationItems + + private void DeleteCalculationItem(ComparisonObject comparisonObject) + { + if ((comparisonObject.ComparisonObjectType == ComparisonObjectType.CalculationItem || comparisonObject.ComparisonObjectType == ComparisonObjectType.Kpi) && + comparisonObject.MergeAction == MergeAction.Delete) + { + foreach (Table tableTarget in _targetTabularModel.Tables) + { + CalculationItem calculationItemTarget = tableTarget.CalculationItems.FindByName(comparisonObject.TargetObjectInternalName); + + if (calculationItemTarget != null) + { + // CalculationItem may have already been deleted if parent table was deleted + tableTarget.DeleteCalculationItem(comparisonObject.TargetObjectInternalName); + break; + } + } + + OnValidationMessage(new ValidationMessageEventArgs($"Delete calculation item {comparisonObject.TargetObjectInternalName}.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Informational)); + } + } + + private void CreateCalculationItem(ComparisonObject comparisonObject, string tableName) + { + if ((comparisonObject.ComparisonObjectType == ComparisonObjectType.CalculationItem || comparisonObject.ComparisonObjectType == ComparisonObjectType.Kpi) && + comparisonObject.MergeAction == MergeAction.Create) + { + foreach (Table tableInTarget in _targetTabularModel.Tables) + { + CalculationItem calculationItemInTarget = tableInTarget.CalculationItems.FindByName(comparisonObject.SourceObjectInternalName); + + if (calculationItemInTarget != null) + { + OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation item {comparisonObject.SourceObjectInternalName} because name already exists in target model.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Warning)); + return; + } + } + + Table tableSource = _sourceTabularModel.Tables.FindByName(tableName); + Table tableTarget = _targetTabularModel.Tables.FindByName(tableName); + + if (tableTarget == null) + { + OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation item {comparisonObject.SourceObjectInternalName} because (considering changes) target table {tableName} does not exist.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Warning)); + return; + } + else if (!tableTarget.IsCalculationGroup) + { + OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation item {comparisonObject.SourceObjectInternalName} because the target table {tableName} is not a calculation group table.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Warning)); + return; + } + + //If we get here, can create calculationItem/kpi + CalculationItem calculationItemSource = tableSource.CalculationItems.FindByName(comparisonObject.SourceObjectInternalName); + tableTarget.CreateCalculationItem(calculationItemSource.TomCalculationItem); + OnValidationMessage(new ValidationMessageEventArgs($"Create calculation item {comparisonObject.SourceObjectInternalName}.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Informational)); + } + } + + private void UpdateCalculationItem(ComparisonObject comparisonObject, string tableName) + { + if ((comparisonObject.ComparisonObjectType == ComparisonObjectType.CalculationItem || comparisonObject.ComparisonObjectType == ComparisonObjectType.Kpi) && + comparisonObject.MergeAction == MergeAction.Update) + { + Table tableSource = _sourceTabularModel.Tables.FindByName(tableName); + Table tableTarget = _targetTabularModel.Tables.FindByName(tableName); + CalculationItem calculationItemSource = tableSource.CalculationItems.FindByName(comparisonObject.SourceObjectInternalName); + + tableTarget.UpdateCalculationItem(calculationItemSource.TomCalculationItem); + OnValidationMessage(new ValidationMessageEventArgs($"Update calculation item {comparisonObject.SourceObjectInternalName}.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Informational)); + } + } + + #endregion + + #endregion + /// /// Update target tabular model with changes defined by actions in ComparisonObject instances. /// diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/ComparisonObject.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/ComparisonObject.cs index 0fa8e96..3a4335d 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/ComparisonObject.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/ComparisonObject.cs @@ -44,7 +44,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } else { - if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi) + if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi || _comparisonObjectType == ComparisonObjectType.CalculationItem) { return " " + _sourceObject.Name; } @@ -114,7 +114,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } else { - if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi) + if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi || _comparisonObjectType == ComparisonObjectType.CalculationItem) { return " " + _targetObject.Name; } @@ -172,36 +172,42 @@ namespace BismNormalizer.TabularCompare.TabularMetadata switch (this.ComparisonObjectType) { //tabular objects - case ComparisonObjectType.DataSource: + case ComparisonObjectType.Model: sortKey = "A"; break; - case ComparisonObjectType.Expression: + case ComparisonObjectType.DataSource: sortKey = "B"; break; - case ComparisonObjectType.Table: + case ComparisonObjectType.Expression: sortKey = "C"; break; - case ComparisonObjectType.Relationship: + case ComparisonObjectType.Table: sortKey = "D"; break; - case ComparisonObjectType.Measure: + case ComparisonObjectType.Relationship: sortKey = "E"; break; - case ComparisonObjectType.Kpi: + case ComparisonObjectType.Measure: sortKey = "F"; break; - case ComparisonObjectType.Action: + case ComparisonObjectType.Kpi: sortKey = "G"; break; - case ComparisonObjectType.Perspective: + case ComparisonObjectType.CalculationItem: sortKey = "H"; break; - case ComparisonObjectType.Culture: + case ComparisonObjectType.Action: sortKey = "I"; break; - case ComparisonObjectType.Role: + case ComparisonObjectType.Perspective: sortKey = "J"; break; + case ComparisonObjectType.Culture: + sortKey = "K"; + break; + case ComparisonObjectType.Role: + sortKey = "L"; + break; default: sortKey = "Z"; diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Model.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Model.cs new file mode 100644 index 0000000..118b542 --- /dev/null +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Model.cs @@ -0,0 +1,65 @@ +using Microsoft.AnalysisServices.Tabular; +using Tom=Microsoft.AnalysisServices.Tabular; + +namespace BismNormalizer.TabularCompare.TabularMetadata +{ + /// + /// Abstraction of a tabular model [model] with properties and methods for comparison purposes. + /// + public class Model : TabularObject + { + #region Private Members + + private TabularModel _parentTabularModel; + private Tom.Model _tomModel; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the Model class using multiple parameters. + /// + /// TabularModel object that the Model object belongs to. + /// Tabular Object Model Model object abtstracted by the Model class. + public Model(TabularModel parentTabularModel, Tom.Model tomModel) : base(tomModel) + { + _parentTabularModel = parentTabularModel; + _tomModel = tomModel; + + PopulateProperties(); + } + + #endregion + + #region Properties + + /// + /// TabularModel object that the Model object belongs to. + /// + public TabularModel ParentTabularModel => _parentTabularModel; + + /// + /// Tabular Object Model Model object abtstracted by the Model class. + /// + public Tom.Model TomModel => _tomModel; + + public override string ToString() => this.GetType().FullName; + + #endregion + + private void PopulateProperties() + { + string customObjectDefinition = "{ "; + if (!string.IsNullOrEmpty(_tomModel.Description)) + { + customObjectDefinition += $"\"description\": \"{_tomModel.Description}\", "; + } + customObjectDefinition += $"\"defaultMode\": \"{_tomModel.DefaultMode.ToString().ToLower()}\", "; + customObjectDefinition += $"\"discourageImplicitMeasures\": {_tomModel.DiscourageImplicitMeasures.ToString().ToLower()} }}"; + base.SetCustomObjectDefinition(customObjectDefinition); + } + + + } +} diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RefreshPolicy.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RefreshPolicy.cs new file mode 100644 index 0000000..2c43312 --- /dev/null +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RefreshPolicy.cs @@ -0,0 +1,48 @@ +//using System; +//using System.Collections.Generic; +//using System.IO; +//using System.Linq; +//using System.Text; +//using System.Globalization; +//using Microsoft.AnalysisServices.Tabular; +//using Tom=Microsoft.AnalysisServices.Tabular; + +//namespace BismNormalizer.TabularCompare.TabularMetadata +//{ +// /// +// /// Abstraction of a tabular model refreshPolicy with properties and methods for comparison purposes. +// /// +// public class RefreshPolicy : TabularObject +// { +// private Table _parentTable; +// private Tom.RefreshPolicy _tomRefreshPolicy; + +// /// +// /// Initializes a new instance of the RefreshPolicy class using multiple parameters. +// /// +// /// Table object that the refreshPolicy belongs to. +// /// Tabular Object Model RefreshPolicy object abtstracted by the RefreshPolicy class. +// public RefreshPolicy(Table parentTable, Tom.RefreshPolicy tomRefreshPolicy) : base(tomRefreshPolicy) +// { +// _parentTable = parentTable; +// _tomRefreshPolicy = tomRefreshPolicy; +// } + +// /// +// /// Table object that the Relationship oject belongs to. +// /// +// public Table ParentTable => _parentTable; + +// /// +// /// Tabular Object Model RefreshPolicy object abtstracted by the RefreshPolicy class. +// /// +// public Tom.RefreshPolicy TomRefreshPolicy => _tomRefreshPolicy; + +// /// +// /// Name of the table that the RefreshPolicy oject belongs to. +// /// +// public string TableName => _tomRefreshPolicy.Table.Name; + +// public override string ToString() => this.GetType().FullName; +// } +//} diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RefreshPolicyCollection.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RefreshPolicyCollection.cs new file mode 100644 index 0000000..eeccb08 --- /dev/null +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RefreshPolicyCollection.cs @@ -0,0 +1,98 @@ +//using System; +//using System.Collections.Generic; + +//namespace BismNormalizer.TabularCompare.TabularMetadata +//{ +// /// +// /// Represents a collection of RefreshPolicy objects. +// /// +// public class RefreshPolicyCollection : List +// { +// /// +// /// Find an object in the collection by name. +// /// +// /// +// /// RefreshPolicy object if found. Null if not found. +// public RefreshPolicy FindByName(string name) +// { +// foreach (RefreshPolicy refreshPolicy in this) +// { +// if (refreshPolicy.Name == name) +// { +// return refreshPolicy; +// } +// } +// return null; +// } + +// /// +// /// A Boolean specifying whether the collection contains object by name. +// /// +// /// +// /// True if the object is found, or False if it's not found. +// public bool ContainsName(string name) +// { +// foreach (RefreshPolicy refreshPolicy in this) +// { +// if (refreshPolicy.Name == name) +// { +// return true; +// } +// } +// return false; +// } + +// /// +// /// A Boolean specifying whether the collection contains object by name searching without case sensitivity. +// /// +// /// +// /// True if the object is found, or False if it's not found. +// public bool ContainsNameCaseInsensitive(string name) +// { +// foreach (RefreshPolicy refreshPolicy in this) +// { +// if (refreshPolicy.Name.ToUpper() == name.ToUpper()) +// { +// return true; +// } +// } +// return false; +// } + +// /// +// /// Returns a collection of RefreshPolicy objects filtered by the parent table's name. +// /// +// /// +// /// RefreshPolicyCollection +// public RefreshPolicyCollection FilterByTableName(string tableName) +// { +// RefreshPolicyCollection returnRefreshPolicys = new RefreshPolicyCollection(); +// foreach (RefreshPolicy refreshPolicy in this) +// { +// if (refreshPolicy.TableName == tableName) +// { +// returnRefreshPolicys.Add(refreshPolicy); +// } +// } +// return returnRefreshPolicys; +// } + +// /// +// /// Removes an object from the collection by its name. +// /// +// /// +// /// True if the object was removed, or False if was not found. +// public bool RemoveByName(string name) +// { +// foreach (RefreshPolicy refreshPolicy in this) +// { +// if (refreshPolicy.Name == name) +// { +// this.Remove(refreshPolicy); +// return true; +// } +// } +// return false; +// } +// } +//} diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Table.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Table.cs index a54db9f..41aa289 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Table.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/Table.cs @@ -20,6 +20,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata private string _dataSourceName; private RelationshipCollection _relationships = new RelationshipCollection(); private MeasureCollection _measures = new MeasureCollection(); + private bool _isCalculationGroup; + private ModeType _tableModeType; + private CalculationItemCollection _calculationItems = new CalculationItemCollection(); /// /// Initializes a new instance of the Table class using multiple parameters. @@ -59,6 +62,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata /// public MeasureCollection Measures => _measures; + /// + /// True if the table is a calculation group. + /// + public bool IsCalculationGroup => _isCalculationGroup; + + public ModeType TableModeType => _tableModeType; + + /// + /// Collection of calculation items for the Table object. + /// + public CalculationItemCollection CalculationItems => _calculationItems; + /// /// Tabular Object Model Table object abtstracted by the Table class. /// @@ -68,16 +83,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { base.RemovePropertyFromObjectDefinition("measures"); + _isCalculationGroup = (_tomTable.CalculationGroup != null); _partitionsDefinition = ""; _dataSourceName = ""; - bool hasMOrQueryPartition = false; + bool hasMQueryOrPolicyPartition = false; //Associate table with a DataSource if possible. It's not possible if calc table or if M expression refers to a shared expression, or multiple data sources foreach (Partition partition in _tomTable.Partitions) { + _tableModeType = partition.Mode; if (partition.SourceType == PartitionSourceType.M) { - hasMOrQueryPartition = true; + hasMQueryOrPolicyPartition = true; //Check M dependency tree to see if all partitions refer only to a single DataSource CalcDependencyCollection calcDependencies = _parentTabularModel.MDependencies.DependenciesReferenceFrom(CalcDependencyObjectType.Partition, _tomTable.Name, partition.Name); @@ -105,17 +122,24 @@ namespace BismNormalizer.TabularCompare.TabularMetadata //If old partition, find the primary partition (first one) to determine DataSource. Technically it is possible for different partitions in the same table to point to different DataSources, but the Tabular Designer in VS doesn't support it. If set manually in .bim file, the UI still associates with the first partition (e.g. when processing table by itself, or deletinig the DataSource gives a warning message listing associated tables). if (partition.SourceType == PartitionSourceType.Query) { - hasMOrQueryPartition = true; + hasMQueryOrPolicyPartition = true; _dataSourceName = ((QueryPartitionSource)partition.Source).DataSource.Name; break; } + + //Might be a policy partition. + if (partition.SourceType == PartitionSourceType.PolicyRange) + { + hasMQueryOrPolicyPartition = true; + break; + } } - if (hasMOrQueryPartition) + if (hasMQueryOrPolicyPartition || _isCalculationGroup) { _partitionsDefinition = base.RetrievePropertyFromObjectDefinition("partitions"); - //Option to hide partitions only applies to M and query partitions (calculated tables hold dax defintitions in their partitions) + //Option to hide partitions only applies to M, query and policy partitions (calculated tables hold dax defintitions in their partitions) if (!_parentTabularModel.ComparisonInfo.OptionsInfo.OptionPartitions) { base.RemovePropertyFromObjectDefinition("partitions"); @@ -136,6 +160,15 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { _measures.Add(new Measure(this, measure, measure.KPI != null)); } + + //Find calc items + if (_isCalculationGroup) + { + foreach (Tom.CalculationItem calcItem in _tomTable.CalculationGroup.CalculationItems) + { + _calculationItems.Add(new CalculationItem(this, calcItem)); + } + } } #region Relationship collection methods @@ -174,8 +207,12 @@ namespace BismNormalizer.TabularCompare.TabularMetadata /// Find all direct relationships that filter this table. This is all ACTIVE relationships where 1) this is FROM table, or 2) this is TO table with CrossFilteringBehavior=BothDirections /// /// All the associated Relationships. - public List FindFilteringRelationships() + public List FindFilteredRelationships(bool checkSecurityBehavior = false) { + //T1[C1]->T2[C2] + //FromTableName: T1 *** this.Name + //ToTableName: T2 + //Considers DIRECT relationships for this table ONLY (1 level). List filteringRelationships = new List(); foreach (Table table in _parentTabularModel.Tables) @@ -184,7 +221,36 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { if (relationship.TomRelationship.IsActive && (relationship.FromTableName == this.Name || - (relationship.ToTableName == this.Name && relationship.TomRelationship.CrossFilteringBehavior == CrossFilteringBehavior.BothDirections) + (relationship.ToTableName == this.Name && relationship.TomRelationship.CrossFilteringBehavior == CrossFilteringBehavior.BothDirections && (!checkSecurityBehavior || (checkSecurityBehavior && relationship.TomRelationship.SecurityFilteringBehavior == SecurityFilteringBehavior.BothDirections))) + ) + ) + { + filteringRelationships.Add(relationship); + } + } + } + return filteringRelationships; + } + + /// + /// Find all direct relationships that filter this table. This is all ACTIVE relationships where 1) this is FROM table, or 2) this is TO table with CrossFilteringBehavior=BothDirections + /// + /// All the associated Relationships. + public List FindFilteringRelationships(bool checkSecurityBehavior = false) + { + //T1[C1]->T2[C2] + //FromTableName: T1 + //ToTableName: T2 *** this.Name + + //Considers DIRECT relationships for this table ONLY (1 level). + List filteringRelationships = new List(); + foreach (Table table in _parentTabularModel.Tables) + { + foreach (Relationship relationship in table.Relationships) + { + if (relationship.TomRelationship.IsActive && + (relationship.ToTableName == this.Name || + (relationship.FromTableName == this.Name && relationship.TomRelationship.CrossFilteringBehavior == CrossFilteringBehavior.BothDirections && (!checkSecurityBehavior || (checkSecurityBehavior && relationship.TomRelationship.SecurityFilteringBehavior == SecurityFilteringBehavior.BothDirections))) ) ) { @@ -311,6 +377,12 @@ namespace BismNormalizer.TabularCompare.TabularMetadata return false; } + if (this.IsCalculationGroup || _parentTabularModel.Tables.FindByName(tabularRelationshipSource.ToTable.Name).IsCalculationGroup) + { + warningMessage = $"Unable to create Relationship {relationshipName} because one or more tables is a calculation group."; + return false; + } + // Delete the target relationship with same tables/columns if still there. Not using RemoveByInternalName in case internal name is actually different. if (this.Relationships.ContainsName(relationshipSource.Name)) { @@ -403,10 +475,75 @@ namespace BismNormalizer.TabularCompare.TabularMetadata CreateMeasure(tomMeasureSource); } + // CalculationItems + + /// + /// Delete calculation item associated with the Table object. + /// + /// Name of the calculationItem to be deleted. + public void DeleteCalculationItem(string name) + { + if (_tomTable.CalculationGroup.CalculationItems.ContainsName(name)) + { + _tomTable.CalculationGroup.CalculationItems.Remove(name); + } + + // shell model + if (_calculationItems.ContainsName(name)) + { + _calculationItems.RemoveByName(name); + } + } + + /// + /// Create calculationItem associated with the Table object. + /// + /// Tabular Object Model CalculationItem object from the source tabular model to be abstracted in the target. + public void CreateCalculationItem(Tom.CalculationItem tomCalculationItemSource) + { + if (_tomTable.CalculationGroup.CalculationItems.ContainsName(tomCalculationItemSource.Name)) + { + _tomTable.CalculationGroup.CalculationItems.Remove(tomCalculationItemSource.Name); + } + + Tom.CalculationItem tomCalculationItemTarget = new Tom.CalculationItem(); + tomCalculationItemSource.CopyTo(tomCalculationItemTarget); + _tomTable.CalculationGroup.CalculationItems.Add(tomCalculationItemTarget); + + // shell model + _calculationItems.Add(new CalculationItem(this, tomCalculationItemTarget)); + } + + /// + /// Update calculationItem associated with the Table object. + /// + /// Tabular Object Model CalculationItem object from the source tabular model to be abstracted in the target. + public void UpdateCalculationItem(Tom.CalculationItem tomCalculationItemSource) + { + if (_calculationItems.ContainsName(tomCalculationItemSource.Name)) + { + DeleteCalculationItem(tomCalculationItemSource.Name); + } + CreateCalculationItem(tomCalculationItemSource); + } + #endregion #region Other public methods + /// + /// For option when retain storage mode in composite models. + /// + /// + public void ResetStorageMode(ModeType modeType) + { + foreach (Partition partition in _tomTable.Partitions) + { + partition.Mode = modeType; + } + _tableModeType = modeType; + } + /// /// A Boolean specifying whether the table contains a column with the same name searching without case sensitivity. /// diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TableCollection.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TableCollection.cs index a848c31..fabd33e 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TableCollection.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TableCollection.cs @@ -66,7 +66,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata /// Returns a collection of Table objects that do not have a DataSource associated with them. These can be calculated tables or tables with M partitions that do not refer to a DataSource. /// /// - public TableCollection WithoutDataSource(Model model) + public TableCollection WithoutDataSource(Microsoft.AnalysisServices.Tabular.Model model) { TableCollection tablesWithDataSource = new TableCollection(); foreach (Microsoft.AnalysisServices.Tabular.DataSource dataSource in model.DataSources) diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs index 9ff1477..0d2da88 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs @@ -22,6 +22,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata private ComparisonInfo _comparisonInfo; private Server _server; private Database _database; + private Model _model; private DataSourceCollection _dataSources = new DataSourceCollection(); private TableCollection _tables = new TableCollection(); private ExpressionCollection _expressions = new ExpressionCollection(); @@ -68,6 +69,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata InitializeCalcDependencies(); //Shell model + _model = new Model(this, _database.Model); foreach (Tom.DataSource dataSource in _database.Model.DataSources) { if (dataSource.Type == DataSourceType.Provider || dataSource.Type == DataSourceType.Structured) @@ -212,6 +214,15 @@ namespace BismNormalizer.TabularCompare.TabularMetadata set { _database = value; } } + /// + /// Model object. + /// + public Model Model + { + get { return _model; } + set { _model = value; } + } + /// /// Collection of DataSources for the TabularModel object. /// @@ -291,6 +302,25 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion + #region Model + + /// + /// Update Model associated with the TabularModel object. + /// + /// Model object from the source tabular model to be updated in the target. + /// Model object in the target tabular model to be updated. + public void UpdateModel(Model modelSource, Model modelTarget) + { + modelTarget.TomModel.Description = modelSource.TomModel.Description; + if (!_comparisonInfo.OptionsInfo.OptionRetainStorageMode) + { + modelTarget.TomModel.DefaultMode = modelSource.TomModel.DefaultMode; + } + modelTarget.TomModel.DiscourageImplicitMeasures = modelSource.TomModel.DiscourageImplicitMeasures; + } + + #endregion + #region DataSources /// @@ -323,7 +353,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata dataSourceSource.TomDataSource.CopyTo(providerTarget); _database.Model.DataSources.Add(providerTarget); - + // shell model _dataSources.Add(new DataSource(this, providerTarget)); } @@ -416,6 +446,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } tomTableTarget.Measures.Clear(); //Measures will be added separately later + tomTableTarget.CalculationGroup?.CalculationItems.Clear(); //Calculation items will be added separately later _database.Model.Tables.Add(tomTableTarget); _tables.Add(new Table(this, tomTableTarget)); @@ -430,6 +461,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { bool canRetainPartitions = CanRetainPartitions(tableSource, tableTarget, out retainPartitionsMessage); Tom.Table tomTableTargetOrig = tableTarget.TomTable.Clone(); + ModeType tableTargetModeType = tableTarget.TableModeType; List tomRelationshipsToAddBack = DeleteTable(tableTarget.Name); CreateTable(tableSource); @@ -462,50 +494,70 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { tableTarget.CreateMeasure(tomMeasureToAddBack); } + + //add back calculationItems + if (tomTableTargetOrig.CalculationGroup != null) + { + foreach (Tom.CalculationItem tomCalculationItemToAddBack in tomTableTargetOrig.CalculationGroup.CalculationItems) + { + tableTarget.CreateCalculationItem(tomCalculationItemToAddBack); + } + } + else + { + //add back storage mode if option selected + if (_comparisonInfo.OptionsInfo.OptionRetainStorageMode) + { + tableTarget.ResetStorageMode(tableTargetModeType); + } + } } public bool CanRetainPartitions(Table tableSource, Table tableTarget, out string retainPartitionsMessage) { + //Initialize variables retainPartitionsMessage = ""; - - //only applies to db deployment, and need option checked - if (!_comparisonInfo.OptionsInfo.OptionRetainPartitions) - return false; - - //both tables need to have M or query partitions. Also type needs to match (won't copy query partition to M table). If a table has no partitions, do nothing. - PartitionSourceType sourceTypeTarget = PartitionSourceType.None; + PartitionSourceType sourceTypeSource = PartitionSourceType.None; foreach (Partition partition in tableSource.TomTable.Partitions) { - sourceTypeTarget = partition.SourceType; + sourceTypeSource = partition.SourceType; break; } - if (!(sourceTypeTarget == PartitionSourceType.M || sourceTypeTarget == PartitionSourceType.Query)) + PartitionSourceType sourceTypeTarget = PartitionSourceType.None; + foreach (Partition partitionTarget in tableTarget.TomTable.Partitions) + { + sourceTypeTarget = partitionTarget.SourceType; + break; + } + + //Verify necessary options are checked + if (!_comparisonInfo.OptionsInfo.OptionRetainPartitions) + return false; + if (_comparisonInfo.OptionsInfo.OptionRetainPolicyPartitions && sourceTypeTarget != PartitionSourceType.PolicyRange) + return false; + + //both tables need to have M or query partitions, or target can be policy partitions. Also type needs to match (won't copy query partition to M table). If a table has no partitions, do nothing. + if (!(sourceTypeSource == PartitionSourceType.M || sourceTypeSource == PartitionSourceType.Query || sourceTypeSource == PartitionSourceType.PolicyRange)) { retainPartitionsMessage = $"Retain partitions not applicable to partition types."; return false; } - PartitionSourceType sourceTypeOrig = PartitionSourceType.None; - foreach (Partition partitionOrig in tableTarget.TomTable.Partitions) - { - sourceTypeOrig = partitionOrig.SourceType; - break; - } - if (!(sourceTypeOrig == PartitionSourceType.M || sourceTypeOrig == PartitionSourceType.Query)) + if (!(sourceTypeTarget == PartitionSourceType.M || sourceTypeTarget == PartitionSourceType.Query || sourceTypeTarget == PartitionSourceType.PolicyRange)) { retainPartitionsMessage = $"Retain partitions not applicable to partition types."; return false; } - if (sourceTypeOrig != sourceTypeTarget) + if ((sourceTypeTarget != sourceTypeSource) && sourceTypeTarget != PartitionSourceType.PolicyRange) { - retainPartitionsMessage = $"Retain partitions not applied because source partition type is {sourceTypeTarget.ToString()} and target partition type is {sourceTypeOrig.ToString()}."; + retainPartitionsMessage = $"Retain partitions not applied because source partition type is {sourceTypeSource.ToString()} and target partition type is {sourceTypeTarget.ToString()}."; return false; } if (tableSource.PartitionsDefinition == tableTarget.PartitionsDefinition) { - retainPartitionsMessage = "Source & target partition definitions match, so retain partitions not necessary."; + retainPartitionsMessage = "Source & target partition definitions already match, so retain partitions not necessary."; return false; } @@ -592,7 +644,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata //beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection. RelationshipChainsFromRoot referencedTableCollection = new RelationshipChainsFromRoot(); - foreach (Relationship filteringRelationship in beginTable.FindFilteringRelationships()) + foreach (Relationship filteringRelationship in beginTable.FindFilteredRelationships()) { // EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi); @@ -661,7 +713,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata chainsFromRoot.Add(link); Table beginTable = link.EndTable; //EndTable is now the begin table as iterating next level ... - foreach (Relationship filteringRelationship in beginTable.FindFilteringRelationships()) + foreach (Relationship filteringRelationship in beginTable.FindFilteredRelationships()) { // EndTable can be either the From or the To table of a Relationship object depending on direction of CrossFilteringBehavior string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi); @@ -694,7 +746,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata #endregion - #region Variation Cleanup + #region Variation / Aggregations Cleanup /// /// Remove variations referring to objects that don't exist. @@ -769,7 +821,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } } - //Check if any tables that have ShowAsVariationsOnly = true really have variatinos pointing at them + //Check if any tables that have ShowAsVariationsOnly = true really have variations pointing at them foreach (Table table in _tables) { if (table.TomTable.ShowAsVariationsOnly == true && !targetVariationTablesRemaining.Contains(table.Name)) @@ -779,6 +831,225 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } } + /// + /// Remove aggregations referring to objects that don't exist. + /// + public void CleanUpAggregations() + { + //modelTablesWithRls to be used for Rule 11 below: + List modelTablesWithRls = new List(); + foreach (Role role in _roles) + { + foreach (TablePermission tablePermission in role.TomRole.TablePermissions) + { + if (!String.IsNullOrEmpty(tablePermission.FilterExpression)) + { + modelTablesWithRls.Add(tablePermission.Name); + } + } + } + + foreach (Table table in _tables) + { + bool foundViolation = false; + string warningMessage = ""; + + foreach (Column column in table.TomTable.Columns) + { + if (!foundViolation) + { + /* Check aggs refer to valid base tables/columns + */ + + if (column.AlternateOf?.BaseTable != null) + { + if (!_database.Model.Tables.ContainsName(column.AlternateOf.BaseTable.Name)) + { + //Base table doesn't exist + foundViolation = true; + warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) refers to detail table that does not exist [table:{column.AlternateOf.BaseTable.Name}].\n"; + break; + } + } + else if (column.AlternateOf?.BaseColumn != null) + { + if (_database.Model.Tables.ContainsName(column.AlternateOf.BaseColumn.Table?.Name)) + { + //the referenced table is there, how about the referenced column? + if (!_database.Model.Tables.Find(column.AlternateOf.BaseColumn.Table.Name).Columns.ContainsName(column.AlternateOf.BaseColumn.Name)) + { + //Base column does not exist + foundViolation = true; + warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) refers to detail column that does not exist [table:{column.AlternateOf.BaseColumn.Table.Name}/column:{column.AlternateOf.BaseColumn.Name}].\n"; + break; + } + } + else + { + //Base table does not exist + foundViolation = true; + warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) refers to detail table that does not exist [table:{column.AlternateOf.BaseColumn.Table.Name}].\n"; + break; + } + } + + string detailTableName = null; + if (!foundViolation && column.AlternateOf != null) + { + detailTableName = (column.AlternateOf.BaseTable != null ? column.AlternateOf.BaseTable.Name : column.AlternateOf.BaseColumn.Table.Name); + } + Table detailTable = _tables.FindByName(detailTableName); + + if (!foundViolation && column.AlternateOf != null && column.AlternateOf.Summarization != SummarizationType.GroupBy && modelTablesWithRls.Count > 0 && detailTable != null) + { + /* Rule 11: RLS expressions that can filter the agg table, must also be able to filter the detail table(s) using an active relationship + */ + + //Get list of filtering RLS tables that filter the agg table + List rlsTablesFilteringAgg = new List(); //RLS tables that filter the agg table + + //beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection. + RelationshipChainsFromRoot referencedTableCollection = new RelationshipChainsFromRoot(); + foreach (Relationship filteringRelationship in table.FindFilteredRelationships(checkSecurityBehavior: true)) + { + // EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior/SecurityBehavior + string endTableName = GetEndTableName(table, filteringRelationship, out bool biDi); + RelationshipLink rootLink = new RelationshipLink(table, _tables.FindByName(endTableName), true, "", false, filteringRelationship, biDi); + ValidateLinkForAggsRls(rootLink, referencedTableCollection, modelTablesWithRls, rlsTablesFilteringAgg); + } + + //If the agg table itself has RLS on it, then consider it a table that is filtering the agg too + if (modelTablesWithRls.Contains(table.Name)) + { + rlsTablesFilteringAgg.Add(table.Name); + } + + if (rlsTablesFilteringAgg.Count > 0) + { + //Get list of filtering RLS tables on the detail table + List rlsTablesFilteringDetail = new List(); //RLS tables that filter the detail table + + //beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection. + referencedTableCollection = new RelationshipChainsFromRoot(); + foreach (Relationship filteringRelationship in detailTable.FindFilteredRelationships(checkSecurityBehavior: true)) + { + // EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior/SecurityBehavior + string endTableName = GetEndTableName(detailTable, filteringRelationship, out bool biDi); + RelationshipLink rootLink = new RelationshipLink(detailTable, _tables.FindByName(endTableName), true, "", false, filteringRelationship, biDi); + ValidateLinkForAggsRls(rootLink, referencedTableCollection, modelTablesWithRls, rlsTablesFilteringDetail); + } + + //For each agg table, check any RLS filter tables also covers the detail table + foreach (string rlsTableFilteringAgg in rlsTablesFilteringAgg) + { + if (!rlsTablesFilteringDetail.Contains(rlsTableFilteringAgg)) + { + foundViolation = true; + warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) RLS filter on table {rlsTableFilteringAgg} that filters the agg, but does not filter detail table {detailTableName}.\n"; + break; + } + } + } + } + + if (!foundViolation && column.AlternateOf != null) + { + /* Rule 10: Relationships between aggregation tables and other (non-aggregation) tables are not allowed if the aggregation table is on the filtering side of a relationship (active or inactive relationships). + This rule applies whether relationships are weak or strong, whether BiDi or not [including to-many BiDi, not just to-one] + */ + + //beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection. + RelationshipChainsFromRoot referencedTableCollection = new RelationshipChainsFromRoot(); + foreach (Relationship filteringRelationship in table.FindFilteringRelationships()) + { + // EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior/SecurityBehavior + string endTableName = GetEndTableName(table, filteringRelationship, out bool biDi); + Table endTable = _tables.FindByName(endTableName); + if (endTable != null) + { + bool endTableContainsAggs = false; + foreach (Column col in endTable.TomTable.Columns) + { + if (col.AlternateOf != null) + { + //End table has at least 1 agg so we are good + endTableContainsAggs = true; + break; + } + } + + if (!endTableContainsAggs) + { + foundViolation = true; + warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) the agg table is on the filtering side of a relationship to a table ({endTable.Name}) that does not contain aggregations, which is not allowed.\n"; + break; + } + } + } + } + + if (!foundViolation && column.AlternateOf != null && detailTable != null) + { + /* Rule 3: Chained aggregations are disallowed + */ + + foreach (Column detailColumn in detailTable.TomTable.Columns) + { + if (detailColumn.AlternateOf != null) + { + foundViolation = true; + warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) the detail table {detailTableName} also contains aggregations, which is not allowed.\n"; + break; + } + } + } + } + } + + //Clear all aggs on the agg table + if (foundViolation) + { + _parentComparison.OnValidationMessage(new ValidationMessageEventArgs(warningMessage, ValidationMessageType.AggregationDependency, ValidationMessageStatus.Warning)); + + foreach (Column column in table.TomTable.Columns) + { + if (column.AlternateOf != null) + { + column.AlternateOf = null; + } + } + } + } + } + + private void ValidateLinkForAggsRls(RelationshipLink link, RelationshipChainsFromRoot chainsFromRoot, List modelTablesWithRls, List rlsTablesFiltering) + { + if (link.FilteringRelationship.TomRelationship.IsActive) + { + if (modelTablesWithRls.Contains(link.EndTable.Name) && !rlsTablesFiltering.Contains(link.EndTable.Name)) + { + rlsTablesFiltering.Add(link.EndTable.Name); + } + + //Add the link to the chain and re-iterate ... + chainsFromRoot.Add(link); + + Table beginTable = link.EndTable; //EndTable is now the begin table as iterating next level ... + foreach (Relationship filteringRelationship in beginTable.FindFilteredRelationships(checkSecurityBehavior: true)) + { + // EndTable can be either the From or the To table of a Relationship object depending on direction of CrossFilteringBehavior + string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi); + + //Need to check if endTableName has already been covered by TablePath to avoid CrossFilteringBehavior leading both ways and never ending loop + if (!link.TablePath.Contains("'" + endTableName + "'")) + { + RelationshipLink newLink = new RelationshipLink(beginTable, _tables.FindByName(endTableName), false, link.TablePath, link.PrecedingPathBiDiInvoked, filteringRelationship, biDi); + ValidateLinkForAggsRls(newLink, chainsFromRoot, modelTablesWithRls, rlsTablesFiltering); + } + } + } + } + #endregion #region Backup / Restore @@ -1275,7 +1546,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata break; } - //If namedObjectTarget is null, the model object doesn't exist in target, so can ignore + //If namedObjectTarget is null, the model object does not exist in target, so can ignore if (namedObjectTarget != null) { //Does the translation already exist in cultureTarget? @@ -1683,7 +1954,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata traceEvent.Columns.Add(Amo.TraceColumn.IntegerData); traceEvent.Columns.Add(Amo.TraceColumn.SessionID); traceEvent.Columns.Add(Amo.TraceColumn.Spid); - trace.Update(); + trace.Update(Amo.UpdateOptions.Default, Amo.UpdateMode.CreateOrReplace); trace.OnEvent += new TraceEventHandler(Trace_OnEvent); trace.Start(); @@ -1707,8 +1978,20 @@ namespace BismNormalizer.TabularCompare.TabularMetadata // Show row count for each table foreach (ProcessingTable table in _tablesToProcess) { - int rowCount = _connectionInfo.DirectQuery ? 0 : Core.Comparison.FindRowCount(_server, table.Name, _database.Name); - _parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, $"Success. {String.Format("{0:#,###0}", rowCount)} rows transferred.", DeploymentStatus.Success)); + string message = ""; + if ( + this._tables.FindByName(table.Name)?.TableModeType == ModeType.DirectQuery || + (this._tables.FindByName(table.Name)?.TableModeType == ModeType.Default && _database.Model.DefaultMode == ModeType.DirectQuery) + ) + { + message = "Success. 0 rows transferred (DirectQuery)."; + } + else + { + int rowCount = Core.Comparison.FindRowCount(_server, table.Name, _database.Name); + message = $"Success. {String.Format("{0:#,###0}", rowCount)} rows transferred."; + } + _parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, message, DeploymentStatus.Success)); } _parentComparison.OnDeploymentComplete(new DeploymentCompleteEventArgs(DeploymentStatus.Success, null)); } @@ -1785,7 +2068,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata PartitionRowCounter partition = processingTable.FindPartition(partitionNodeList[0].InnerText); partition.RowCount = e.IntegerData; - _parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(processingTable.Name, $"Retreived {String.Format("{0:#,###0}", processingTable.GetRowCount())} rows ...", DeploymentStatus.Deploying)); + _parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(processingTable.Name, $"Retrieved {String.Format("{0:#,###0}", processingTable.GetRowCount())} rows ...", DeploymentStatus.Deploying)); } } @@ -1860,10 +2143,10 @@ namespace BismNormalizer.TabularCompare.TabularMetadata private void FinalValidation() { - if (_connectionInfo.DirectQuery && _dataSources.Count > 1) - { - throw new InvalidOperationException("Target model contains multiple data sources, which are not allowed for Direct Query models. Re-run comparison and (considering changes) ensure there is a single connection in the target model."); - } + //if (_connectionInfo.DirectQuery && _dataSources.Count > 1) + //{ + // throw new InvalidOperationException("Target model contains multiple data sources, which are not allowed for Direct Query models. Re-run comparison and (considering changes) ensure there is a single connection in the target model."); + //} } public override string ToString() => this.GetType().FullName; diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularObject.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularObject.cs index 2a2204b..287d65a 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularObject.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularObject.cs @@ -24,6 +24,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata public TabularObject(NamedMetadataObject namedMetaDataObject) { _name = namedMetaDataObject.Name; + if (namedMetaDataObject is Tom.Model) return; //Model has custom JSON string //Serialize json SerializeOptions options = new SerializeOptions(); @@ -40,9 +41,23 @@ namespace BismNormalizer.TabularCompare.TabularMetadata _objectDefinition = token.ToString(Formatting.Indented); } + //todo: remove with Giri's fix + //Remove return characters + if (namedMetaDataObject is Tom.NamedExpression || namedMetaDataObject is Tom.Table) + { + _objectDefinition = _objectDefinition.Replace("\\r", ""); + } + //Order table columns if (namedMetaDataObject is Tom.Table) - { + { + if (((Tom.Table)namedMetaDataObject).CalculationGroup != null) + { + JToken token = JToken.Parse(_objectDefinition); + RemovePropertyFromObjectDefinition(token, "calculationItems"); + _objectDefinition = token.ToString(Formatting.Indented); + } + _objectDefinition = SortArray(_objectDefinition, "columns"); _objectDefinition = SortArray(_objectDefinition, "partitions"); } @@ -111,6 +126,14 @@ namespace BismNormalizer.TabularCompare.TabularMetadata _objectDefinition = jObj.ToString(Formatting.Indented); } + /// + /// Set a custom JSON string. An example is for the model class which contains properties that cannot be set. + /// + public void SetCustomObjectDefinition(string customObjectDefinition) + { + _objectDefinition = JToken.Parse(customObjectDefinition).ToString(); + } + /// /// Retrieve a JSON property definition from the full object definition. An example is partitions. /// diff --git a/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.Designer.cs b/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.Designer.cs index 3a9fc13..bff6554 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.Designer.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.Designer.cs @@ -58,16 +58,16 @@ this.btnOptions = new System.Windows.Forms.ToolStripButton(); this.btnReportDifferences = new System.Windows.Forms.ToolStripButton(); this.scDifferenceResults = new System.Windows.Forms.SplitContainer(); - this.pnlProgressBar = new System.Windows.Forms.Panel(); - this.progressBar = new System.Windows.Forms.ProgressBar(); - this.lblProgressBar = new System.Windows.Forms.Label(); - this.treeGridComparisonResults = new BismNormalizer.TabularCompare.UI.TreeGridViewComparison(); this.TreeGridImageList = new System.Windows.Forms.ImageList(this.components); this.scObjectDefinitions = new System.Windows.Forms.SplitContainer(); - this.txtSourceObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox(); this.label4 = new System.Windows.Forms.Label(); - this.txtTargetObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox(); this.label5 = new System.Windows.Forms.Label(); + this.progressBar = new System.Windows.Forms.ToolStripProgressBar(); + this.pnlProgressBar = new System.Windows.Forms.StatusStrip(); + this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.treeGridComparisonResults = new BismNormalizer.TabularCompare.UI.TreeGridViewComparison(); + this.txtSourceObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox(); + this.txtTargetObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox(); this.pnlHeader.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.spltSourceTarget)).BeginInit(); this.spltSourceTarget.Panel1.SuspendLayout(); @@ -78,12 +78,12 @@ this.scDifferenceResults.Panel1.SuspendLayout(); this.scDifferenceResults.Panel2.SuspendLayout(); this.scDifferenceResults.SuspendLayout(); - this.pnlProgressBar.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.scObjectDefinitions)).BeginInit(); this.scObjectDefinitions.Panel1.SuspendLayout(); this.scObjectDefinitions.Panel2.SuspendLayout(); this.scObjectDefinitions.SuspendLayout(); + this.pnlProgressBar.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).BeginInit(); this.SuspendLayout(); // // pnlHeader @@ -92,8 +92,9 @@ this.pnlHeader.Controls.Add(this.toolStrip1); this.pnlHeader.Dock = System.Windows.Forms.DockStyle.Top; this.pnlHeader.Location = new System.Drawing.Point(0, 0); + this.pnlHeader.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.pnlHeader.Name = "pnlHeader"; - this.pnlHeader.Size = new System.Drawing.Size(653, 55); + this.pnlHeader.Size = new System.Drawing.Size(980, 85); this.pnlHeader.TabIndex = 46; // // spltSourceTarget @@ -101,6 +102,7 @@ this.spltSourceTarget.Dock = System.Windows.Forms.DockStyle.Fill; this.spltSourceTarget.IsSplitterFixed = true; this.spltSourceTarget.Location = new System.Drawing.Point(0, 25); + this.spltSourceTarget.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.spltSourceTarget.Name = "spltSourceTarget"; // // spltSourceTarget.Panel1 @@ -112,16 +114,18 @@ // this.spltSourceTarget.Panel2.Controls.Add(this.txtTarget); this.spltSourceTarget.Panel2.Controls.Add(this.label2); - this.spltSourceTarget.Size = new System.Drawing.Size(653, 30); - this.spltSourceTarget.SplitterDistance = 321; + this.spltSourceTarget.Size = new System.Drawing.Size(980, 60); + this.spltSourceTarget.SplitterDistance = 481; + this.spltSourceTarget.SplitterWidth = 6; this.spltSourceTarget.TabIndex = 45; // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(2, 8); + this.label1.Location = new System.Drawing.Point(3, 12); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(41, 13); + this.label1.Size = new System.Drawing.Size(60, 20); this.label1.TabIndex = 39; this.label1.Text = "Source"; // @@ -131,9 +135,10 @@ | System.Windows.Forms.AnchorStyles.Right))); this.txtSource.BackColor = System.Drawing.SystemColors.Control; this.txtSource.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.txtSource.Location = new System.Drawing.Point(49, 7); + this.txtSource.Location = new System.Drawing.Point(74, 11); + this.txtSource.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtSource.Name = "txtSource"; - this.txtSource.Size = new System.Drawing.Size(269, 20); + this.txtSource.Size = new System.Drawing.Size(401, 26); this.txtSource.TabIndex = 41; this.txtSource.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_KeyDown); // @@ -143,9 +148,10 @@ | System.Windows.Forms.AnchorStyles.Right))); this.txtTarget.BackColor = System.Drawing.SystemColors.Control; this.txtTarget.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.txtTarget.Location = new System.Drawing.Point(45, 7); + this.txtTarget.Location = new System.Drawing.Point(68, 11); + this.txtTarget.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtTarget.Name = "txtTarget"; - this.txtTarget.Size = new System.Drawing.Size(272, 20); + this.txtTarget.Size = new System.Drawing.Size(408, 26); this.txtTarget.TabIndex = 42; this.txtTarget.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_KeyDown); // @@ -154,9 +160,10 @@ this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(3, 8); + this.label2.Location = new System.Drawing.Point(4, 12); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(38, 13); + this.label2.Size = new System.Drawing.Size(55, 20); this.label2.TabIndex = 40; this.label2.Text = "Target"; // @@ -173,7 +180,8 @@ this.btnReportDifferences}); this.toolStrip1.Location = new System.Drawing.Point(0, 0); this.toolStrip1.Name = "toolStrip1"; - this.toolStrip1.Size = new System.Drawing.Size(653, 25); + this.toolStrip1.Padding = new System.Windows.Forms.Padding(0, 0, 2, 0); + this.toolStrip1.Size = new System.Drawing.Size(980, 25); this.toolStrip1.TabIndex = 46; this.toolStrip1.Text = "toolStrip1"; // @@ -325,14 +333,15 @@ this.btnReportDifferences.Image = ((System.Drawing.Image)(resources.GetObject("btnReportDifferences.Image"))); this.btnReportDifferences.ImageTransparentColor = System.Drawing.Color.Magenta; this.btnReportDifferences.Name = "btnReportDifferences"; - this.btnReportDifferences.Size = new System.Drawing.Size(124, 20); + this.btnReportDifferences.Size = new System.Drawing.Size(124, 22); this.btnReportDifferences.Text = "Report Differences"; this.btnReportDifferences.Click += new System.EventHandler(this.btnReportDifferences_Click); // // scDifferenceResults // this.scDifferenceResults.Dock = System.Windows.Forms.DockStyle.Fill; - this.scDifferenceResults.Location = new System.Drawing.Point(0, 55); + this.scDifferenceResults.Location = new System.Drawing.Point(0, 85); + this.scDifferenceResults.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.scDifferenceResults.Name = "scDifferenceResults"; this.scDifferenceResults.Orientation = System.Windows.Forms.Orientation.Horizontal; // @@ -344,75 +353,11 @@ // scDifferenceResults.Panel2 // this.scDifferenceResults.Panel2.Controls.Add(this.scObjectDefinitions); - this.scDifferenceResults.Size = new System.Drawing.Size(653, 510); - this.scDifferenceResults.SplitterDistance = 371; + this.scDifferenceResults.Size = new System.Drawing.Size(980, 784); + this.scDifferenceResults.SplitterDistance = 570; + this.scDifferenceResults.SplitterWidth = 6; this.scDifferenceResults.TabIndex = 2; // - // pnlProgressBar - // - this.pnlProgressBar.BackColor = System.Drawing.SystemColors.Control; - this.pnlProgressBar.Controls.Add(this.progressBar); - this.pnlProgressBar.Controls.Add(this.lblProgressBar); - this.pnlProgressBar.Location = new System.Drawing.Point(60, 21); - this.pnlProgressBar.Name = "pnlProgressBar"; - this.pnlProgressBar.Size = new System.Drawing.Size(280, 54); - this.pnlProgressBar.TabIndex = 1; - this.pnlProgressBar.Visible = false; - // - // progressBar - // - this.progressBar.Location = new System.Drawing.Point(120, 16); - this.progressBar.Name = "progressBar"; - this.progressBar.Size = new System.Drawing.Size(137, 18); - this.progressBar.Step = 1; - this.progressBar.TabIndex = 1; - // - // lblProgressBar - // - this.lblProgressBar.AutoSize = true; - this.lblProgressBar.Location = new System.Drawing.Point(12, 16); - this.lblProgressBar.Name = "lblProgressBar"; - this.lblProgressBar.Size = new System.Drawing.Size(101, 13); - this.lblProgressBar.TabIndex = 0; - this.lblProgressBar.Text = "Generating report ..."; - // - // treeGridComparisonResults - // - this.treeGridComparisonResults.AllowUserToAddRows = false; - this.treeGridComparisonResults.AllowUserToDeleteRows = false; - this.treeGridComparisonResults.AllowUserToResizeRows = false; - this.treeGridComparisonResults.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; - dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control; - dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText; - dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; - dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; - this.treeGridComparisonResults.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; - this.treeGridComparisonResults.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.treeGridComparisonResults.Comparison = null; - dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window; - dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight; - dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False; - this.treeGridComparisonResults.DefaultCellStyle = dataGridViewCellStyle2; - this.treeGridComparisonResults.Dock = System.Windows.Forms.DockStyle.Fill; - this.treeGridComparisonResults.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter; - this.treeGridComparisonResults.ImageList = this.TreeGridImageList; - this.treeGridComparisonResults.Location = new System.Drawing.Point(0, 0); - this.treeGridComparisonResults.Name = "treeGridComparisonResults"; - this.treeGridComparisonResults.RowHeadersVisible = false; - this.treeGridComparisonResults.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; - this.treeGridComparisonResults.Size = new System.Drawing.Size(653, 371); - this.treeGridComparisonResults.TabIndex = 0; - this.treeGridComparisonResults.Unloading = false; - this.treeGridComparisonResults.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.treeGridComparisonResults_DataError); - this.treeGridComparisonResults.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeGridComparisonResults_MouseUp); - // // TreeGridImageList // this.TreeGridImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("TreeGridImageList.ImageStream"))); @@ -445,6 +390,7 @@ // this.scObjectDefinitions.Dock = System.Windows.Forms.DockStyle.Fill; this.scObjectDefinitions.Location = new System.Drawing.Point(0, 0); + this.scObjectDefinitions.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.scObjectDefinitions.Name = "scObjectDefinitions"; // // scObjectDefinitions.Panel1 @@ -458,10 +404,93 @@ this.scObjectDefinitions.Panel2.BackColor = System.Drawing.SystemColors.Control; this.scObjectDefinitions.Panel2.Controls.Add(this.txtTargetObjectDefinition); this.scObjectDefinitions.Panel2.Controls.Add(this.label5); - this.scObjectDefinitions.Size = new System.Drawing.Size(653, 135); - this.scObjectDefinitions.SplitterDistance = 331; + this.scObjectDefinitions.Size = new System.Drawing.Size(980, 208); + this.scObjectDefinitions.SplitterDistance = 496; + this.scObjectDefinitions.SplitterWidth = 6; this.scObjectDefinitions.TabIndex = 0; // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(6, 2); + this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(181, 20); + this.label4.TabIndex = 0; + this.label4.Text = "Source Object Definition"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(4, 2); + this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(176, 20); + this.label5.TabIndex = 1; + this.label5.Text = "Target Object Definition"; + // + // progressBar + // + this.progressBar.Name = "progressBar"; + this.progressBar.Size = new System.Drawing.Size(150, 16); + // + // pnlProgressBar + // + this.pnlProgressBar.ImageScalingSize = new System.Drawing.Size(36, 36); + this.pnlProgressBar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripStatusLabel1, + this.progressBar}); + this.pnlProgressBar.Location = new System.Drawing.Point(0, 548); + this.pnlProgressBar.Name = "pnlProgressBar"; + this.pnlProgressBar.Padding = new System.Windows.Forms.Padding(1, 0, 21, 0); + this.pnlProgressBar.Size = new System.Drawing.Size(980, 22); + this.pnlProgressBar.TabIndex = 49; + this.pnlProgressBar.Text = "Comparison Status"; + this.pnlProgressBar.Visible = false; + // + // toolStripStatusLabel1 + // + this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; + this.toolStripStatusLabel1.Size = new System.Drawing.Size(0, 17); + // + // treeGridComparisonResults + // + this.treeGridComparisonResults.AllowUserToAddRows = false; + this.treeGridComparisonResults.AllowUserToDeleteRows = false; + this.treeGridComparisonResults.AllowUserToResizeRows = false; + this.treeGridComparisonResults.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.treeGridComparisonResults.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; + this.treeGridComparisonResults.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.treeGridComparisonResults.Comparison = null; + dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.treeGridComparisonResults.DefaultCellStyle = dataGridViewCellStyle2; + this.treeGridComparisonResults.Dock = System.Windows.Forms.DockStyle.Fill; + this.treeGridComparisonResults.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter; + this.treeGridComparisonResults.ImageList = this.TreeGridImageList; + this.treeGridComparisonResults.Location = new System.Drawing.Point(0, 0); + this.treeGridComparisonResults.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.treeGridComparisonResults.Name = "treeGridComparisonResults"; + this.treeGridComparisonResults.RowHeadersVisible = false; + this.treeGridComparisonResults.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; + this.treeGridComparisonResults.Size = new System.Drawing.Size(980, 570); + this.treeGridComparisonResults.TabIndex = 0; + this.treeGridComparisonResults.Unloading = false; + this.treeGridComparisonResults.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.treeGridComparisonResults_DataError); + this.treeGridComparisonResults.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeGridComparisonResults_MouseUp); + // // txtSourceObjectDefinition // this.txtSourceObjectDefinition.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -469,25 +498,17 @@ | System.Windows.Forms.AnchorStyles.Right))); this.txtSourceObjectDefinition.BackColor = System.Drawing.Color.White; this.txtSourceObjectDefinition.Font = new System.Drawing.Font("Consolas", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.txtSourceObjectDefinition.Location = new System.Drawing.Point(0, 16); + this.txtSourceObjectDefinition.Location = new System.Drawing.Point(0, 25); + this.txtSourceObjectDefinition.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtSourceObjectDefinition.Name = "txtSourceObjectDefinition"; this.txtSourceObjectDefinition.ReadOnly = true; - this.txtSourceObjectDefinition.Size = new System.Drawing.Size(331, 119); + this.txtSourceObjectDefinition.Size = new System.Drawing.Size(494, 181); this.txtSourceObjectDefinition.TabIndex = 1; this.txtSourceObjectDefinition.Text = ""; this.txtSourceObjectDefinition.WordWrap = false; this.txtSourceObjectDefinition.vScroll += new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox.vScrollEventHandler(this.txtSourceObjectDefinition_vScroll); this.txtSourceObjectDefinition.KeyUp += new System.Windows.Forms.KeyEventHandler(this.txtSourceObjectDefinition_KeyUp); // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(4, 1); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(122, 13); - this.label4.TabIndex = 0; - this.label4.Text = "Source Object Definition"; - // // txtTargetObjectDefinition // this.txtTargetObjectDefinition.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -495,33 +516,26 @@ | System.Windows.Forms.AnchorStyles.Right))); this.txtTargetObjectDefinition.BackColor = System.Drawing.Color.White; this.txtTargetObjectDefinition.Font = new System.Drawing.Font("Consolas", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.txtTargetObjectDefinition.Location = new System.Drawing.Point(0, 16); + this.txtTargetObjectDefinition.Location = new System.Drawing.Point(0, 25); + this.txtTargetObjectDefinition.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtTargetObjectDefinition.Name = "txtTargetObjectDefinition"; this.txtTargetObjectDefinition.ReadOnly = true; - this.txtTargetObjectDefinition.Size = new System.Drawing.Size(313, 119); + this.txtTargetObjectDefinition.Size = new System.Drawing.Size(469, 181); this.txtTargetObjectDefinition.TabIndex = 2; this.txtTargetObjectDefinition.Text = ""; this.txtTargetObjectDefinition.WordWrap = false; this.txtTargetObjectDefinition.vScroll += new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox.vScrollEventHandler(this.txtTargetObjectDefinition_vScroll); this.txtTargetObjectDefinition.KeyUp += new System.Windows.Forms.KeyEventHandler(this.txtTargetObjectDefinition_KeyUp); // - // label5 - // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(3, 1); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(119, 13); - this.label5.TabIndex = 1; - this.label5.Text = "Target Object Definition"; - // // ComparisonControl // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.scDifferenceResults); this.Controls.Add(this.pnlHeader); + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "ComparisonControl"; - this.Size = new System.Drawing.Size(653, 565); + this.Size = new System.Drawing.Size(980, 869); this.Load += new System.EventHandler(this.BismNormalizer_Load); this.pnlHeader.ResumeLayout(false); this.pnlHeader.PerformLayout(); @@ -534,18 +548,19 @@ this.toolStrip1.ResumeLayout(false); this.toolStrip1.PerformLayout(); this.scDifferenceResults.Panel1.ResumeLayout(false); + this.scDifferenceResults.Panel1.PerformLayout(); this.scDifferenceResults.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.scDifferenceResults)).EndInit(); this.scDifferenceResults.ResumeLayout(false); - this.pnlProgressBar.ResumeLayout(false); - this.pnlProgressBar.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).EndInit(); this.scObjectDefinitions.Panel1.ResumeLayout(false); this.scObjectDefinitions.Panel1.PerformLayout(); this.scObjectDefinitions.Panel2.ResumeLayout(false); this.scObjectDefinitions.Panel2.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.scObjectDefinitions)).EndInit(); this.scObjectDefinitions.ResumeLayout(false); + this.pnlProgressBar.ResumeLayout(false); + this.pnlProgressBar.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).EndInit(); this.ResumeLayout(false); } @@ -559,9 +574,6 @@ private System.Windows.Forms.Label label4; private SynchronizedScrollRichTextBox txtTargetObjectDefinition; private System.Windows.Forms.Label label5; - private System.Windows.Forms.Panel pnlProgressBar; - private System.Windows.Forms.ProgressBar progressBar; - private System.Windows.Forms.Label lblProgressBar; private System.Windows.Forms.Panel pnlHeader; public System.Windows.Forms.ImageList TreeGridImageList; private System.Windows.Forms.SplitContainer spltSourceTarget; @@ -588,5 +600,8 @@ private System.Windows.Forms.ToolStripButton btnOptions; private System.Windows.Forms.ToolStripButton btnReportDifferences; private System.Windows.Forms.ToolStripMenuItem hideSkipObjectsWithSameDefinitionToolStripMenuItem; + private System.Windows.Forms.StatusStrip pnlProgressBar; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; + private System.Windows.Forms.ToolStripProgressBar progressBar; } } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.cs b/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.cs index e3ecd9c..7e8fd3d 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.cs @@ -131,7 +131,7 @@ namespace BismNormalizer.TabularCompare.UI //Blank file not saved to yet return; } - _comparisonInfo = ComparisonInfo.DeserializeBsmnFile(fileName); + _comparisonInfo = ComparisonInfo.DeserializeBsmnFile(fileName, "BISM Normalizer"); PopulateSourceTargetTextBoxes(); } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.resx b/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.resx index 63b01d9..be7f85c 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.resx +++ b/BismNormalizer/BismNormalizer/TabularCompare/UI/ComparisonControl.resx @@ -194,6 +194,9 @@ 3gAAAABJRU5ErkJggg== + + 405, 17 + 17, 17 @@ -201,354 +204,353 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 - ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA4 - UQAAAk1TRnQBSQFMAgEBFwEAAXwBAgF8AQIBEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA - AwABYAMAAQEBAAEgBgABYP8AKwADnwH/A58B/wOfAf8DnwH/ZAADbgG7A4AB/4QAA58B/wOfAf8DnwH/ - A58B/zAAAwcBCgNWAcEDLgFHEAADNAFUA1YBuwM6AWEQAAMHAQkDYAGZgAADoQH/A58B/wOfAf8DoQH/ - NwAB/wNTAa0QAANYAcADDAH8Az0BaRQAA4AB/wgAA4AB/wMUARoEAANVAYADUAF3ZAADoQH/A58B/wOf - Af8DnwH/GAADEgEZAyoBQRQAA0wBkQMAAf8MAAMEAQUDAAH/A0cBggMGAQgUAAN4Ad4DFAEaBAADUAF3 - A0wBbwQAA34B8wQAAzABQGAAA6EB/wOfAf8DnwH/A58B/xgAAzkBYAMAAf8DSwGOEAADIwE0AwAB/wNZ - AcQDWQHSA1sB0ANaAcoDAAH/AxoBJRgAA2gBqgNnAaoMAANzAcwDewHmVAADoQH/A58B/wOfAf8DnwH/ + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADy + UAAAAk1TRnQBSQFMAgEBFwEAAZQBAgGUAQIBEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABYAMAAQEBAAEgBgABYP8AKwADnwH/A58B/wOfAf8DnwH/ZAADWwG7A4AB/4QAA58B/wOfAf8DnwH/ + A58B/zAAAwcBCgNWAcEDLgFHEAADNAFUA1YBuwM6AWEQAAMHAQkDTgGZgAADoQH/A58B/wOfAf8DoQH/ + NwAB/wNTAa0QAANYAcADDwH8Az0BaRQAA4AB/wgAA4AB/wMTARoEAANHAYADQwF3ZAADoQH/A58B/wOf + Af8DnwH/GAADEgEZAyoBQRQAA0wBkQMAAf8MAAMEAQUDAAH/A0cBggMGAQgUAANoAd4DEwEaBAADQwF3 + A0ABbwQAA3MB8wQAAyoBQGAAA6EB/wOfAf8DnwH/A58B/xgAAzkBYAMAAf8DSwGOEAADIwE0AwAB/wNZ + AcQDWQHSA1sB0ANaAcoDAAH/AxoBJRgAA1UBqgNVAaoMAANiAcwDbwHmVAADoQH/A58B/wOfAf8DnwH/ A58B/wOhAf8DnwH/A58B/wOfAf8DnwH/A6EB/wOfAf8DnwH/A58B/wgAAzwBZwMAAf8DPwFvCAADWQHJ - AyoBQANZAcQDAAH/A1cBxQNXAcUDAAH/A1kB1xwAA0UBYgOAAf8MAAM9AVUDYAGZVAADnwH/A6EB/wOf + AyoBQANZAcQDAAH/A1cBxQNXAcUDAAH/A1kB1xwAAzoBYgOAAf8MAAM1AVUDTgGZVAADnwH/A6EB/wOf Af8DnwH/A6EB/wOhAf8DoQH/A58B/wOhAf8DoQH/A6EB/wOfAf8DoQH/A58B/wwAA0kBiQMAAf8EAAM4 - AVwDAAH/AwoBDgNEAXkDAAH/CwAB/wNKAY0cAAMUARoDgAH/DAADgAH/A2QBolQAA58B/wOfAf8DoQH/ - A58B/wOhAf8DnwH/A6EB/wOfAf8DnwH/A58B/wOfAf8DnwH/A6EB/wOfAf8QAANJAe8DWQHJAx4B+gNI - AYgEAAMfASwDAAH/Ax4BKwMdASoDAAH/Ax0BKSAAA4AB/wMqATgEAANdAZEDgAH/BAADYAGZA3sB6kwA + AVwDAAH/AwoBDgNEAXkDAAH/CwAB/wNKAY0cAAMTARoDgAH/DAADgAH/A1IBolQAA58B/wOfAf8DoQH/ + A58B/wOhAf8DnwH/A6EB/wOfAf8DnwH/A58B/wOfAf8DnwH/A6EB/wOfAf8QAANMAe8DWQHJAyEB+gNI + AYgEAAMfASwDAAH/Ax4BKwMdASoDAAH/Ax0BKSAAA4AB/wMmATgEAANNAZEDgAH/BAADTgGZA28B6kwA A58B/wOhAf8DnwH/A6EB/wOfAf8DoQH/A6EB/wOfAf8DoQH/A58B/wOfAf8DoQH/A58B/wOfAf8QAAMx - AU4DAAH/A1oB1QwAAx4B+gNKAYsDSgGKAygB+CQAA3wB7gNQAXd4AAOfAf8DnwH/A58B/wOfAf8YAAMl - ATcDQgF0AzgBXQM7AWUDAAH/A1MBsAMoAT0DQgF2Aw4BEwNCAXUDAAH/AwAB/wNIAYYgAANJAWsDewHm - A3gB3QNVAYB0AAOfAf8DnwH/A6EB/wOhAf8YAANKAYwDAAH/AwAB/wMAAf4DAAH+AwwB/AMAAf8DAAH/ - A1YBuwMMARADAAH/AwAB/wMdASooAAN/Afd4AAOhAf8DnwH/A58B/wOhAf8YAAMEAQUDCQEMAwABAQMK - AQ0DAAH/AzsBZAQAAwUBBwMPARQDBAQGAQgDBwEJLAADfAHueAADoQH/A6EB/wOhAf8DnwH/JAADAgED - AwAB/wM3AVtEAANHAWYDGgEiA3MBzHAAA58B/wOfAf8DnwH/A6EB/3gAAz0BVQNJAWt4AAMBAQL/AN0A + AU4DAAH/A1oB1QwAAyEB+gNKAYsDSgGKAzEB+CQAA3IB7gNDAXd4AAOfAf8DnwH/A58B/wOfAf8YAAMl + ATcDQgF0AzgBXQM7AWUDAAH/A1MBsAMoAT0DQgF2Aw4BEwNCAXUDAAH/AwAB/wNIAYYgAAM+AWsDbwHm + A2kB3QNHAYB0AAOfAf8DnwH/A6EB/wOhAf8YAANKAYwDAAH/AwAB/wMAAf4DAAH+Aw8B/AMAAf8DAAH/ + A1YBuwMMARADAAH/AwAB/wMdASooAAN3Afd4AAOhAf8DnwH/A58B/wOhAf8YAAMEAQUDCQEMAwABAQMK + AQ0DAAH/AzsBZAQAAwUBBwMPARQDBAQGAQgDBwEJLAADcgHueAADoQH/A6EB/wOhAf8DnwH/JAADAgED + AwAB/wM3AVtEAAM8AWYDGAEiA2IBzHAAA58B/wOfAf8DnwH/A6EB/3gAAzUBVQM+AWt4AAMBAQL/AN0A Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHz AfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/ - Af4B8wHyAf8EAAMqAf8DKgH/AyoB/wMqAf8DKgH/AyoB/wMqAf8DKgH/EAABwAGVAUkB/wHAAZUBSQH/ - hAAB/gHzAfIB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8DHQH/Af4B8wHyAf8EAAMqAf8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8DKgH/DAABwAGVAUkB/wGh - ATwBAAH/AcABlQFJAf8MAAPAAf8DkgH/A+oB/yAAA2MB6APAAf8UAAP+Af8D8QH/A8cB/wOPAf8DgAH/ - A6IB/wPsAf8UAAH+AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 - AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wP4Af8D+AH/ - A/gB/wP4Af8D+gH/A/wB/wPQAf8IAAHAAZUBSQH/AaEBPAEAAf8BwAGVAUkB/xAAA5IB/wOSAf8DkgH/ - A2MB6BgAA+sB/wOwAf8DDQEREAAD/QH/A9YB/wOVAf8DlQH/A50B/wOWAf8DiQH/A1MB/wPVAf8QAAH+ - AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 - AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wP4Af8D+AH/A/gB/wP8Af8BxgGf - AVkB/wGhATwBAAH/AaEBPAEAAf8BoQE8AQAB/wGzAVwBFQH/AaEBPAEAAf8BwAGVAUkB/xQAA+sB/wOQ + Af4B8wHyAf8EAAMnAf8DJwH/AycB/wMnAf8DJwH/AycB/wMnAf8DJwH/EAABwAGVAUYB/wHAAZUBRgH/ + hAAB/gHzAfIB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMa + Af8DGgH/Af4B8wHyAf8EAAMnAf8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8DJwH/DAABwAGVAUYB/wGh + ATkBAAH/AcABlQFGAf8MAAPAAf8DkgH/A+oB/yAAA18B6APAAf8UAAP+Af8D8QH/A8cB/wOPAf8DgAH/ + A6IB/wPsAf8UAAH+AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 + AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wP4Af8D+AH/ + A/gB/wP4Af8D+gH/A/wB/wPQAf8IAAHAAZUBRgH/AaEBOQEAAf8BwAGVAUYB/xAAA5IB/wOSAf8DkgH/ + A18B6BgAA+sB/wOwAf8DDQEREAAD/QH/A9YB/wOVAf8DlQH/A50B/wOWAf8DiQH/A1AB/wPVAf8QAAH+ + AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 + AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wP4Af8D+AH/A/gB/wP8Af8BxgGf + AVYB/wGhATkBAAH/AaEBOQEAAf8BoQE5AQAB/wGzAVkBEgH/AaEBOQEAAf8BwAGVAUYB/xQAA+sB/wOQ Af8DkgH/A5IB/wNSAakMAAMQARYD3wH/A50B/wMxAU0UAAPXAf8DogH/A6IB/wOiAf8DnQH/A5wB/wOc - Af8DnAH/A10B/wPSAf8MAAH+AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 - AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wP4 - Af8D+AH/A/oB/wHGAZ8BWQH/AbcBZAEkAf8B8gHwAesB/wP4Af8B8gHwAesB/wG3AWQBJAH/AbMBXAEV + Af8DnAH/A1oB/wPSAf8MAAH+AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 + AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wP4 + Af8D+AH/A/oB/wHGAZ8BVgH/AbcBYQEhAf8B8gHwAesB/wP4Af8B8gHwAesB/wG3AWEBIQH/AbMBWQES Af8cAANdAcoDkgH/A5AB/wOgAf8DMQFNBAADDQERA8AB/wOSAf8DUgGpFAAD9AH/A64B/wOuAf8DsAH/ - A7AB/wOuAf8DqAH/A6IB/wOcAf8DnAH/A1MB/wPsAf8IAAH+AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5 - AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMd - Af8B/gHzAfIB/wQAAyoB/wP4Af8D+AH/A/wB/wGhATwBAAH/AfIB8AHrAf8D+AH/A/gB/wP4Af8B8gHw - AesB/wGhATwBAAH/A8QB/wMqAf8DKgH/FAADVgGrA5AB/wOSAf8DnQH/Az4BawPAAf8DkAH/A2MB6AMD + A7AB/wOuAf8DqAH/A6IB/wOcAf8DnAH/A1AB/wPsAf8IAAH+AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5 + AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMa + Af8B/gHzAfIB/wQAAycB/wP4Af8D+AH/A/wB/wGhATkBAAH/AfIB8AHrAf8D+AH/A/gB/wP4Af8B8gHw + AesB/wGhATkBAAH/A8QB/wMnAf8DJwH/FAADVgGrA5AB/wOSAf8DnQH/Az4BawPAAf8DkAH/A18B6AMD AQQUAAPdAf8DvAH/A8IB/wPGAf8DxwH/A8EB/wO8Af8DrgH/A6IB/wOcAf8DiQH/A6IB/wgAAf4B8wHy - Af8DHQH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/ - AfkC6gH/AfkC6gH/AfkC6gH/Ax0B/wH+AfMB8gH/BAADKgH/A/gB/wP4Af8D/QH/AaEBPAEAAf8D+AH/ - A/gB/wP4Af8D+AH/A/gB/wGhATwBAAH/A/0B/wP4Af8DKgH/GAADPgFrA50B/wOSAf8DkAH/A5IB/wPr + Af8DGgH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/ + AfkC6gH/AfkC6gH/AfkC6gH/AxoB/wH+AfMB8gH/BAADJwH/A/gB/wP4Af8D/QH/AaEBOQEAAf8D+AH/ + A/gB/wP4Af8D+AH/A/gB/wGhATkBAAH/A/0B/wP4Af8DJwH/GAADPgFrA50B/wOSAf8DkAH/A5IB/wPr Af8DAwQEAQUUAAPWAf8DwgH/A/4B/wP+Af8D/gH/A/0B/wP8Af8D/AH/A/wB/wP6Af8DjwH/A4AB/wgA - Af4B8wHyAf8DHQH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/ - AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/Ax0B/wH+AfMB8gH/BAADKgH/A/gB/wP4Af8D/AH/AaEBPAEA - Af8B8gHwAesB/wP4Af8D+AH/A/gB/wHyAfAB6wH/AaEBPAEAAf8D/AH/A/gB/wMqAf8cAANcAckDkgH/ + Af4B8wHyAf8DGgH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/ + AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AxoB/wH+AfMB8gH/BAADJwH/A/gB/wP4Af8D/AH/AaEBOQEA + Af8B8gHwAesB/wP4Af8D+AH/A/gB/wHyAfAB6wH/AaEBOQEAAf8D/AH/A/gB/wMnAf8cAANcAckDkgH/ A5AB/wOdAf8DDQERHAAD1gH/A88N/wP+Af8D/gH/A/0B/wP8Af8D+wH/A48B/wOOAf8IAAH+AfMB8gH/ - Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 - AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wMqAf8DKgH/A2QB/wHGAZ8BWQH/AbkBiAE3 - Af8B8gHwAesB/wP4Af8B8gHwAesB/wG5AYgBNwH/AcYBnwFZAf8D+gH/A/gB/wMqAf8YAANSAakDkgH/ + AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 + AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wMnAf8DJwH/A2EB/wHGAZ8BVgH/AbkBiAE0 + Af8B8gHwAesB/wP4Af8B8gHwAesB/wG5AYgBNAH/AcYBnwFWAf8D+gH/A/gB/wMnAf8YAANSAakDkgH/ A5IB/wOQAf8DkgH/A1wByRAAAwsBDwgAA+kB/wPZAf8D7AH/A/AB/wPwAf8D7AH/A+EB/wPXAf8DwAH/ - A6gB/wOPAf8DxgH/CAAB/gHzAfIB/wMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq - Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Af4B8wHyAf8YAAHGAZ8BWQH/ - AaEBPAEAAf8BoQE8AQAB/wGhATwBAAH/AcYBnwFZAf8D/AH/A/gB/wP4Af8DKgH/FAADXAHJA5IB/wOQ + A6gB/wOPAf8DxgH/CAAB/gHzAfIB/wMaAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq + Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DGgH/Af4B8wHyAf8YAAHGAZ8BVgH/ + AaEBOQEAAf8BoQE5AQAB/wGhATkBAAH/AcYBnwFWAf8D/AH/A/gB/wP4Af8DJwH/FAADXAHJA5IB/wOQ Af8DnQH/Az4BawPAAf8DkgH/A0oBjBgAA/cB/wPZAf8D7AH/A/EB/wPwAf8D7AH/A+AB/wPWAf8DwAH/ - A6gB/wOcAf8D8AH/CAAB/gHzAfIB/wMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq - Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Af4B8wHyAf8cAAPEAf8D/QH/ - A/wB/wP6Af8D+AH/A/gB/wP4Af8DKgH/EAAD6gH/A5IB/wOQAf8DkAH/A1IBqQQAAw0BEQPgAf8DoAH/ + A6gB/wOcAf8D8AH/CAAB/gHzAfIB/wMaAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq + Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DGgH/Af4B8wHyAf8cAAPEAf8D/QH/ + A/wB/wP6Af8D+AH/A/gB/wP4Af8DJwH/EAAD6gH/A5IB/wOQAf8DkAH/A1IBqQQAAw0BEQPgAf8DoAH/ AzEBTRQAA/4B/wPsAf8D4AH/A/EB/wPxAf8D6gH/A+IB/wPSAf8DwQH/A6gB/wPXAf8D/gH/CAAB/gHz - AfIB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/ - Af4B8wHyAf8cAAMqAf8D+AH/A/gB/wP4Af8D+AH/AyoB/wMqAf8DigH/EAADkgH/A5IB/wOQAf8DYwHo + AfIB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/ + Af4B8wHyAf8cAAMnAf8D+AH/A/gB/wP4Af8D+AH/AycB/wMnAf8DigH/EAADkgH/A5IB/wOQAf8DXwHo EAAD6gH/A7AB/wMNAREUAAP9Af8D7AH/A90B/wPjAf8D5gH/A9wB/wPLAf8DuwH/A9YB/wP9Af8MAAH+ - AfMB8gH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8B/gHzAfIB/xwAAyoB/wP4Af8D+AH/A/gB/wP4Af8DKgH/A4oB/xQAA8AB/wOSAf8D6gH/GAADYwHo - A8AB/xgAA/0B/wP3Af8D6QH/A9wB/wPVAf8D4gH/A+8B/wP+Af8QAAH+AfMB8gH/Ax0B/wMdAf8DHQH/ - Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8B/gHzAfIB/xwAAyoB/wMq - Af8DKgH/AyoB/wMqAf8DigH/EwABAXgAAf4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHz + AfMB8gH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMa + Af8B/gHzAfIB/xwAAycB/wP4Af8D+AH/A/gB/wP4Af8DJwH/A4oB/xQAA8AB/wOSAf8D6gH/GAADXwHo + A8AB/xgAA/0B/wP3Af8D6QH/A9wB/wPVAf8D4gH/A+8B/wP+Af8QAAH+AfMB8gH/AxoB/wMaAf8DGgH/ + AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8B/gHzAfIB/xwAAycB/wMn + Af8DJwH/AycB/wMnAf8DigH/EwABAXgAAf4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHz AfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/ Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf//AAEAAf8BAAP/AQAD/wEAA/8BAAP/AQAD/wEA A/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/AysBQgMrAUIDMwFSAzcBWgM3 AVsDSgGJA0sBjgNKAY0DPQFnAzcBWwM3AVsDNAFUAycBOgMRARcIAAMrAUIDKwFCAysBQgMyAVADNwFa - A0gBgwNLAY4DSgGNA0QBegM3AVsDNwFbFAABJQG5AdMB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEA + A0gBgwNLAY4DSgGNA0QBegM3AVsDNwFbFAABIgG5AdMB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEA AYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGp - Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BJQG5AdMB/wEAAbkB0wH/AQABjAGpAf8BAAGMAakB/wEA + Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BIgG5AdMB/wEAAbkB0wH/AQABjAGpAf8BAAGMAakB/wEA AYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGp - Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAbkB0wH/AysBQgMdAf8DHQH/Ax0B/wMd - Af8DHQH/Ax0B/wMdAf8DQAFxAx0B/wMdAf8DNAH/A1wB6gNWAbQDPgFrBAADLgFIAyUB/wMlAf8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AzcBWxQAARQBtAHRAf8BAwHJAekB/wEAAcYB8QH/AQABwgHw + Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAbkB0wH/AysBQgMaAf8DGgH/AxoB/wMa + Af8DGgH/AxoB/wMaAf8DQAFxAxoB/wMaAf8DMQH/A1wB6gNWAbQDPgFrBAADLgFIAyIB/wMiAf8DIgH/ + AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AzcBWxQAAREBtAHRAf8BAAHJAekB/wEAAcYB8QH/AQABwgHw Af8BAAHBAfAB/wEAAcAB8AH/AQABwQHwAf8BAAHBAfAB/wEAAcEB8AH/AQABwQHwAf8BAAHCAfAB/wEA - AcMB8AH/AQABwwHwAf8BAAHEAfEB/wEAAcMB5AH/ARQBtAHRAf8BAAG0AdEB/wEAAckB6QH/AQABxgHx + AcMB8AH/AQABwwHwAf8BAAHEAfEB/wEAAcMB5AH/AREBtAHRAf8BAAG0AdEB/wEAAckB6QH/AQABxgHx Af8BAAHCAfAB/wEAAcEB8AH/AQABwAHwAf8BAAHBAfAB/wEAAcEB8AH/AQABwQHwAf8BAAHBAfAB/wEA - AcIB8AH/AQABwwHwAf8BAAHDAfAB/wEAAcQB8QH/AQABwwHkAf8BAAG0AdEB/wMrAUIDHQH/Ax0B/wMd - Af8DHQH/Ax0B/wMdAf8DHQH/A0gBgwMdAf8DHQH/Ax0B/wMdAf8DHQH/A0oBjQM/AW0DNQFVAyUB/wH5 - AuoB/wH5AuoB/wH5AuoB/wMlAf8B+QLqAf8B+QLqAf8B+QLqAf8DJQH/AzcBWwM3AVsDNwFbDAABTgHI + AcIB8AH/AQABwwHwAf8BAAHDAfAB/wEAAcQB8QH/AQABwwHkAf8BAAG0AdEB/wMrAUIDGgH/AxoB/wMa + Af8DGgH/AxoB/wMaAf8DGgH/A0gBgwMaAf8DGgH/AxoB/wMaAf8DGgH/A0oBjQM/AW0DNQFVAyIB/wH5 + AuoB/wH5AuoB/wH5AuoB/wMiAf8B+QLqAf8B+QLqAf8B+QLqAf8DIgH/AzcBWwM3AVsDNwFbDAABSwHI AdwB/wEAAa4BzwH/AQABzQHyAf8BAAHIAfEB/wEAAcUB8QH/AQABxAHxAf8BAAHDAfAB/wMAAf8DAAH/ - AQABxgHxAf8BAAHIAfEB/wEAAcoB8gH/AQABygHyAf8BAAHMAfEB/wEAAasBzQH/AU4ByAHcAv8BAAL/ + AQABxgHxAf8BAAHIAfEB/wEAAcoB8gH/AQABygHyAf8BAAHMAfEB/wEAAasBzQH/AUsByAHcAv8BAAL/ AQABrgHPAf8BAAHNAfIB/wEAAcgB8QH/AQABxQHxAf8BAAHEAfEB/wEAAcMB8AH/AwAB/wMAAf8BAAHG - AfEB/wEAAcgB8QH/AQABygHyAf8BAAHKAfIB/wEAAcwB8QH/AQABqwHNAv8BAAL/AysBQgMdAf8DHQH/ - Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DTAGTAx0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/AzcBWwM2AVgDJQH/ - AfkC6gH/AfkC6gH/AfkC6gH/AyUB/wH5AuoB/wH5AuoB/wH5AuoB/wMlAf8DNwFbAyUB/wNAAW4MAAHi - Ae4B8gH/ARQBtAHRAf8BBAHGAeQB/wEEAdEB8wH/AQABzQHyAf8BAAHLAfIB/wEAAcoB8gH/AwAB/wMA - Af8BAAHNAfMB/wEEAdEB8wH/AQUB0gH0Af8BBgHTAfQB/wEDAcUB4wH/ARQBtAHRAf8B4gHuAfIC/wEA + AfEB/wEAAcgB8QH/AQABygHyAf8BAAHKAfIB/wEAAcwB8QH/AQABqwHNAv8BAAL/AysBQgMaAf8DGgH/ + AxoB/wMaAf8DGgH/AxoB/wMaAf8DTAGTAxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWwM2AVgDIgH/ + AfkC6gH/AfkC6gH/AfkC6gH/AyIB/wH5AuoB/wH5AuoB/wH5AuoB/wMiAf8DNwFbAyIB/wNAAW4MAAHi + Ae4B8gH/AREBtAHRAf8BAQHGAeQB/wEBAdEB8wH/AQABzQHyAf8BAAHLAfIB/wEAAcoB8gH/AwAB/wMA + Af8BAAHNAfMB/wEBAdEB8wH/AQIB0gH0Af8BAwHTAfQB/wEAAcUB4wH/AREBtAHRAf8B4gHuAfIC/wEA Av8BAAG0AdEB/wEAAcYB5AH/AQAB0QHzAf8BAAHNAfIB/wEAAcsB8gH/AQABygHyAf8DAAH/AwAB/wEA - Ac0B8wH/AQAB0QHzAf8BAAHSAfQB/wEAAdMB9AH/AQABxQHjAf8BAAG0AdEC/wEAAv8DNQFVAx0B/wMd - Af8DHQH/A2IB6QMdAf8DHQH/Ax0B/wNRAZwDHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DNwFbAzIBUAMl - Af8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wM3AVsDJQH/A0oBihAAAYABzQHfAf8BAAGs - Ac0B/wESAdsB9AH/AQ4B2QH1Af8BCwHXAfUB/wEKAdYB9AH/AQoB1gH0Af8BDAHXAfUB/wEOAdkB9QH/ - AQ4B2QH1Af8BEAHbAfUB/wERAdoB8wH/AQABqgHLAf8BgAHNAd8B/wQAAf8BAAP/AQAC/wEAAawBzQH/ + Ac0B8wH/AQAB0QHzAf8BAAHSAfQB/wEAAdMB9AH/AQABxQHjAf8BAAG0AdEC/wEAAv8DNQFVAxoB/wMa + Af8DGgH/A2IB6QMaAf8DGgH/AxoB/wNRAZwDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DNwFbAzIBUAMi + Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wM3AVsDIgH/A0oBihAAAYABzQHfAf8BAAGs + Ac0B/wEPAdsB9AH/AQsB2QH1Af8BCAHXAfUB/wEHAdYB9AH/AQcB1gH0Af8BCQHXAfUB/wELAdkB9QH/ + AQsB2QH1Af8BDQHbAfUB/wEOAdoB8wH/AQABqgHLAf8BgAHNAd8B/wQAAf8BAAP/AQAC/wEAAawBzQH/ AQAB2wH0Af8BAAHZAfUB/wEAAdcB9QH/AQAB1gH0Af8BAAHWAfQB/wEAAdcB9QH/AQAB2QH1Af8BAAHZ - AfUB/wEAAdsB9QH/AQAB2gHzAf8BAAGqAcsC/wEAA/8BAAL/AzYBWAMdAf8DHQH/A10B0wNUAaYDXQHT - Ax0B/wMdAf8DWgG9Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/AzcBWwMxAU4DNwFaA0ABbgNKAY0DUgGk - A1wB6gNiAe8DYgHvAV8CXgHtA1wBzANIAYUDJQH/A0gBhQM3AVsDNwFbAzcBWwQAAeoB8gH1Af8BFAG0 - AdEB/wEJAcYB3wH/AR0B5gH4Af8BHAHkAfcB/wEbAeMB9wH/AQABRgFPAf8BAAFGAU8B/wEZAeIB9wH/ - ARkB4gH3Af8BGgHjAfcB/wEIAccB4AH/ARQBtAHRAf8B6gHyAfUB/wQAAf8BAAP/AQAC/wEAAbQB0QH/ - AQABxgHfAf8BAAHmAfgB/wEAAeQB9wH/AQAB4wH3Af8BAAEXASAB/wEAARcBIAH/AQAB4gH3Af8BAAHi - AfcB/wEAAeMB9wH/AQABxwHgAf8BAAG0AdEC/wEAA/8BAAL/Ay4BRwMzAVIDPQFnA0YBfwNOAZUDUgGj - AWICYAH1AXsCeQH6AXsCeQH6Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/AzcBWwMAAQEDOQFfAyUB/wNO - AZQDUgGnAyUB/wGKAoMB+gGIAoYB+QMlAf8BggGBAYAB9wNbAcgDJQH/A0gBhQMlAf8DJQH/AzcBWwgA - AY4B0wHjAf8BAAGqAcsB/wElAeoB9gH/ASkB8AH6Af8BJwHuAfkB/wMAAf8DAAH/ASMB6gH5Af8BIwHq - AfkB/wEeAeUB9QH/AQABqgHLAf8BjgHTAeMB/wgAAf8BAAP/AQAD/wEAAv8BAAGqAcsB/wEAAeoB9gH/ - AQAB8AH6Af8BAAHuAfkB/wMAAf8DAAH/AQAB6gH5Af8BAAHqAfkB/wEAAeUB9QH/AQABqgHLAv8BAAP/ - AQAD/wEAAv8EAAMgAS8DTAGSAygB/wMdAf8DKAH/AXEBbwFuAfoDPwH/Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8DHQH/Ax0B/wM4AV4EAAM5AV8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/ - A0gBhQNOAZUDJQH/AzcBWwgAAe8B9AH2Af8BFAG0AdEB/wEHAcIB2wH/AS0B8wH7Af8BLAHxAfoB/wMA - Af8DAAH/ASgB7wH6Af8BKAHvAfoB/wEJAcYB3wH/ARQBtAHRAf8B7wH0AfYB/wgAAf8BAAP/AQAD/wEA - Av8BAAG0AdEB/wEAAcIB2wH/AQAB8wH7Af8BAAHxAfoB/wMAAf8DAAH/AQAB7wH6Af8BAAHvAfoB/wEA - AcYB3wH/AQABtAHRAv8BAAP/AQAD/wEAAv8EAAM4AVwDKAH/Ax0B/wMdAf8DHQH/AygB/wNfAegDHQH/ - Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/A0oBiwQAAzkBXwMlAf8B+QLqAf8B+QLqAf8DJQH/AfkC6gH/ - AfkC6gH/AyUB/wH5AuoB/wH5AuoB/wMlAf8DXgHZAyUB/wMlAf8DNwFbDAABnAHXAeUB/wEAAakBywH/ - AScB6gH0Af8BLwH0AfsB/wMAAf8DAAH/AS0B8gH6Af8BJgHqAfYB/wEAAakBywH/AZwB1wHlAf8MAAH/ - AQAD/wEAA/8BAAP/AQAC/wEAAakBywH/AQAB6gH0Af8BAAH0AfsB/wMAAf8DAAH/AQAB8gH6Af8BAAHq - AfYB/wEAAakBywL/AQAD/wEAA/8BAAP/AQAC/wQAAzgBXAMdAf8DHQH/Ax0B/wMdAf8DHQH/A1QBrwMd - Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DRAF7BAADOQFfAyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wNiAekDYgHpAyUB/wNIAYYQAAEUAbQB0QH/AQQBvwHZAf8BOQH2AfsB/wMA - Af8DAAH/ATAB9QH7Af8BCwHGAd0B/wEUAbQB0QH/AfUB9gH3Af8MAAH/AQAD/wEAA/8BAAP/AQAC/wEA - AbQB0QH/AQABvwHZAf8BCgH2AfsB/wMAAf8DAAH/AQEB9QH7Af8BAAHGAd0B/wEAAbQB0QL/AQAD/wEA - A/8BAAP/AQAC/wQAAzgBXAMoAf8DHQH/Ax0B/wMdAf8DKAH/A1YBqwNeAdADXAHqA1wB6gNcAeoDXAHq - AygB/wMdAf8DNwFbBAADOQFfAyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wNh - AdwDJQH/AyUB/wM9AWcQAAGrAd0B6QH/AQABqQHKAf8BJgHoAfMB/wEwAfUB+wH/ATAB9QH7Af8BKQHt - AfYB/wEAAakBywH/AasB3QHpAf8QAAH/AQAD/wEAA/8BAAP/AQAD/wEAAv8BAAGpAcoB/wEAAegB8wH/ - AQEB9QH7Af8BAQH1AfsB/wEAAe0B9gH/AQABqQHLAv8BAAP/AQAD/wEAA/8BAAP/AQAC/wQAAyABLwNM - AZIDKAH/Ax0B/wMoAf8DVAGvA1YBqwNVAa0DXwHoA1wB6gNcAeoDXAHqA1wB6gMdAf8DNwFbBAADQwF4 - AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wNOAZUDXQHTAyUB/wM3AVsUAAEV - AbQB0QH/AQIBuwHWAf8BMAH1AfsB/wEwAfUB+wH/AQoBxgHdAf8BFQG0AdEB/xQAAf8BAAP/AQAD/wEA - A/8BAAP/AQAC/wEAAbQB0QH/AQABuwHWAf8BAQH1AfsB/wEBAfUB+wH/AQABxgHdAf8BAAG0AdEC/wEA - A/8BAAP/AQAD/wEAA/8BAAL/CAADSwGOA0wBkgNXAboDVgGrA1YBqwNWAasDVgGrA2EB5ANcAeoDXAHq - A1wB6gNcAeoDHQH/AzcBWwQAA0kBhwM+AWsDNwFbAzcBWwM3AVsDOgFiA1ABngNSAaMDUgGjA1IBowNS - AaMDTgGVAyUB/wMlAf8DNwFbFAABuwHiAe0B/wEAAagBygH/ASMB5QHxAf8BKQHsAfUB/wEAAakBywH/ - AbsB4gHtAf8UAAH/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/AQABqAHKAf8BAAHlAfEB/wEAAewB9QH/ - AQABqQHLAv8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAAv8IAAM+AWsDSgGMAygB/wNWAbEDWAG5A1YBqwNW - AasDYQHhA1wB6gNcAeoDXAHqAygB/wNKAYwDLgFIEAADNwFbAyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wM2AVkYAAEVAbQB0QH/AQABuQHVAf8BCgHGAd0B/wEVAbQB0QH/GAAB/wEA - A/8BAAP/AQAD/wEAA/8BAAP/AQAC/wEAAbQB0QH/AQABuQHVAf8BAAHGAd0B/wEAAbQB0QL/AQAD/wEA - A/8BAAP/AQAD/wEAA/8BAAL/DAADPQFoA1YBtANcAeoDNAH/Ax0B/wMdAf8DHQH/Ax0B/wM0Af8DXAHq - A1cBugM+AWsUAAM3AVsDJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AzQBVBgA - AckB6QHxAf8BBQGvAc4B/wEFAa8BzgH/AckB6QHxAf8YAAH/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAP/ - AQAC/wEAAa8BzgH/AQABrwHOAv8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/EAADDQERAycBOgMz - AVMDNwFbAzsBZQNKAY0DSwGOA0gBhAM4AV4DEgEZGAADNwFbAzcBWwNJAYcDSwGOA0sBjgNKAY0DRQF8 - AzcBWwM3AVsDNwFbAzUBVwM3AVr/AEEAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFA - AyoBQAMqAUADKgFAAyoBQAMmATgDDAEQBAADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMq - AUADKgFAAyoBQAMqAUADKgFAAyYBOAMMARAUAAHWAb4BrQH/AbUBmgGEAf8BnAEfAQQB/wGUARMBAAH/ - AZQBEwEAAf8BpQEnARUB/wG9AaYBlAH/AdYBvgG1Af8gAAH3AfMB7wH/AdYBvgG1Af8BrQGOAVsB/wGU - AUkBMgH/AYwBQQEqAf8BlAFVAUMB/wHGAbYBrQH/FAADXAHfA1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHf - A1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHfA1sBwwMmATgEAANcAd8DXAHfA1wB3wNcAd8DXAHfA1wB3wNc - Ad8DXAHfA1wB3wNcAd8DXAHfA1wB3wNcAd8DWwHDAyYBOBAAAcYBrgGcAf8BrQGGARUB/wGtAZIBJQH/ - AdYBzwHOAf8B5wHzAfcB/wHnAe8B9wH/AcYBwwG9Af8BpQGGAR0B/wGlASMBDQH/AcYBrgGcAf8YAAH3 - AesB5wH/Ab0BmgGMAf8BnAFNATIB/wGcAU0BMgH/AZwBUQE6Af8BnAFNAToB/wGUAUUBMgH/AVsBMQEZ - Af8BrQGaAYwB/xAAAacCpgH/ArMBsgH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/ - A7oB/wGxArAB/wNcAd8DKgFABAABpwKmAf8CswGyAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/ - A7oB/wO6Af8DugH/AbECsAH/A1wB3wMqAUAMAAHOAa4BnAH/AbUBmgGMAf8B7wHrAecF/wH3AfsC/wHv - AfsB9wH/Ae8C9wH/Au8B9wH/AecB8wL/Ab0BtgG1Af8BlAETAQAB/wHGAa4BnAH/FAABvQGaAYwB/wGl - AVUBOgH/AaUBVQE6Af8BpQFVAToB/wGcAVEBOgH/AZwBUQE6Af8BnAFRAToB/wGcAVEBOgH/AYQBNQEi - Af8BrQGWAYwB/wwAA5EB/wPEAf8D4wH/A+MB/wPjAf8C4QHgAf8B2AHUAdIB/wHaAdYB1QH/A+MB/wPj - Af8D4wH/A+MB/wO6Af8DXAHfAyoBQAQAA5EB/wPEAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/ - A+MB/wPjAf8D4wH/A7oB/wNcAd8DKgFACAAB1gG6Aa0B/wG1AZIBJQH/A/cJ/wHeAbYBpQH/AaUCAAH/ - AaUCAAH/AdYBvgGtAf8B9wP/Ae8B9wL/Ab0BrgGlAf8BnAEXAQAB/wHWAb4BtQH/DAAB5wHHAbUB/wGt - AV0BQwH/Aa0BXQFDAf8BrQFdAUsB/wGtAV0BSwH/Aa0BXQFDAf8BpQFZAUMB/wGlAVUBOgH/AZwBUQE6 - Af8BnAFRAToB/wFbATEBGQH/AcYBtgGtAf8IAAORAf8DxAH/A+MB/wPjAf8D4wH/AdkB1AHSAf8BowGI - ASQB/wGuAZcBjAH/A+MB/wPjAf8D4wH/A+MB/wO6Af8DXAHfAyoBQAQAA5EB/wPEAf8D4wH/A+MB/wPj - Af8D4wH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A7oB/wNcAd8DKgFACAABvQGeAYQB/wHeAccBvQ3/ - Ae8B1wHGAf8BrQIAAf8BpQIAAf8B9wHvAecF/wHvAesB7wH/AecB8wH3Af8BnAEnARUB/wG1AZIBJQH/ - DAABzgGiAYwB/wGtAYYBSwH/AbUBigFTAf8BvQGOAVMB/wG9AY4BUwH/AbUBigFTAf8BtQGGAUsB/wGt - AV0BQwH/AaUBVQE6Af8BnAFRAToB/wGUAUUBMgH/AZQBVQFDAf8IAAORAf8BxgLHAf8B5gLnAf8B5gLn - Af8B5gLnAf8B2AHSAc8B/wGPARQBAgH/AZ4BgAEaAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8DvAH/ - A1wB3wMqAUAEAAORAf8BxgLHAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLn - Af8B5gLnAf8B5gLnAf8B5gLnAf8DvAH/A1wB3wMqAUAEAAHWAboBrQH/Ab0BmgGEAv8B+w7/Ae8B4wHW - Af8BvQIAAf8BtQEPAQAB/wH3AfsB9wX/AfcB8wH3Af8B7wHzAfcB/wHGAb4BvQH/AZwBHwEEAf8MAAHG - AZoBhAH/Ab0BigFTAv8B+wP/AvcC/wHzAe8B/wH3AesB5wH/AfcB4wHeAf8B9wHjAdYB/wH3AeMB3gH/ - AfcB2wHOAf8BnAFJATIB/wGMAUEBKgH/CAADkQH/AcoCywH/AewC7QH/AewC7QH/AewC7QH/Ad0B2AHV - Af8BkQEWAQMB/wGgAYIBHAH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/A78B/wNcAd8DKgFABAADkQH/ - AcoCywH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/ - AewC7QH/A78B/wNcAd8DKgFABAABzgGuAZwB/wHOAaYBjBH/Ae8B3wHWAf8BvQEDAQAB/wG1AQ8BAAH/ - AfcC7wn/Ae8B8wH3Af8B1gHbAd4B/wGcAR8BBAH/DAABxgGaAYQB/wHGAZYBWw7/AfsD/wHzAe8B/wH3 - AesB5wH/AfcB4wHeAf8B9wHfAdYB/wGcAUkBMgH/AZQBSQEyAf8IAAORAf8D0AH/AegB3gHaAf8ByAGj - AZYB/wHBAZcBiAH/AbsBjwEoAf8BnwEQAQAB/wGkARgBAwH/AcEBlwGIAf8BwQGXAYgB/wHOAa8BowH/ - Ae8B6gHnAf8DwwH/A1wB3wMqAUAEAAORAf8BygLLAf8B4QHYAdQB/wHHAaEBlAH/AcEBlwGIAf8BwQGX - AYgB/wHBAZcBiAH/AcEBlwGIAf8BwQGXAYgB/wHBAZcBiAH/AcwBrQGhAf8B5gHiAeEB/wO/Af8DXAHf - AyoBQAQAAc4BpgGUAf8BzgGqAZQR/wHvAdsBzgH/AcYBAwEAAf8BtQELAQAB/wHvAesB5wn/A/cB/wHe - AeMB5wH/AZwBIwENAf8MAAHeAbIBnAH/Ac4BngGEAf8B5wG2AZwB/wHnAb4BrQH/AecBvgGtAf8B5wG2 - AZwB/wHWAaYBjAH/Ac4BmgGEAf8BtQGKAUsB/wGlAVkBQwH/AZwBSQEyAf8BrQGOAVsB/wgAA5EB/wPT - Af8B6wHfAdkB/wHEAZcBhwH/AbwBiQEgAf8BuAGDARkB/wGiAQ8BAAH/AacBFQEAAf8BvAGJASAB/wG8 - AYkBIAH/AcwBpgGYAf8B9AHtAeoB/wPGAf8DXAHfAyoBQAQAA5EB/wHMAs0B/wHjAdcB0gH/AcMBlgGG - Af8BvAGJASAB/wG8AYkBIAH/AbwBiQEgAf8BvAGJASAB/wG8AYkBIAH/AbwBiQEgAf8ByQGjAZYB/wHp - AeQB4QH/A8EB/wNcAd8DKgFABAABxgGiAYwB/wHOAaoBlBH/Ad4BwwGtAf8BvQEHAQAB/wG9AR8BAAH/ - AfcC7wb/AfsC/wH3AfsC/wHeAdcB3gH/AZwBHwEEAf8MAAHvAc8BvQH/Ac4BngGEAf8B5wG2AZwB/wHn - Ab4BrQH/AecBvgGtAf8B5wG2AZwB/wHWAaYBjAH/Ac4BmgGEAf8BtQGKAUsB/wGlAVkBQwH/AZwBUQE6 - Af8B1gG+AbUB/wgAA5EB/wPTAf8B+AH2AfUB/wHyAeoB5wH/AfEB6AHkAf8B4gHUAc0B/wGWARcBBAH/ - AaUBgwEbAf8B8QHoAeQB/wHxAegB5AH/AfMB7QHqAf8B+gH5AfcB/wPGAf8DXAHfAyoBQAQAA5EB/wPS - Af8B9wH1AfMB/wHyAeoB5wH/AfEB6AHkAf8B8QHoAeQB/wHxAegB5AH/AfEB6AHkAf8B8QHoAeQB/wHx - AegB5AH/AfMB7AHpAf8B+AH3AfYB/wPFAf8DXAHfAyoBQAQAAdYBsgGcAf8BzgGiAYwR/wH3AesB5wH/ - Ae8B2wHWAf8B9wHrAecK/wH7Av8B9wP/Ac4BvgG9Af8BpQEnAQ0B/wwAAf8B8wHvAf8B3gG2AaUB/wHW - AaYBjAH/AecBvgGtAf8B7wG+Aa0B/wHnAbIBnAH/AdYBpgGUAf8BxgGWAYQB/wG1AYoBUwH/AaUBWQFD - Af8BvQGaAYwB/wH3Au8B/wgAA5EB/wPUAf8C/AH7Af8C/AH7Af8C/AH7Af8B6wHlAeEB/wGVARoBBwH/ - AaYBiAEhAf8C/AH7Af8C/AH7Af8C/AH7Af8C/AH7Af8DxwH/A1wB3wMqAUAEAAORAf8D0wH/AvsB+gH/ - AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/A8YB/wNc - Ad8DKgFACAAB1gGmAYwB/wHvAecB3g7/AfsC/wHWAaIBhAH/AdYBogGEAv8B+w7/AaUBJwENAf8BxgGm - AZQB/xAAAfcB6wHnAf8B3gG2AaUB/wHWAaIBjAH/AdYBqgGUAf8B3gGuAZQB/wHOAaIBjAH/Ab0BkgFb - Af8BtQGGAUsB/wG9AZoBjAH/AfcB6wHnAf8MAAORAf8D1AH/A/wB/wP8Af8D/AH/Ae0B6AHlAf8BogGC - ARsB/wGxAZcBiQH/A/wB/wP8Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6 - Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMq - AUAIAAHWAbYBnAH/AecBsgGUDf8B9wHvAecB/wHGARcBAAH/Ab0BDwEAAf8B9wLvCf8BxgGuAZwB/wGt - AYYBFQH/AdYBwwG1Af8UAAH/Ae8B5wH/Ae8BzwG9Af8B3gGyAZwB/wHOAZ4BjAH/AcYBmgGEAf8B1gGm - AZQB/wHWAboBrQL/AvcB/xAAA5EB/wPUAf8D/AH/A/wB/wP8Af8B+AL2Af8B4gHZAdUB/wHnAd8B2wH/ - A/wB/wP8Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6 - Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMqAUAMAAHeAbIBlAH/ - Ae8BxwGtDv8C9wL/AfcB7wb/AvcB/wHOAbYBpQH/Aa0BhgEVAf8B1gHDAbUB/0gAAqEBoAH/A8EB/wPU - Af8D1AH/A9QB/wPUAf8D1AH/A9QB/wPUAf8D1AH/A9QB/wPUAf8DuwH/A1wB3wMqAUAEAAKhAaAB/wPA - Af8D0wH/A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A7oB/wNcAd8DKgFAEAAB3gG2 - AZwB/wHnAbYBlAH/AfcB3wHGAv8B5wHeAv8B6wHnAf8B9wHnAd4B/wHnAc8BxgH/Ac4BqgGUAf8BvQGa - AYQB/wHWAcMBtQH/TAABvAG7AboB/wKhAaAB/wORAf8DkQH/A5EB/wORAf8DkQH/A5EB/wORAf8DkQH/ - A5EB/wORAf8BpwKmAf8DXAHfAyoBQAQAAbwBuwG6Af8CoQGgAf8DkQH/A5EB/wORAf8DkQH/A5EB/wOR - Af8DkQH/A5EB/wORAf8DkQH/AacCpgH/A1wB3wMqAUAUAAHeAb4BrQH/Ad4BtgGcAf8B5wG2AZwB/wHn - AbIBlAH/Ad4BqgGMAf8B1gGuAZwB/wHWAb4BtQH//wDtAAEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQ - AY8BFQH/GAAD2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wNbAcQkAAEWASoBtwH/AQABAgGm - Af8BRgFVAcQB/yAAA18B6AEWASoBtwH/DAAB/wFGATQC/wFGATQB/0gAARABjwEVAf8BEAGPARUB/wEQ - AY8BFQH/ARABjwEVAf8YAAPaAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8D2gH/A2UB9AMBAQIDAAEB - BwABARUAAQIBpgH/AQABAgGmAf8BAAECAaYB/wNfAegYAAFGAVUBxAH/AQYBHAGyAf8DDQERDAAB/wFG - ATQB/wgAAf8BRgE0Af9AAAEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/GAAD2gH/Ax0B/wMd - Af8DHQH/Ax0B/wMdAf8DHQH/A9oB/wNRAZwEAAMBAQIDBAEFAwABARQAAUYBVQHEAf8BAAECAaYB/wEA - AQIBpgH/AQABAgGmAf8DUgGpDAADEAEWATgBSAG+Af8BAAEPAawB/wMxAU0YAAH/AUYBNAL/AUYBNAL/ - AUYBNAH/PAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wMdAf8DHQH/Ax0B/wMd - Af8DHQH/Ax0B/wPaAf8DYAHrA10B0gNZAcIDWwHGA1QBrAMbASYUAANdAcoBAAECAaYB/wEAAQIBpgH/ - AQABDwGsAf8DMQFNBAADDQERARYBKgG3Af8BAAECAaYB/wNSAakYAAH/AUYBNAL/AUYBNAL/AUYBNAL/ - AUYBNAL/AUYBNAH/OAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wPaAf8D2gH/ - A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A0MBdwMAAQEYAANWAasBAAECAaYB/wEA - AQIBpgH/AQABDwGsAf8DPgFrARYBKgG3Af8BAAECAaYB/wNfAegDAwEEHAAB/wFGATQC/wFGATQC/wFG - ATQC/wFGATQC/wFGATQB/yAAARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQ - AY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEV - Af8BEAGPARUB/wQAA9oB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8D2gH/ - AzUBVwMEAQUDDwEUAwABARQAAz4BawEAAQ8BrAH/AQABAgGmAf8BAAECAaYB/wEAAQIBpgH/AUYBVQHE - Af8DAwQEAQUgAAH/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAH/HAABEAGPARUB/wEQAY8BFQH/ - ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGP - ARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/BAAD2gH/Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wPaAf8DCgEOJAADXAHJAQABAgGmAf8BAAECAaYB/wEAAQ8BrAH/ - Aw0BESwAAf8BRgE0Av8BRgE0Av8BRgE0Av8BRgE0Av8BRgE0Af8YAAEQAY8BFQH/ARABjwEVAf8BEAGP - ARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ - ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8EAAPaAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8DHQH/Ax0B/wMdAf8DHQH/A9oB/wMvAUoDBQEHAyABLgMsAUMUAANSAakBAAECAaYB/wEAAQIBpgH/ - AQABAgGmAf8BAAECAaYB/wNcAckQAAMLAQ8cAAH/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAH/ - FAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQ - AY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/BAAD2gH/ - A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/ - EAADXAHJAQABAgGmAf8BAAECAaYB/wEAAQ8BrAH/Az4BawEWASoBtwH/AQABAgGmAf8DSgGMMAAB/wFG - ATQC/wFGATQC/wFGATQB/ywAARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8YAAPaAf8DHQH/ - Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wPaAf8MAAFG - AVUBxAH/AQABAgGmAf8BAAECAaYB/wEAAQIBpgH/A1IBqQQAAw0BEQE4AUgBvgH/AQABDwGsAf8DMQFN - MAAB/wFGATQB/wgAAf8BRgE0Av8BRgE0Af8gAAEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ - GAAD2gH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8D2gH/DQABAgGmAf8BAAECAaYB/wEAAQIBpgH/A18B6BAAAUYBVQHEAf8BBgEcAbIB/wMNARE0AAH/ - AUYBNAL/AUYBNAL/AUYBNAH/IAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wMd - Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/A9oB/wwA - ARYBKgG3Af8BAAECAaYB/wFGAVUBxAH/GAADXwHoARYBKgG3Af80AAH/AUYBNAL/AUYBNAH/JAABEAGP - ARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPa - Af8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wcAAQGQAAEQAY8BFQH/ARABjwEVAf8BEAGP - ARUB/wEQAY8BFQH/+AADAQECnAADPQFpAz4BagNBAXIDNwFaAzcBWwM3AVsDOgFiA0oBiwNLAY4kAAP9 - Af8D9wH/A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/YB/wP3Af8D/QH/HAADigH/Az0B/wMlAf8DJQH/ - AyUB/wMlAf8DPQH/A4oB/xAAA/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1 - Af8D9QH/A/UB/wP1Af8D9QH/BAADPQFpAx0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wNKAYkkAAP3 - Af8DhgH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wOGAf8D9wH/FAADuQH/AyUB/wMlAf8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wO5Af8IAAP1Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMc - Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/A/UB/wQAAzsBZQMdAf8B+QLqAf8B+QLqAf8B+QLq - Af8B+QLqAf8B+QLqAf8DHQH/Az4BawM3AVsDNwFbAzcBWwM3AVsUAAP2Af8DHQH/AfEB7wHwAf8B8QHv - AfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DHQH/A/YB/xAABP8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8IAAP1Af8DHAH/Ae4C7wH/ - Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/ - A/UB/wQAAy4BSAMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Ax0B/wMdAf8DHQH/ - Ax0B/wM3AVsUAAP2Af8DHQH/AfEB7wHwAf8DHQH/AfEB7wHwAf8DHQH/AfEB7wHwAf8DHQH/AfEB7wHw - Af8DHQH/A/YB/xAABP8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMl - Af8IAAP1Af8DHAH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8B7gLv - Af8B7gLvAf8B7gLvAf8DHAH/A/UB/wQAAywBQwMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq - Af8DHQH/AzcBWwM3AVsDNwFbAx0B/wM3AVsUAAP2Af8DHQH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/ - AfEB7wHwAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/AyUB/wMlAf8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMc - Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8D9QH/BAADKwFCAx0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/ - Ax0B/wM3AVsEAAM3AVsDHQH/AzcBWxQAA/YB/wMdAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8B8QHv - AfAB/wMdAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/Ae4C7wH/Ae4C7wH/ - Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8D9QH/BAADKAE8Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8DHQH/Ax0B/wM3AVsEAAM3AVsDHQH/A0ABcRQAA/YB/wMdAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw - Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/AyUB/wMl - Af8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8B7gLvAf8B7gLvAf8B7gLv - Af8DHAH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8D9QH/BAADKgFB - AyoBQQMoAT0DJQE3AycBOwMuAUgDNgFZAzcBWwM3AVsEAAM3AVsDHQH/A0oBjRQAA/YB/wMdAf8B8QHv - AfAB/wMdAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/ - AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8DHAH/AxwB/wMc - Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8D9QH/HAADNQFWAzcBWwM3AVsDNwFb - AzcBWwMdAf8DSwGOA0oBjANHAYEMAAP2Af8DHQH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw - Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DHQH/A/YB/xAABP8DJQH/AyUB/wMlAf8DJQH/AyUB/wMl - Af8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8IAAP1Af8DHAH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxwB/wHu - Au8B/wHuAu8B/wHuAu8B/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/A/UB/xwAAykBPgMdAf8DHQH/ - Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DPQFoDAAD9gH/Ax0B/wHxAe8B8AH/Ax0B/wHxAe8B8AH/Ax0B/wHx - Ae8B8AH/Ax0B/wHxAe8B8AH/Ax0B/wP2Af8QAAT/AyUB/wMwAf8DqQH/AuEB4gH/A/gB/wP4Af8D+AH/ - A/gB/wLhAeIB/wOpAf8DMAH/AyUB/wgAA/UB/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/Ae4C7wH/ - Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8D9QH/HAADJQE3Ax0B/wH5AuoB/wH5 - AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8DOwFjDAAD9gH/Ax0B/wHxAe8B8AH/AfEB7wHwAf8B8QHv - AfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/Ax0B/wP2Af8QAAPzAf8DMAH/A+0B/wP4 - Af8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/A+0B/wMlAf8IAAP1Af8DHAH/AxwB/wMcAf8DHAH/ - AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/A/UB/xwAAyUBNwMdAf8B+QLqAf8B+QLq - Af8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Ay8BSgwAA/YB/wMdAf8B8QHvAfAB/wMdAf8DHQH/Ax0B/wMd - Af8DHQH/AfEB7wHwAf8DHQH/A/YB/xQAAzAB/wPtAf8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/ - A/gB/wPtAf8DJQH/CAAD9QH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMc - Af8DHAH/AxwB/wP1Af8cAAMlATcDHQH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/Ax0B/wMr - AUIMAAP2Af8DHQH/AfEB7wHwAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wHxAe8B8AH/Ax0B/wP2Af8UAAO5 - Af8DMAH/A6kB/wLhAeIB/wP4Af8D+AH/A/gB/wP4Af8C4QHiAf8DqQH/AzAB/wO5Af8IAAP1Af8DHAH/ - AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/A/UB/xwAAyoBQAMd - Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DKwFCDAAD9gH/Ax0B/wHxAe8B8AH/AfEB7wHwAf8B8QHv - AfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/Ax0B/wP2Af8YAAPoAf8DigH/Az0B/wMl - Af8DJQH/AyUB/wMlAf8DPQH/A4oB/wPoAf8MAAP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/ - A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/xwAAysBQgMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd - Af8DMwFTDAAD9wH/A4YB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DhgH/A/cB/6QAAysBQgMu - AUcDPgFqA0cBgQNKAYkDNwFbAzcBWwM3AVoDNwFaDAAD/QH/A/cB/wP2Af8D9gH/A/YB/wP2Af8D9gH/ - A/YB/wP2Af8D9wH/A/0B/wwAAUIBTQE+BwABPgMAASgDAAFAAwABYAMAAQEBAAEBBgABAxYAA/8BAAb/ - AgAB/AE/Av8B5wH/AgAB/AE/AfwBeAHzAf8CAAH8AT8B/gF4AfsBJwIAAfwBPwE+AXAB+QErAgAB/AE/ - AR4BAQH5Ac8CAAGAAQEBjAEDAfkBzwIAAYABAQHIATMB+QHPAgABgAEBAeEBAwH8AZMCAAGAAQEB4wGH - AfwB/wIAAfwBPwEAAQcB+AF/AgAB/AE/AQABBwH+Af8CAAH8AT8BAgEPAf4B/wIAAfwBPwHjAf8B/gE/ - AgAB/AE/A/8BPwIAAf8BfwT/AgAI/wIAAYABeQT/AgABgAFxAccB+QHwAR8CAAGAAWMBwwHxAeABDwIA - AYABBwHBAcMB4AEHAgABgAEPAeABhwHAAQMCAAGAAQEB8AEHAcABAwIAAYABAQH4AQcBwAEDAgABgAEB - AfwBHwHAAQMCAAGAAQEB+AEeAcABAwIAAfwBAQHwAQ8BwAEDAgAB/gEBAeABhwHAAQMCAAH+AQEB4QHj - AeABBwIAAf4BAwHjAfMB8AEPAgAB/gEHAb8D/wIACP8DAAEDAQABHwUAAQEBAAEfBwABBwcAAQcHAAEH - AYABAQYAAYABAQYAAcABAwIAAYABAAGAAQABwAEDAgABgAEAAYABAAHgAQcCAAGAAQABgAEAAfABBwIA - AYABAAGAAQAB8AEPAgABgAEAAYABAAH4AR8CAAHAAQABgAEAAfgBHwIAAcABAAHwAQAB/AE/AgAB4AEB - AfABAAH8AT8CAAHwAQMB8AEACv8BAAEBAQABAQHwAQ8B8AEfAQABAQEAAQEB4AEHAeABDwEAAQEBAAEB - AcABAwHgAQcBAAEBAQABAQGAAQEBwAEDAQABAQEAAQEBgAEBAcABAwEAAQEBAAEBAQABAQHAAQMBAAEB - AQABAQEAAQEBwAEDAQABAQEAAQEBAAEBAcABAwEAAQEBAAEBAQABAQHAAQMBAAEBAQABAQEAAQEBwAED - AQABAQEAAQEBgAEBAeABBwEAAQEBAAEBAYABAQHwAQ8BAAEBAQABAQHAAQMC/wEAAQEBAAEBAeABBwL/ - AQABAQEAAQEB8AEfDv8B/AE/AQABfwHHAfkBzwH/AfwBPwEAARcBwwHxAdsB/wH8AT8BAAFHAcEBwwHx - Af8B/AE/AQABAwHgAYcB4AH/AfwBPwEAAQMB8AEHAfABfwGAAQECAAH4AQcB+AE/AYABAQEAAQcB/AEf - AfwBHwGAAQECAAH4AR4B/gEPAYABAQIAAfABDwH/AR8B/AE/AgAB4AGHAf8BswH8AT8CAAHhAeMB/wHj - AfwBPwIAAeMB8wH/AecB/AE/AgABvwP/AfwBPwf/AX8E/wEAAX8BwAEHAfABDwEAAQEBAAF/AcABBwHA - AQMBAAEBAQABBwHAAQcBgAEDAQABAQEAAQcBwAEHAYABAwEAAQEBAAEHAcABBwGAAQMBAAEBAQABRwHA - AQcBgAEDAQABAQEAAUcBwAEHAYABAwEAAQEBAAFHAcABBwGAAQMBAAEBAfwBAQHAAQcBgAEDAQABAQH8 - AQEBwAEHAYABAwEAAQEB/AEBAcABBwGAAQMBAAEBAfwBAQHAAQcBwAEDAQABAQH8AQEBwAEHAcABAwEA - AQEB/AEBAcABBwHgAQcBAAEBAfwBAQHAAQcE/wH8AQEBwAEHCw== + AfUB/wEAAdsB9QH/AQAB2gHzAf8BAAGqAcsC/wEAA/8BAAL/AzYBWAMaAf8DGgH/A10B0wNUAaYDXQHT + AxoB/wMaAf8DWgG9AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWwMxAU4DNwFaA0ABbgNKAY0DUgGk + A1wB6gNiAe8DYgHvA10B7QNcAcwDSAGFAyIB/wNIAYUDNwFbAzcBWwM3AVsEAAHqAfIB9QH/AREBtAHR + Af8BBgHGAd8B/wEaAeYB+AH/ARkB5AH3Af8BGAHjAfcB/wEAAUMBTAH/AQABQwFMAf8BFgHiAfcB/wEW + AeIB9wH/ARcB4wH3Af8BBQHHAeAB/wERAbQB0QH/AeoB8gH1Af8EAAH/AQAD/wEAAv8BAAG0AdEB/wEA + AcYB3wH/AQAB5gH4Af8BAAHkAfcB/wEAAeMB9wH/AQABFAEdAf8BAAEUAR0B/wEAAeIB9wH/AQAB4gH3 + Af8BAAHjAfcB/wEAAccB4AH/AQABtAHRAv8BAAP/AQAC/wMuAUcDMwFSAz0BZwNGAX8DTgGVA1IBowFc + AloB9QF4AnYB+gF4AnYB+gMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wM3AVsDAAEBAzkBXwMiAf8DTgGU + A1IBpwMiAf8BgQJ9AfoBhQKDAfkDIgH/AX8BfQF6AfcDWwHIAyIB/wNIAYUDIgH/AyIB/wM3AVsIAAGO + AdMB4wH/AQABqgHLAf8BIgHqAfYB/wEmAfAB+gH/ASQB7gH5Af8DAAH/AwAB/wEgAeoB+QH/ASAB6gH5 + Af8BGwHlAfUB/wEAAaoBywH/AY4B0wHjAf8IAAH/AQAD/wEAA/8BAAL/AQABqgHLAf8BAAHqAfYB/wEA + AfAB+gH/AQAB7gH5Af8DAAH/AwAB/wEAAeoB+QH/AQAB6gH5Af8BAAHlAfUB/wEAAaoBywL/AQAD/wEA + A/8BAAL/BAADIAEvA0wBkgMlAf8DGgH/AyUB/wFuAWwBawH6AzwB/wMaAf8DGgH/AxoB/wMaAf8DGgH/ + AxoB/wMaAf8DOAFeBAADOQFfAyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wNI + AYUDTgGVAyIB/wM3AVsIAAHvAfQB9gH/AREBtAHRAf8BBAHCAdsB/wEqAfMB+wH/ASkB8QH6Af8DAAH/ + AwAB/wElAe8B+gH/ASUB7wH6Af8BBgHGAd8B/wERAbQB0QH/Ae8B9AH2Af8IAAH/AQAD/wEAA/8BAAL/ + AQABtAHRAf8BAAHCAdsB/wEAAfMB+wH/AQAB8QH6Af8DAAH/AwAB/wEAAe8B+gH/AQAB7wH6Af8BAAHG + Ad8B/wEAAbQB0QL/AQAD/wEAA/8BAAL/BAADOAFcAyUB/wMaAf8DGgH/AxoB/wMlAf8DXwHoAxoB/wMa + Af8DGgH/AxoB/wMaAf8DGgH/AxoB/wNKAYsEAAM5AV8DIgH/AfkC6gH/AfkC6gH/AyIB/wH5AuoB/wH5 + AuoB/wMiAf8B+QLqAf8B+QLqAf8DIgH/A14B2QMiAf8DIgH/AzcBWwwAAZwB1wHlAf8BAAGpAcsB/wEk + AeoB9AH/ASwB9AH7Af8DAAH/AwAB/wEqAfIB+gH/ASMB6gH2Af8BAAGpAcsB/wGcAdcB5QH/DAAB/wEA + A/8BAAP/AQAD/wEAAv8BAAGpAcsB/wEAAeoB9AH/AQAB9AH7Af8DAAH/AwAB/wEAAfIB+gH/AQAB6gH2 + Af8BAAGpAcsC/wEAA/8BAAP/AQAD/wEAAv8EAAM4AVwDGgH/AxoB/wMaAf8DGgH/AxoB/wNUAa8DGgH/ + AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A0QBewQAAzkBXwMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi + Af8DIgH/AyIB/wMiAf8DYgHpA2IB6QMiAf8DSAGGEAABEQG0AdEB/wEBAb8B2QH/ATYB9gH7Af8DAAH/ + AwAB/wEtAfUB+wH/AQgBxgHdAf8BEQG0AdEB/wH1AfYB9wH/DAAB/wEAA/8BAAP/AQAD/wEAAv8BAAG0 + AdEB/wEAAb8B2QH/AQcB9gH7Af8DAAH/AwAB/wEAAfUB+wH/AQABxgHdAf8BAAG0AdEC/wEAA/8BAAP/ + AQAD/wEAAv8EAAM4AVwDJQH/AxoB/wMaAf8DGgH/AyUB/wNWAasDXgHQA1wB6gNcAeoDXAHqA1wB6gMl + Af8DGgH/AzcBWwQAAzkBXwMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DYQHc + AyIB/wMiAf8DPQFnEAABqwHdAekB/wEAAakBygH/ASMB6AHzAf8BLQH1AfsB/wEtAfUB+wH/ASYB7QH2 + Af8BAAGpAcsB/wGrAd0B6QH/EAAB/wEAA/8BAAP/AQAD/wEAA/8BAAL/AQABqQHKAf8BAAHoAfMB/wEA + AfUB+wH/AQAB9QH7Af8BAAHtAfYB/wEAAakBywL/AQAD/wEAA/8BAAP/AQAD/wEAAv8EAAMgAS8DTAGS + AyUB/wMaAf8DJQH/A1QBrwNWAasDVQGtA18B6ANcAeoDXAHqA1wB6gNcAeoDGgH/AzcBWwQAA0MBeAMi + Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DTgGVA10B0wMiAf8DNwFbFAABEgG0 + AdEB/wEAAbsB1gH/AS0B9QH7Af8BLQH1AfsB/wEHAcYB3QH/ARIBtAHRAf8UAAH/AQAD/wEAA/8BAAP/ + AQAD/wEAAv8BAAG0AdEB/wEAAbsB1gH/AQAB9QH7Af8BAAH1AfsB/wEAAcYB3QH/AQABtAHRAv8BAAP/ + AQAD/wEAA/8BAAP/AQAC/wgAA0sBjgNMAZIDVwG6A1YBqwNWAasDVgGrA1YBqwNhAeQDXAHqA1wB6gNc + AeoDXAHqAxoB/wM3AVsEAANJAYcDPgFrAzcBWwM3AVsDNwFbAzoBYgNQAZ4DUgGjA1IBowNSAaMDUgGj + A04BlQMiAf8DIgH/AzcBWxQAAbsB4gHtAf8BAAGoAcoB/wEgAeUB8QH/ASYB7AH1Af8BAAGpAcsB/wG7 + AeIB7QH/FAAB/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAC/wEAAagBygH/AQAB5QHxAf8BAAHsAfUB/wEA + AakBywL/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/CAADPgFrA0oBjAMlAf8DVgGxA1gBuQNWAasDVgGr + A2EB4QNcAeoDXAHqA1wB6gMlAf8DSgGMAy4BSBAAAzcBWwMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi + Af8DIgH/AyIB/wMiAf8DNgFZGAABEgG0AdEB/wEAAbkB1QH/AQcBxgHdAf8BEgG0AdEB/xgAAf8BAAP/ + AQAD/wEAA/8BAAP/AQAD/wEAAv8BAAG0AdEB/wEAAbkB1QH/AQABxgHdAf8BAAG0AdEC/wEAA/8BAAP/ + AQAD/wEAA/8BAAP/AQAC/wwAAz0BaANWAbQDXAHqAzEB/wMaAf8DGgH/AxoB/wMaAf8DMQH/A1wB6gNX + AboDPgFrFAADNwFbAyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wM0AVQYAAHJ + AekB8QH/AQIBrwHOAf8BAgGvAc4B/wHJAekB8QH/GAAB/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEA + Av8BAAGvAc4B/wEAAa8BzgL/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAC/xAAAw0BEQMnAToDMwFT + AzcBWwM7AWUDSgGNA0sBjgNIAYQDOAFeAxIBGRgAAzcBWwM3AVsDSQGHA0sBjgNLAY4DSgGNA0UBfAM3 + AVsDNwFbAzcBWwM1AVcDNwFa/wBBAAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMq + AUADKgFAAyoBQAMqAUADJgE4AwwBEAQAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFA + AyoBQAMqAUADKgFAAyoBQAMmATgDDAEQFAAB1gG+Aa0B/wG1AZoBhAH/AZwBHAEBAf8BlAEQAQAB/wGU + ARABAAH/AaUBJAESAf8BvQGmAZQB/wHWAb4BtQH/IAAB9wHzAe8B/wHWAb4BtQH/Aa0BjgFYAf8BlAFG + AS8B/wGMAT4BJwH/AZQBUgFAAf8BxgG2Aa0B/xQAA1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHfA1wB3wNc + Ad8DXAHfA1wB3wNcAd8DXAHfA1wB3wNbAcMDJgE4BAADXAHfA1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHf + A1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHfA1sBwwMmATgQAAHGAa4BnAH/Aa0BhgESAf8BrQGSASIB/wHW + Ac8BzgH/AecB8wH3Af8B5wHvAfcB/wHGAcMBvQH/AaUBhgEaAf8BpQEgAQoB/wHGAa4BnAH/GAAB9wHr + AecB/wG9AZoBjAH/AZwBSgEvAf8BnAFKAS8B/wGcAU4BNwH/AZwBSgE3Af8BlAFCAS8B/wFYAS4BFgH/ + Aa0BmgGMAf8QAAGnAqYB/wKzAbIB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6 + Af8BsQKwAf8DXAHfAyoBQAQAAacCpgH/ArMBsgH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6 + Af8DugH/A7oB/wGxArAB/wNcAd8DKgFADAABzgGuAZwB/wG1AZoBjAH/Ae8B6wHnBf8B9wH7Av8B7wH7 + AfcB/wHvAvcB/wLvAfcB/wHnAfMC/wG9AbYBtQH/AZQBEAEAAf8BxgGuAZwB/xQAAb0BmgGMAf8BpQFS + ATcB/wGlAVIBNwH/AaUBUgE3Af8BnAFOATcB/wGcAU4BNwH/AZwBTgE3Af8BnAFOATcB/wGEATIBHwH/ + Aa0BlgGMAf8MAAORAf8DxAH/A+MB/wPjAf8D4wH/AuEB4AH/AdgB1AHSAf8B2gHWAdUB/wPjAf8D4wH/ + A+MB/wPjAf8DugH/A1wB3wMqAUAEAAORAf8DxAH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wPj + Af8D4wH/A+MB/wO6Af8DXAHfAyoBQAgAAdYBugGtAf8BtQGSASIB/wP3Cf8B3gG2AaUB/wGlAgAB/wGl + AgAB/wHWAb4BrQH/AfcD/wHvAfcC/wG9Aa4BpQH/AZwBFAEAAf8B1gG+AbUB/wwAAecBxwG1Af8BrQFa + AUAB/wGtAVoBQAH/Aa0BWgFIAf8BrQFaAUgB/wGtAVoBQAH/AaUBVgFAAf8BpQFSATcB/wGcAU4BNwH/ + AZwBTgE3Af8BWAEuARYB/wHGAbYBrQH/CAADkQH/A8QB/wPjAf8D4wH/A+MB/wHZAdQB0gH/AaMBiAEh + Af8BrgGXAYwB/wPjAf8D4wH/A+MB/wPjAf8DugH/A1wB3wMqAUAEAAORAf8DxAH/A+MB/wPjAf8D4wH/ + A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wO6Af8DXAHfAyoBQAgAAb0BngGEAf8B3gHHAb0N/wHv + AdcBxgH/Aa0CAAH/AaUCAAH/AfcB7wHnBf8B7wHrAe8B/wHnAfMB9wH/AZwBJAESAf8BtQGSASIB/wwA + Ac4BogGMAf8BrQGGAUgB/wG1AYoBUAH/Ab0BjgFQAf8BvQGOAVAB/wG1AYoBUAH/AbUBhgFIAf8BrQFa + AUAB/wGlAVIBNwH/AZwBTgE3Af8BlAFCAS8B/wGUAVIBQAH/CAADkQH/AcYCxwH/AeYC5wH/AeYC5wH/ + AeYC5wH/AdgB0gHPAf8BjwERAQAB/wGeAYABFwH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/A7wB/wNc + Ad8DKgFABAADkQH/AcYCxwH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/ + AeYC5wH/AeYC5wH/AeYC5wH/A7wB/wNcAd8DKgFABAAB1gG6Aa0B/wG9AZoBhAL/AfsO/wHvAeMB1gH/ + Ab0CAAH/AbUBDAEAAf8B9wH7AfcF/wH3AfMB9wH/Ae8B8wH3Af8BxgG+Ab0B/wGcARwBAQH/DAABxgGa + AYQB/wG9AYoBUAL/AfsD/wL3Av8B8wHvAf8B9wHrAecB/wH3AeMB3gH/AfcB4wHWAf8B9wHjAd4B/wH3 + AdsBzgH/AZwBRgEvAf8BjAE+AScB/wgAA5EB/wHKAssB/wHsAu0B/wHsAu0B/wHsAu0B/wHdAdgB1QH/ + AZEBEwEAAf8BoAGCARkB/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wO/Af8DXAHfAyoBQAQAA5EB/wHK + AssB/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHs + Au0B/wO/Af8DXAHfAyoBQAQAAc4BrgGcAf8BzgGmAYwR/wHvAd8B1gH/Ab0CAAH/AbUBDAEAAf8B9wLv + Cf8B7wHzAfcB/wHWAdsB3gH/AZwBHAEBAf8MAAHGAZoBhAH/AcYBlgFYDv8B+wP/AfMB7wH/AfcB6wHn + Af8B9wHjAd4B/wH3Ad8B1gH/AZwBRgEvAf8BlAFGAS8B/wgAA5EB/wPQAf8B6AHeAdoB/wHIAaMBlgH/ + AcEBlwGIAf8BuwGPASUB/wGfAQ0BAAH/AaQBFQEAAf8BwQGXAYgB/wHBAZcBiAH/Ac4BrwGjAf8B7wHq + AecB/wPDAf8DXAHfAyoBQAQAA5EB/wHKAssB/wHhAdgB1AH/AccBoQGUAf8BwQGXAYgB/wHBAZcBiAH/ + AcEBlwGIAf8BwQGXAYgB/wHBAZcBiAH/AcEBlwGIAf8BzAGtAaEB/wHmAeIB4QH/A78B/wNcAd8DKgFA + BAABzgGmAZQB/wHOAaoBlBH/Ae8B2wHOAf8BxgIAAf8BtQEIAQAB/wHvAesB5wn/A/cB/wHeAeMB5wH/ + AZwBIAEKAf8MAAHeAbIBnAH/Ac4BngGEAf8B5wG2AZwB/wHnAb4BrQH/AecBvgGtAf8B5wG2AZwB/wHW + AaYBjAH/Ac4BmgGEAf8BtQGKAUgB/wGlAVYBQAH/AZwBRgEvAf8BrQGOAVgB/wgAA5EB/wPTAf8B6wHf + AdkB/wHEAZcBhwH/AbwBiQEdAf8BuAGDARYB/wGiAQwBAAH/AacBEgEAAf8BvAGJAR0B/wG8AYkBHQH/ + AcwBpgGYAf8B9AHtAeoB/wPGAf8DXAHfAyoBQAQAA5EB/wHMAs0B/wHjAdcB0gH/AcMBlgGGAf8BvAGJ + AR0B/wG8AYkBHQH/AbwBiQEdAf8BvAGJAR0B/wG8AYkBHQH/AbwBiQEdAf8ByQGjAZYB/wHpAeQB4QH/ + A8EB/wNcAd8DKgFABAABxgGiAYwB/wHOAaoBlBH/Ad4BwwGtAf8BvQEEAQAB/wG9ARwBAAH/AfcC7wb/ + AfsC/wH3AfsC/wHeAdcB3gH/AZwBHAEBAf8MAAHvAc8BvQH/Ac4BngGEAf8B5wG2AZwB/wHnAb4BrQH/ + AecBvgGtAf8B5wG2AZwB/wHWAaYBjAH/Ac4BmgGEAf8BtQGKAUgB/wGlAVYBQAH/AZwBTgE3Af8B1gG+ + AbUB/wgAA5EB/wPTAf8B+AH2AfUB/wHyAeoB5wH/AfEB6AHkAf8B4gHUAc0B/wGWARQBAQH/AaUBgwEY + Af8B8QHoAeQB/wHxAegB5AH/AfMB7QHqAf8B+gH5AfcB/wPGAf8DXAHfAyoBQAQAA5EB/wPSAf8B9wH1 + AfMB/wHyAeoB5wH/AfEB6AHkAf8B8QHoAeQB/wHxAegB5AH/AfEB6AHkAf8B8QHoAeQB/wHxAegB5AH/ + AfMB7AHpAf8B+AH3AfYB/wPFAf8DXAHfAyoBQAQAAdYBsgGcAf8BzgGiAYwR/wH3AesB5wH/Ae8B2wHW + Af8B9wHrAecK/wH7Av8B9wP/Ac4BvgG9Af8BpQEkAQoB/wwAAf8B8wHvAf8B3gG2AaUB/wHWAaYBjAH/ + AecBvgGtAf8B7wG+Aa0B/wHnAbIBnAH/AdYBpgGUAf8BxgGWAYQB/wG1AYoBUAH/AaUBVgFAAf8BvQGa + AYwB/wH3Au8B/wgAA5EB/wPUAf8C/AH7Af8C/AH7Af8C/AH7Af8B6wHlAeEB/wGVARcBBAH/AaYBiAEe + Af8C/AH7Af8C/AH7Af8C/AH7Af8C/AH7Af8DxwH/A1wB3wMqAUAEAAORAf8D0wH/AvsB+gH/AvsB+gH/ + AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/A8YB/wNcAd8DKgFA + CAAB1gGmAYwB/wHvAecB3g7/AfsC/wHWAaIBhAH/AdYBogGEAv8B+w7/AaUBJAEKAf8BxgGmAZQB/xAA + AfcB6wHnAf8B3gG2AaUB/wHWAaIBjAH/AdYBqgGUAf8B3gGuAZQB/wHOAaIBjAH/Ab0BkgFYAf8BtQGG + AUgB/wG9AZoBjAH/AfcB6wHnAf8MAAORAf8D1AH/A/wB/wP8Af8D/AH/Ae0B6AHlAf8BogGCARgB/wGx + AZcBiQH/A/wB/wP8Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6Af8C+wH6 + Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMqAUAIAAHW + AbYBnAH/AecBsgGUDf8B9wHvAecB/wHGARQBAAH/Ab0BDAEAAf8B9wLvCf8BxgGuAZwB/wGtAYYBEgH/ + AdYBwwG1Af8UAAH/Ae8B5wH/Ae8BzwG9Af8B3gGyAZwB/wHOAZ4BjAH/AcYBmgGEAf8B1gGmAZQB/wHW + AboBrQL/AvcB/xAAA5EB/wPUAf8D/AH/A/wB/wP8Af8B+AL2Af8B4gHZAdUB/wHnAd8B2wH/A/wB/wP8 + Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6 + Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMqAUAMAAHeAbIBlAH/Ae8BxwGt + Dv8C9wL/AfcB7wb/AvcB/wHOAbYBpQH/Aa0BhgESAf8B1gHDAbUB/0gAAqEBoAH/A8EB/wPUAf8D1AH/ + A9QB/wPUAf8D1AH/A9QB/wPUAf8D1AH/A9QB/wPUAf8DuwH/A1wB3wMqAUAEAAKhAaAB/wPAAf8D0wH/ + A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A7oB/wNcAd8DKgFAEAAB3gG2AZwB/wHn + AbYBlAH/AfcB3wHGAv8B5wHeAv8B6wHnAf8B9wHnAd4B/wHnAc8BxgH/Ac4BqgGUAf8BvQGaAYQB/wHW + AcMBtQH/TAABvAG7AboB/wKhAaAB/wORAf8DkQH/A5EB/wORAf8DkQH/A5EB/wORAf8DkQH/A5EB/wOR + Af8BpwKmAf8DXAHfAyoBQAQAAbwBuwG6Af8CoQGgAf8DkQH/A5EB/wORAf8DkQH/A5EB/wORAf8DkQH/ + A5EB/wORAf8DkQH/AacCpgH/A1wB3wMqAUAUAAHeAb4BrQH/Ad4BtgGcAf8B5wG2AZwB/wHnAbIBlAH/ + Ad4BqgGMAf8B1gGuAZwB/wHWAb4BtQH//wDtAAENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/ + GAAD2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wNbAcQkAAETAScBtwH/AgABpgH/AUMBUgHE + Af8gAANfAegBEwEnAbcB/wwAAf8BQwExAv8BQwExAf9IAAENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wEN + AY8BEgH/GAAD2gH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A9oB/wNlAfQDAQECAwABAQcAAQEWAAGm + Af8CAAGmAf8CAAGmAf8DXwHoGAABQwFSAcQB/wEDARkBsgH/Aw0BEQwAAf8BQwExAf8IAAH/AUMBMQH/ + QAABDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/xgAA9oB/wMaAf8DGgH/AxoB/wMaAf8DGgH/ + AxoB/wPaAf8DUQGcBAADAQECAwQBBQMAAQEUAAFDAVIBxAH/AgABpgH/AgABpgH/AgABpgH/A1IBqQwA + AxABFgE1AUUBvgH/AQABDAGsAf8DMQFNGAAB/wFDATEC/wFDATEC/wFDATEB/zwAAQ0BjwESAf8BDQGP + ARIB/wENAY8BEgH/AQ0BjwESAf8YAAPaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8D2gH/A2AB6wNd + AdIDWQHCA1sBxgNUAawDGwEmFAADXQHKAgABpgH/AgABpgH/AQABDAGsAf8DMQFNBAADDQERARMBJwG3 + Af8CAAGmAf8DUgGpGAAB/wFDATEC/wFDATEC/wFDATEC/wFDATEC/wFDATEB/zgAAQ0BjwESAf8BDQGP + ARIB/wENAY8BEgH/AQ0BjwESAf8YAAPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPa + Af8D2gH/A9oB/wNDAXcDAAEBGAADVgGrAgABpgH/AgABpgH/AQABDAGsAf8DPgFrARMBJwG3Af8CAAGm + Af8DXwHoAwMBBBwAAf8BQwExAv8BQwExAv8BQwExAv8BQwExAv8BQwExAf8gAAENAY8BEgH/AQ0BjwES + Af8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wEN + AY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8EAAPaAf8DGgH/AxoB/wMaAf8DGgH/ + AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A9oB/wM1AVcDBAEFAw8BFAMAAQEUAAM+AWsBAAEMAawB/wIA + AaYB/wIAAaYB/wIAAaYB/wFDAVIBxAH/AwMEBAEFIAAB/wFDATEC/wFDATEC/wFDATEC/wFDATEC/wFD + ATEB/xwAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwES + Af8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wQA + A9oB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8D2gH/AwoBDiQAA1wByQIA + AaYB/wIAAaYB/wEAAQwBrAH/Aw0BESwAAf8BQwExAv8BQwExAv8BQwExAv8BQwExAv8BQwExAf8YAAEN + AY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwES + Af8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8EAAPaAf8DGgH/ + AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A9oB/wMvAUoDBQEHAyABLgMsAUMUAANS + AakCAAGmAf8CAAGmAf8CAAGmAf8CAAGmAf8DXAHJEAADCwEPHAAB/wFDATEC/wFDATEC/wFDATEC/wFD + ATEC/wFDATEB/xQAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/ + AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGP + ARIB/wQAA9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPa + Af8D2gH/A9oB/xAAA1wByQIAAaYB/wIAAaYB/wEAAQwBrAH/Az4BawETAScBtwH/AgABpgH/A0oBjDAA + Af8BQwExAv8BQwExAv8BQwExAf8sAAENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/GAAD2gH/ + AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8D2gH/ + DAABQwFSAcQB/wIAAaYB/wIAAaYB/wIAAaYB/wNSAakEAAMNAREBNQFFAb4B/wEAAQwBrAH/AzEBTTAA + Af8BQwExAf8IAAH/AUMBMQL/AUMBMQH/IAABDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/xgA + A9oB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/ + A9oB/w4AAaYB/wIAAaYB/wIAAaYB/wNfAegQAAFDAVIBxAH/AQMBGQGyAf8DDQERNAAB/wFDATEC/wFD + ATEC/wFDATEB/yAAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8YAAPaAf8DGgH/AxoB/wMa + Af8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wPaAf8MAAETAScBtwH/ + AgABpgH/AUMBUgHEAf8YAANfAegBEwEnAbcB/zQAAf8BQwExAv8BQwExAf8kAAENAY8BEgH/AQ0BjwES + Af8BDQGPARIB/wENAY8BEgH/GAAD2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/ + A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/BwABAZAAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwES + Af/4AAMBAQKcAAM9AWkDPgFqA0EBcgM3AVoDNwFbAzcBWwM6AWIDSgGLA0sBjiQAA/0B/wP3Af8D9gH/ + A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/cB/wP9Af8cAAOKAf8DOgH/AyIB/wMiAf8DIgH/AyIB/wM6 + Af8DigH/EAAD9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/ + A/UB/wP1Af8EAAM9AWkDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A0oBiSQAA/cB/wOGAf8DGgH/ + AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A4YB/wP3Af8UAAO5Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi + Af8DIgH/AyIB/wMiAf8DIgH/A7kB/wgAA/UB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/ + AxkB/wMZAf8DGQH/AxkB/wMZAf8D9QH/BAADOwFlAxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 + AuoB/wMaAf8DPgFrAzcBWwM3AVsDNwFbAzcBWxQAA/YB/wMaAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw + Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wMaAf8D9gH/EAAE/wMiAf8DIgH/AyIB/wMi + Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wgAA/UB/wMZAf8B7gLvAf8B7gLvAf8B7gLv + Af8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8D9QH/BAADLgFI + AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWxQA + A/YB/wMaAf8B8QHvAfAB/wMaAf8B8QHvAfAB/wMaAf8B8QHvAfAB/wMaAf8B8QHvAfAB/wMaAf8D9gH/ + EAAE/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wgAA/UB/wMZ + Af8B7gLvAf8B7gLvAf8B7gLvAf8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wHuAu8B/wHuAu8B/wHu + Au8B/wMZAf8D9QH/BAADLAFDAxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8DNwFb + AzcBWwM3AVsDGgH/AzcBWxQAA/YB/wMaAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHx + Ae8B8AH/AxoB/wHxAe8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi + Af8DIgH/AyIB/wMiAf8DIgH/CAAD9QH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/ + AxkB/wMZAf8DGQH/AxkB/wP1Af8EAAMrAUIDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWwQA + AzcBWwMaAf8DNwFbFAAD9gH/AxoB/wHxAe8B8AH/AxoB/wHxAe8B8AH/AxoB/wHxAe8B8AH/AxoB/wHx + Ae8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi + Af8DIgH/CAAD9QH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8B7gLvAf8B7gLvAf8B7gLvAf8DGQH/ + Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wP1Af8EAAMoATwDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/ + AzcBWwQAAzcBWwMaAf8DQAFxFAAD9gH/AxoB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/ + AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/ + AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/CAAD9QH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8B7gLv + Af8B7gLvAf8B7gLvAf8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wP1Af8EAAMqAUEDKgFBAygBPQMl + ATcDJwE7Ay4BSAM2AVkDNwFbAzcBWwQAAzcBWwMaAf8DSgGNFAAD9gH/AxoB/wHxAe8B8AH/AxoB/wHx + Ae8B8AH/AxoB/wHxAe8B8AH/AxoB/wHxAe8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMi + Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/CAAD9QH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/ + AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wP1Af8cAAM1AVYDNwFbAzcBWwM3AVsDNwFbAxoB/wNL + AY4DSgGMA0cBgQwAA/YB/wMaAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/ + AfEB7wHwAf8B8QHvAfAB/wMaAf8D9gH/EAAE/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/ + AyIB/wMiAf8DIgH/AyIB/wgAA/UB/wMZAf8B7gLvAf8B7gLvAf8B7gLvAf8DGQH/Ae4C7wH/Ae4C7wH/ + Ae4C7wH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8D9QH/HAADKQE+AxoB/wMaAf8DGgH/AxoB/wMa + Af8DGgH/AxoB/wM9AWgMAAP2Af8DGgH/AfEB7wHwAf8DGgH/AfEB7wHwAf8DGgH/AfEB7wHwAf8DGgH/ + AfEB7wHwAf8DGgH/A/YB/xAABP8DIgH/Ay0B/wOpAf8C4QHiAf8D+AH/A/gB/wP4Af8D+AH/AuEB4gH/ + A6kB/wMtAf8DIgH/CAAD9QH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8B7gLvAf8B7gLvAf8B7gLv + Af8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wP1Af8cAAMlATcDGgH/AfkC6gH/AfkC6gH/AfkC6gH/ + AfkC6gH/AfkC6gH/AxoB/wM7AWMMAAP2Af8DGgH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw + Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DGgH/A/YB/xAAA/MB/wMtAf8D7QH/A/gB/wP4Af8D+AH/ + A/gB/wP4Af8D+AH/A/gB/wP4Af8D7QH/AyIB/wgAA/UB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZ + Af8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8D9QH/HAADJQE3AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5 + AuoB/wH5AuoB/wMaAf8DLwFKDAAD9gH/AxoB/wHxAe8B8AH/AxoB/wMaAf8DGgH/AxoB/wMaAf8B8QHv + AfAB/wMaAf8D9gH/FAADLQH/A+0B/wP4Af8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/A+0B/wMi + Af8IAAP1Af8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/ + A/UB/xwAAyUBNwMaAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DGgH/AysBQgwAA/YB/wMa + Af8B8QHvAfAB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AfEB7wHwAf8DGgH/A/YB/xQAA7kB/wMtAf8DqQH/ + AuEB4gH/A/gB/wP4Af8D+AH/A/gB/wLhAeIB/wOpAf8DLQH/A7kB/wgAA/UB/wMZAf8DGQH/AxkB/wMZ + Af8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8D9QH/HAADKgFAAxoB/wMaAf8DGgH/ + AxoB/wMaAf8DGgH/AxoB/wMrAUIMAAP2Af8DGgH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw + Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DGgH/A/YB/xgAA+gB/wOKAf8DOgH/AyIB/wMiAf8DIgH/ + AyIB/wM6Af8DigH/A+gB/wwAA/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1 + Af8D9QH/A/UB/wP1Af8D9QH/HAADKwFCAxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMzAVMMAAP3 + Af8DhgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wOGAf8D9wH/pAADKwFCAy4BRwM+AWoDRwGB + A0oBiQM3AVsDNwFbAzcBWgM3AVoMAAP9Af8D9wH/A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/YB/wP3 + Af8D/QH/DAABQgFNAT4HAAE+AwABKAMAAUADAAFgAwABAQEAAQEGAAEDFgAD/wEABv8CAAH8AT8C/wHn + Af8CAAH8AT8B/AF4AfMB/wIAAfwBPwH+AXgB+wEnAgAB/AE/AT4BcAH5ASsCAAH8AT8BHgEBAfkBzwIA + AYABAQGMAQMB+QHPAgABgAEBAcgBMwH5Ac8CAAGAAQEB4QEDAfwBkwIAAYABAQHjAYcB/AH/AgAB/AE/ + AQABBwH4AX8CAAH8AT8BAAEHAf4B/wIAAfwBPwECAQ8B/gH/AgAB/AE/AeMB/wH+AT8CAAH8AT8D/wE/ + AgAB/wF/BP8CAAj/AgABgAF5BP8CAAGAAXEBxwH5AfABHwIAAYABYwHDAfEB4AEPAgABgAEHAcEBwwHg + AQcCAAGAAQ8B4AGHAcABAwIAAYABAQHwAQcBwAEDAgABgAEBAfgBBwHAAQMCAAGAAQEB/AEfAcABAwIA + AYABAQH4AR4BwAEDAgAB/AEBAfABDwHAAQMCAAH+AQEB4AGHAcABAwIAAf4BAQHhAeMB4AEHAgAB/gED + AeMB8wHwAQ8CAAH+AQcBvwP/AgAI/wMAAQMBAAEfBQABAQEAAR8HAAEHBwABBwcAAQcBgAEBBgABgAEB + BgABwAEDAgABgAEAAYABAAHAAQMCAAGAAQABgAEAAeABBwIAAYABAAGAAQAB8AEHAgABgAEAAYABAAHw + AQ8CAAGAAQABgAEAAfgBHwIAAcABAAGAAQAB+AEfAgABwAEAAfABAAH8AT8CAAHgAQEB8AEAAfwBPwIA + AfABAwHwAQAK/wEAAQEBAAEBAfABDwHwAR8BAAEBAQABAQHgAQcB4AEPAQABAQEAAQEBwAEDAeABBwEA + AQEBAAEBAYABAQHAAQMBAAEBAQABAQGAAQEBwAEDAQABAQEAAQEBAAEBAcABAwEAAQEBAAEBAQABAQHA + AQMBAAEBAQABAQEAAQEBwAEDAQABAQEAAQEBAAEBAcABAwEAAQEBAAEBAQABAQHAAQMBAAEBAQABAQGA + AQEB4AEHAQABAQEAAQEBgAEBAfABDwEAAQEBAAEBAcABAwL/AQABAQEAAQEB4AEHAv8BAAEBAQABAQHw + AR8O/wH8AT8BAAF/AccB+QHPAf8B/AE/AQABFwHDAfEB2wH/AfwBPwEAAUcBwQHDAfEB/wH8AT8BAAED + AeABhwHgAf8B/AE/AQABAwHwAQcB8AF/AYABAQIAAfgBBwH4AT8BgAEBAQABBwH8AR8B/AEfAYABAQIA + AfgBHgH+AQ8BgAEBAgAB8AEPAf8BHwH8AT8CAAHgAYcB/wGzAfwBPwIAAeEB4wH/AeMB/AE/AgAB4wHz + Af8B5wH8AT8CAAG/A/8B/AE/B/8BfwT/AQABfwHAAQcB8AEPAQABAQEAAX8BwAEHAcABAwEAAQEBAAEH + AcABBwGAAQMBAAEBAQABBwHAAQcBgAEDAQABAQEAAQcBwAEHAYABAwEAAQEBAAFHAcABBwGAAQMBAAEB + AQABRwHAAQcBgAEDAQABAQEAAUcBwAEHAYABAwEAAQEB/AEBAcABBwGAAQMBAAEBAfwBAQHAAQcBgAED + AQABAQH8AQEBwAEHAYABAwEAAQEB/AEBAcABBwHAAQMBAAEBAfwBAQHAAQcBwAEDAQABAQH8AQEBwAEH + AeABBwEAAQEB/AEBAcABBwT/AfwBAQHAAQcL \ No newline at end of file diff --git a/BismNormalizer/BismNormalizer/TabularCompare/UI/ConnectionsAlmt.Designer.cs b/BismNormalizer/BismNormalizer/TabularCompare/UI/ConnectionsAlmt.Designer.cs new file mode 100644 index 0000000..516e157 --- /dev/null +++ b/BismNormalizer/BismNormalizer/TabularCompare/UI/ConnectionsAlmt.Designer.cs @@ -0,0 +1,314 @@ +namespace BismNormalizer.TabularCompare.UI +{ + partial class ConnectionsAlmt + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cboSourceDatabase = new System.Windows.Forms.ComboBox(); + this.cboSourceServer = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.pnlSourceDb = new System.Windows.Forms.Panel(); + this.grpSource = new System.Windows.Forms.GroupBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnSwitch = new System.Windows.Forms.Button(); + this.panel2 = new System.Windows.Forms.Panel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.grpTarget = new System.Windows.Forms.GroupBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.pnlTargetDb = new System.Windows.Forms.Panel(); + this.cboTargetServer = new System.Windows.Forms.ComboBox(); + this.cboTargetDatabase = new System.Windows.Forms.ComboBox(); + this.pnlSourceDb.SuspendLayout(); + this.grpSource.SuspendLayout(); + this.panel2.SuspendLayout(); + this.panel1.SuspendLayout(); + this.grpTarget.SuspendLayout(); + this.pnlTargetDb.SuspendLayout(); + this.SuspendLayout(); + // + // cboSourceDatabase + // + this.cboSourceDatabase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cboSourceDatabase.FormattingEnabled = true; + this.cboSourceDatabase.Location = new System.Drawing.Point(26, 87); + this.cboSourceDatabase.Margin = new System.Windows.Forms.Padding(7); + this.cboSourceDatabase.MaxDropDownItems = 11; + this.cboSourceDatabase.Name = "cboSourceDatabase"; + this.cboSourceDatabase.Size = new System.Drawing.Size(858, 37); + this.cboSourceDatabase.TabIndex = 2; + this.cboSourceDatabase.Enter += new System.EventHandler(this.cboSourceDatabase_Enter); + // + // cboSourceServer + // + this.cboSourceServer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cboSourceServer.FormattingEnabled = true; + this.cboSourceServer.Location = new System.Drawing.Point(26, 16); + this.cboSourceServer.Margin = new System.Windows.Forms.Padding(7); + this.cboSourceServer.MaxDropDownItems = 11; + this.cboSourceServer.Name = "cboSourceServer"; + this.cboSourceServer.Size = new System.Drawing.Size(858, 37); + this.cboSourceServer.TabIndex = 1; + this.cboSourceServer.TextChanged += new System.EventHandler(this.cboSourceServer_TextChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(18, 154); + this.label2.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(94, 29); + this.label2.TabIndex = 2; + this.label2.Text = "Dataset"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(14, 83); + this.label1.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(134, 29); + this.label1.TabIndex = 1; + this.label1.Text = "Workspace"; + // + // pnlSourceDb + // + this.pnlSourceDb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pnlSourceDb.Controls.Add(this.cboSourceServer); + this.pnlSourceDb.Controls.Add(this.cboSourceDatabase); + this.pnlSourceDb.Location = new System.Drawing.Point(147, 60); + this.pnlSourceDb.Margin = new System.Windows.Forms.Padding(7); + this.pnlSourceDb.Name = "pnlSourceDb"; + this.pnlSourceDb.Size = new System.Drawing.Size(910, 147); + this.pnlSourceDb.TabIndex = 1; + // + // grpSource + // + this.grpSource.Controls.Add(this.label1); + this.grpSource.Controls.Add(this.label2); + this.grpSource.Controls.Add(this.pnlSourceDb); + this.grpSource.Dock = System.Windows.Forms.DockStyle.Top; + this.grpSource.Location = new System.Drawing.Point(0, 0); + this.grpSource.Margin = new System.Windows.Forms.Padding(7); + this.grpSource.Name = "grpSource"; + this.grpSource.Padding = new System.Windows.Forms.Padding(7); + this.grpSource.Size = new System.Drawing.Size(1069, 263); + this.grpSource.TabIndex = 16; + this.grpSource.TabStop = false; + this.grpSource.Text = "Source"; + // + // btnOK + // + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(696, 23); + this.btnOK.Margin = new System.Windows.Forms.Padding(7); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(161, 51); + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(871, 23); + this.btnCancel.Margin = new System.Windows.Forms.Padding(7); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(166, 51); + this.btnCancel.TabIndex = 7; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnSwitch + // + this.btnSwitch.BackgroundImage = global::BismNormalizer.Resources.ButtonSwitch; + this.btnSwitch.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.btnSwitch.Location = new System.Drawing.Point(467, 6); + this.btnSwitch.Margin = new System.Windows.Forms.Padding(7); + this.btnSwitch.Name = "btnSwitch"; + this.btnSwitch.Size = new System.Drawing.Size(130, 71); + this.btnSwitch.TabIndex = 3; + this.btnSwitch.UseVisualStyleBackColor = true; + this.btnSwitch.Click += new System.EventHandler(this.btnSwitch_Click); + // + // panel2 + // + this.panel2.Controls.Add(this.btnCancel); + this.panel2.Controls.Add(this.btnOK); + this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel2.Location = new System.Drawing.Point(0, 627); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(1069, 100); + this.panel2.TabIndex = 22; + // + // panel1 + // + this.panel1.Controls.Add(this.btnSwitch); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 263); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(1069, 84); + this.panel1.TabIndex = 25; + // + // grpTarget + // + this.grpTarget.Controls.Add(this.label3); + this.grpTarget.Controls.Add(this.label4); + this.grpTarget.Controls.Add(this.pnlTargetDb); + this.grpTarget.Dock = System.Windows.Forms.DockStyle.Top; + this.grpTarget.Location = new System.Drawing.Point(0, 347); + this.grpTarget.Margin = new System.Windows.Forms.Padding(7); + this.grpTarget.Name = "grpTarget"; + this.grpTarget.Padding = new System.Windows.Forms.Padding(7); + this.grpTarget.Size = new System.Drawing.Size(1069, 268); + this.grpTarget.TabIndex = 26; + this.grpTarget.TabStop = false; + this.grpTarget.Text = "Target"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(15, 83); + this.label3.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(134, 29); + this.label3.TabIndex = 4; + this.label3.Text = "Workspace"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(15, 154); + this.label4.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(94, 29); + this.label4.TabIndex = 5; + this.label4.Text = "Dataset"; + // + // pnlTargetDb + // + this.pnlTargetDb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pnlTargetDb.Controls.Add(this.cboTargetServer); + this.pnlTargetDb.Controls.Add(this.cboTargetDatabase); + this.pnlTargetDb.Location = new System.Drawing.Point(144, 60); + this.pnlTargetDb.Margin = new System.Windows.Forms.Padding(7); + this.pnlTargetDb.Name = "pnlTargetDb"; + this.pnlTargetDb.Size = new System.Drawing.Size(911, 147); + this.pnlTargetDb.TabIndex = 15; + // + // cboTargetServer + // + this.cboTargetServer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cboTargetServer.FormattingEnabled = true; + this.cboTargetServer.Location = new System.Drawing.Point(26, 16); + this.cboTargetServer.Margin = new System.Windows.Forms.Padding(7); + this.cboTargetServer.MaxDropDownItems = 11; + this.cboTargetServer.Name = "cboTargetServer"; + this.cboTargetServer.Size = new System.Drawing.Size(862, 37); + this.cboTargetServer.TabIndex = 4; + this.cboTargetServer.TextChanged += new System.EventHandler(this.cboTargetServer_TextChanged); + // + // cboTargetDatabase + // + this.cboTargetDatabase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cboTargetDatabase.FormattingEnabled = true; + this.cboTargetDatabase.Location = new System.Drawing.Point(26, 87); + this.cboTargetDatabase.Margin = new System.Windows.Forms.Padding(7); + this.cboTargetDatabase.MaxDropDownItems = 11; + this.cboTargetDatabase.Name = "cboTargetDatabase"; + this.cboTargetDatabase.Size = new System.Drawing.Size(862, 37); + this.cboTargetDatabase.TabIndex = 5; + this.cboTargetDatabase.Enter += new System.EventHandler(this.cboTargetDatabase_Enter); + // + // Connections + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(14F, 29F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(1069, 727); + this.Controls.Add(this.grpTarget); + this.Controls.Add(this.panel1); + this.Controls.Add(this.panel2); + this.Controls.Add(this.grpSource); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Margin = new System.Windows.Forms.Padding(7); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Connections"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.Text = "Connections"; + this.Load += new System.EventHandler(this.Connections_Load); + this.pnlSourceDb.ResumeLayout(false); + this.grpSource.ResumeLayout(false); + this.grpSource.PerformLayout(); + this.panel2.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.grpTarget.ResumeLayout(false); + this.grpTarget.PerformLayout(); + this.pnlTargetDb.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ComboBox cboSourceDatabase; + private System.Windows.Forms.ComboBox cboSourceServer; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Panel pnlSourceDb; + private System.Windows.Forms.GroupBox grpSource; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnSwitch; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.GroupBox grpTarget; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Panel pnlTargetDb; + private System.Windows.Forms.ComboBox cboTargetServer; + private System.Windows.Forms.ComboBox cboTargetDatabase; + } +} \ No newline at end of file diff --git a/BismNormalizer/BismNormalizer/TabularCompare/UI/ConnectionsAlmt.cs b/BismNormalizer/BismNormalizer/TabularCompare/UI/ConnectionsAlmt.cs new file mode 100644 index 0000000..9396c1d --- /dev/null +++ b/BismNormalizer/BismNormalizer/TabularCompare/UI/ConnectionsAlmt.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Windows.Forms; +using Microsoft.AnalysisServices; +using System.Drawing; + +namespace BismNormalizer.TabularCompare.UI +{ + public partial class ConnectionsAlmt : Form + { + private ComparisonInfo _comparisonInfo; + private float _dpiScaleFactor; + private bool _sourceDatabaseBound = false; + private bool _targetDatabaseBound = false; + + public ConnectionsAlmt() + { + InitializeComponent(); + } + + private void Connections_Load(object sender, EventArgs e) + { + this.Width = Convert.ToInt32(this.Width * 1.3); + + if (_dpiScaleFactor > 1) + { + //DPI + float dpiScaleFactorFudged = _dpiScaleFactor * HighDPIUtils.PrimaryFudgeFactor; + float fudgeFactorWidth = 0.95f; + + this.Scale(new SizeF(dpiScaleFactorFudged * (_dpiScaleFactor > 1.7 ? 1 : HighDPIUtils.SecondaryFudgeFactor), dpiScaleFactorFudged * HighDPIUtils.SecondaryFudgeFactor)); + this.Width = Convert.ToInt32(this.Width * dpiScaleFactorFudged * fudgeFactorWidth); + foreach (Control control in HighDPIUtils.GetChildInControl(this)) //.OfType