diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4506a280e1..864260862d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -179,15 +179,14 @@ jobs: for plugin in https://api.github.com/repos/SMI/RdmpDicom/releases/latest https://api.github.com/repos/HICServices/HicPlugin/releases/latest https://api.github.com/repos/HICServices/RdmpExtensions/releases/latest do PluginName="$(cut -d/ -f6 <<< $plugin)" - NAME="$(curl -s $plugin | grep "browser_download_url.*$PluginName.*nupkg" | cut -d : -f 2,3 | cut -d "\"" -f 2)" + NAME="$(curl -s $plugin | grep "browser_download_url.*$PluginName.*rdmp" | cut -d : -f 2,3 | cut -d "\"" -f 2)" curl -OL $NAME done - ls *.nupkg > rdmpplugins.txt for platform in PublishWindows PublishLinux PublishWinForms do - cp rdmpplugins.txt *.nupkg $platform + cp *.rdmp $platform done - rm rdmpplugins.txt *.nupkg + rm *.rdmp - name: Sign & zip shell: bash run: | diff --git a/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.Designer.cs b/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.Designer.cs index c0f7ed96d9..1b872ce7ee 100644 --- a/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.Designer.cs +++ b/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.Designer.cs @@ -31,511 +31,466 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RDMPTopMenuStripUI)); - this.menuStrip1 = new System.Windows.Forms.MenuStrip(); - this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.newSessionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.runToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.findToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.findMultipleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.findAndReplaceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.quitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.LocationsMenu = new System.Windows.Forms.ToolStripMenuItem(); - this.configureExternalServersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.setTicketingSystemToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.instancesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.launchAnotherInstanceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.launchNewWithDefaultSettings = new System.Windows.Forms.ToolStripMenuItem(); - this.switchToInstanceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.switchToDefaultSettings = new System.Windows.Forms.ToolStripMenuItem(); - this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.logViewerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.userSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.navigateBackwardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.navigateForwardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.issuesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.generateReportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.metadataReportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.governanceReportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.dITAExtractionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.testsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.generateTestDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.pluginsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.codeGenerationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.listAllTypesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.showPerformanceCounterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.lastCommandMonitorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openExeDirectoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.queryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.queryCatalogue = new System.Windows.Forms.ToolStripMenuItem(); - this.queryDataExport = new System.Windows.Forms.ToolStripMenuItem(); - this.restartApplicationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.terminateProcessToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.userManualToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.generateClassTableSummaryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.showHelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.tutorialsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.licenseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.checkForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.rdmpTaskBar1 = new ResearchDataManagementPlatform.WindowManagement.TopBar.RDMPTaskBarUI(); - this.viewHistoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.menuStrip1.SuspendLayout(); - this.SuspendLayout(); + menuStrip1 = new System.Windows.Forms.MenuStrip(); + fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + newSessionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + runToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + findToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + findMultipleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + findAndReplaceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + quitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + LocationsMenu = new System.Windows.Forms.ToolStripMenuItem(); + configureExternalServersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + setTicketingSystemToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + instancesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + launchAnotherInstanceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + launchNewWithDefaultSettings = new System.Windows.Forms.ToolStripMenuItem(); + switchToInstanceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + switchToDefaultSettings = new System.Windows.Forms.ToolStripMenuItem(); + viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + logViewerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + userSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + navigateBackwardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + navigateForwardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + viewHistoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + issuesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + generateReportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + metadataReportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + governanceReportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + dITAExtractionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + testsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + generateTestDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + pluginsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + codeGenerationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + listAllTypesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + showPerformanceCounterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + lastCommandMonitorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + openExeDirectoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + queryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + queryCatalogue = new System.Windows.Forms.ToolStripMenuItem(); + queryDataExport = new System.Windows.Forms.ToolStripMenuItem(); + restartApplicationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + terminateProcessToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + userManualToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + generateClassTableSummaryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + showHelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + tutorialsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + licenseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + checkForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + rdmpTaskBar1 = new WindowManagement.TopBar.RDMPTaskBarUI(); + instanceSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + menuStrip1.SuspendLayout(); + SuspendLayout(); // // menuStrip1 // - this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileToolStripMenuItem, - this.LocationsMenu, - this.viewToolStripMenuItem, - this.issuesToolStripMenuItem, - this.testsToolStripMenuItem, - this.helpToolStripMenuItem}); - this.menuStrip1.Location = new System.Drawing.Point(0, 0); - this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2); - this.menuStrip1.Size = new System.Drawing.Size(1353, 24); - this.menuStrip1.TabIndex = 56; - this.menuStrip1.Text = "menuStrip1"; + menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { fileToolStripMenuItem, LocationsMenu, viewToolStripMenuItem, issuesToolStripMenuItem, testsToolStripMenuItem, helpToolStripMenuItem }); + menuStrip1.Location = new System.Drawing.Point(0, 0); + menuStrip1.Name = "menuStrip1"; + menuStrip1.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2); + menuStrip1.Size = new System.Drawing.Size(1353, 24); + menuStrip1.TabIndex = 56; + menuStrip1.Text = "menuStrip1"; // // fileToolStripMenuItem // - this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.newToolStripMenuItem, - this.newSessionToolStripMenuItem, - this.runToolStripMenuItem, - this.openToolStripMenuItem, - this.findToolStripMenuItem, - this.findMultipleToolStripMenuItem, - this.findAndReplaceToolStripMenuItem, - this.closeToolStripMenuItem, - this.quitToolStripMenuItem}); - this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); - this.fileToolStripMenuItem.Text = "File"; + fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { newToolStripMenuItem, newSessionToolStripMenuItem, runToolStripMenuItem, openToolStripMenuItem, findToolStripMenuItem, findMultipleToolStripMenuItem, findAndReplaceToolStripMenuItem, closeToolStripMenuItem, quitToolStripMenuItem }); + fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + fileToolStripMenuItem.Text = "File"; // // newToolStripMenuItem // - this.newToolStripMenuItem.Name = "newToolStripMenuItem"; - this.newToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); - this.newToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.newToolStripMenuItem.Text = "New..."; - this.newToolStripMenuItem.Click += new System.EventHandler(this.NewToolStripMenuItem_Click); + newToolStripMenuItem.Name = "newToolStripMenuItem"; + newToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N; + newToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + newToolStripMenuItem.Text = "New..."; + newToolStripMenuItem.Click += NewToolStripMenuItem_Click; // // newSessionToolStripMenuItem // - this.newSessionToolStripMenuItem.Name = "newSessionToolStripMenuItem"; - this.newSessionToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.newSessionToolStripMenuItem.Text = "New Session"; - this.newSessionToolStripMenuItem.Click += new System.EventHandler(this.newSessionToolStripMenuItem_Click); + newSessionToolStripMenuItem.Name = "newSessionToolStripMenuItem"; + newSessionToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + newSessionToolStripMenuItem.Text = "New Session"; + newSessionToolStripMenuItem.Click += newSessionToolStripMenuItem_Click; // // runToolStripMenuItem // - this.runToolStripMenuItem.Name = "runToolStripMenuItem"; - this.runToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R))); - this.runToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.runToolStripMenuItem.Text = "Run..."; - this.runToolStripMenuItem.Click += new System.EventHandler(this.runToolStripMenuItem_Click); + runToolStripMenuItem.Name = "runToolStripMenuItem"; + runToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R; + runToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + runToolStripMenuItem.Text = "Run..."; + runToolStripMenuItem.Click += runToolStripMenuItem_Click; // // openToolStripMenuItem // - this.openToolStripMenuItem.Name = "openToolStripMenuItem"; - this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.openToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.openToolStripMenuItem.Text = "Open..."; - this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click); + openToolStripMenuItem.Name = "openToolStripMenuItem"; + openToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O; + openToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + openToolStripMenuItem.Text = "Open..."; + openToolStripMenuItem.Click += openToolStripMenuItem_Click; // // findToolStripMenuItem // - this.findToolStripMenuItem.Name = "findToolStripMenuItem"; - this.findToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F))); - this.findToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.findToolStripMenuItem.Text = "Find"; - this.findToolStripMenuItem.Click += new System.EventHandler(this.findToolStripMenuItem_Click); + findToolStripMenuItem.Name = "findToolStripMenuItem"; + findToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F; + findToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + findToolStripMenuItem.Text = "Find"; + findToolStripMenuItem.Click += findToolStripMenuItem_Click; // // findMultipleToolStripMenuItem // - this.findMultipleToolStripMenuItem.Name = "findMultipleToolStripMenuItem"; - this.findMultipleToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.F))); - this.findMultipleToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.findMultipleToolStripMenuItem.Text = "Find Multiple"; - this.findMultipleToolStripMenuItem.Click += new System.EventHandler(this.findMultipleToolStripMenuItem_Click); + findMultipleToolStripMenuItem.Name = "findMultipleToolStripMenuItem"; + findMultipleToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.F; + findMultipleToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + findMultipleToolStripMenuItem.Text = "Find Multiple"; + findMultipleToolStripMenuItem.Click += findMultipleToolStripMenuItem_Click; // // findAndReplaceToolStripMenuItem // - this.findAndReplaceToolStripMenuItem.Name = "findAndReplaceToolStripMenuItem"; - this.findAndReplaceToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.H))); - this.findAndReplaceToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.findAndReplaceToolStripMenuItem.Text = "Find and Replace"; - this.findAndReplaceToolStripMenuItem.Click += new System.EventHandler(this.findAndReplaceToolStripMenuItem_Click); + findAndReplaceToolStripMenuItem.Name = "findAndReplaceToolStripMenuItem"; + findAndReplaceToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.H; + findAndReplaceToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + findAndReplaceToolStripMenuItem.Text = "Find and Replace"; + findAndReplaceToolStripMenuItem.Click += findAndReplaceToolStripMenuItem_Click; // // closeToolStripMenuItem // - this.closeToolStripMenuItem.Name = "closeToolStripMenuItem"; - this.closeToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W))); - this.closeToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.closeToolStripMenuItem.Text = "Close"; - this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click); + closeToolStripMenuItem.Name = "closeToolStripMenuItem"; + closeToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W; + closeToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + closeToolStripMenuItem.Text = "Close"; + closeToolStripMenuItem.Click += closeToolStripMenuItem_Click; // // quitToolStripMenuItem // - this.quitToolStripMenuItem.Name = "quitToolStripMenuItem"; - this.quitToolStripMenuItem.Size = new System.Drawing.Size(216, 22); - this.quitToolStripMenuItem.Text = "Quit"; - this.quitToolStripMenuItem.Click += new System.EventHandler(this.quitToolStripMenuItem_Click); + quitToolStripMenuItem.Name = "quitToolStripMenuItem"; + quitToolStripMenuItem.Size = new System.Drawing.Size(216, 22); + quitToolStripMenuItem.Text = "Quit"; + quitToolStripMenuItem.Click += quitToolStripMenuItem_Click; // // LocationsMenu // - this.LocationsMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.configureExternalServersToolStripMenuItem, - this.setTicketingSystemToolStripMenuItem, - this.instancesToolStripMenuItem}); - this.LocationsMenu.Name = "LocationsMenu"; - this.LocationsMenu.Size = new System.Drawing.Size(70, 20); - this.LocationsMenu.Text = "Locations"; + LocationsMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { configureExternalServersToolStripMenuItem, setTicketingSystemToolStripMenuItem, instancesToolStripMenuItem }); + LocationsMenu.Name = "LocationsMenu"; + LocationsMenu.Size = new System.Drawing.Size(70, 20); + LocationsMenu.Text = "Locations"; // // configureExternalServersToolStripMenuItem // - this.configureExternalServersToolStripMenuItem.Name = "configureExternalServersToolStripMenuItem"; - this.configureExternalServersToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D))); - this.configureExternalServersToolStripMenuItem.Size = new System.Drawing.Size(207, 22); - this.configureExternalServersToolStripMenuItem.Text = "Change Default..."; - this.configureExternalServersToolStripMenuItem.Click += new System.EventHandler(this.configureExternalServersToolStripMenuItem_Click); + configureExternalServersToolStripMenuItem.Name = "configureExternalServersToolStripMenuItem"; + configureExternalServersToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D; + configureExternalServersToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + configureExternalServersToolStripMenuItem.Text = "Change Default..."; + configureExternalServersToolStripMenuItem.Click += configureExternalServersToolStripMenuItem_Click; // // setTicketingSystemToolStripMenuItem // - this.setTicketingSystemToolStripMenuItem.Name = "setTicketingSystemToolStripMenuItem"; - this.setTicketingSystemToolStripMenuItem.Size = new System.Drawing.Size(207, 22); - this.setTicketingSystemToolStripMenuItem.Text = "Set Ticketing System..."; - this.setTicketingSystemToolStripMenuItem.Click += new System.EventHandler(this.setTicketingSystemToolStripMenuItem_Click); + setTicketingSystemToolStripMenuItem.Name = "setTicketingSystemToolStripMenuItem"; + setTicketingSystemToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + setTicketingSystemToolStripMenuItem.Text = "Set Ticketing System..."; + setTicketingSystemToolStripMenuItem.Click += setTicketingSystemToolStripMenuItem_Click; // // instancesToolStripMenuItem // - this.instancesToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.launchAnotherInstanceToolStripMenuItem, - this.switchToInstanceToolStripMenuItem}); - this.instancesToolStripMenuItem.Name = "instancesToolStripMenuItem"; - this.instancesToolStripMenuItem.Size = new System.Drawing.Size(207, 22); - this.instancesToolStripMenuItem.Text = "Instances"; + instancesToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { launchAnotherInstanceToolStripMenuItem, switchToInstanceToolStripMenuItem }); + instancesToolStripMenuItem.Name = "instancesToolStripMenuItem"; + instancesToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + instancesToolStripMenuItem.Text = "Instances"; // // launchAnotherInstanceToolStripMenuItem // - this.launchAnotherInstanceToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.launchNewWithDefaultSettings}); - this.launchAnotherInstanceToolStripMenuItem.Name = "launchAnotherInstanceToolStripMenuItem"; - this.launchAnotherInstanceToolStripMenuItem.Size = new System.Drawing.Size(206, 22); - this.launchAnotherInstanceToolStripMenuItem.Text = "Launch Another Instance"; + launchAnotherInstanceToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { launchNewWithDefaultSettings }); + launchAnotherInstanceToolStripMenuItem.Name = "launchAnotherInstanceToolStripMenuItem"; + launchAnotherInstanceToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + launchAnotherInstanceToolStripMenuItem.Text = "Launch Another Instance"; // // launchNewWithDefaultSettings // - this.launchNewWithDefaultSettings.Name = "launchNewWithDefaultSettings"; - this.launchNewWithDefaultSettings.Size = new System.Drawing.Size(112, 22); - this.launchNewWithDefaultSettings.Text = "Default"; - this.launchNewWithDefaultSettings.ToolTipText = "The RDMP instance recorded in your user settings"; + launchNewWithDefaultSettings.Name = "launchNewWithDefaultSettings"; + launchNewWithDefaultSettings.Size = new System.Drawing.Size(112, 22); + launchNewWithDefaultSettings.Text = "Default"; + launchNewWithDefaultSettings.ToolTipText = "The RDMP instance recorded in your user settings"; // // switchToInstanceToolStripMenuItem // - this.switchToInstanceToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.switchToDefaultSettings}); - this.switchToInstanceToolStripMenuItem.Name = "switchToInstanceToolStripMenuItem"; - this.switchToInstanceToolStripMenuItem.Size = new System.Drawing.Size(206, 22); - this.switchToInstanceToolStripMenuItem.Text = "Switch To Instance"; + switchToInstanceToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { switchToDefaultSettings }); + switchToInstanceToolStripMenuItem.Name = "switchToInstanceToolStripMenuItem"; + switchToInstanceToolStripMenuItem.Size = new System.Drawing.Size(206, 22); + switchToInstanceToolStripMenuItem.Text = "Switch To Instance"; // // switchToDefaultSettings // - this.switchToDefaultSettings.Name = "switchToDefaultSettings"; - this.switchToDefaultSettings.Size = new System.Drawing.Size(112, 22); - this.switchToDefaultSettings.Text = "Default"; - this.switchToDefaultSettings.ToolTipText = "The RDMP instance recorded in your user settings"; + switchToDefaultSettings.Name = "switchToDefaultSettings"; + switchToDefaultSettings.Size = new System.Drawing.Size(112, 22); + switchToDefaultSettings.Text = "Default"; + switchToDefaultSettings.ToolTipText = "The RDMP instance recorded in your user settings"; // // viewToolStripMenuItem // - this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.logViewerToolStripMenuItem, - this.userSettingsToolStripMenuItem, - this.navigateBackwardToolStripMenuItem, - this.navigateForwardToolStripMenuItem, - this.viewHistoryToolStripMenuItem}); - this.viewToolStripMenuItem.Name = "viewToolStripMenuItem"; - this.viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); - this.viewToolStripMenuItem.Text = "View"; + viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { logViewerToolStripMenuItem, userSettingsToolStripMenuItem, instanceSettingsToolStripMenuItem, navigateBackwardToolStripMenuItem, navigateForwardToolStripMenuItem, viewHistoryToolStripMenuItem }); + viewToolStripMenuItem.Name = "viewToolStripMenuItem"; + viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + viewToolStripMenuItem.Text = "View"; // // logViewerToolStripMenuItem // - this.logViewerToolStripMenuItem.Name = "logViewerToolStripMenuItem"; - this.logViewerToolStripMenuItem.Size = new System.Drawing.Size(238, 22); - this.logViewerToolStripMenuItem.Text = "Log Viewer..."; - this.logViewerToolStripMenuItem.Click += new System.EventHandler(this.logViewerToolStripMenuItem_Click); + logViewerToolStripMenuItem.Name = "logViewerToolStripMenuItem"; + logViewerToolStripMenuItem.Size = new System.Drawing.Size(238, 22); + logViewerToolStripMenuItem.Text = "Log Viewer..."; + logViewerToolStripMenuItem.Click += logViewerToolStripMenuItem_Click; // // userSettingsToolStripMenuItem // - this.userSettingsToolStripMenuItem.Name = "userSettingsToolStripMenuItem"; - this.userSettingsToolStripMenuItem.Size = new System.Drawing.Size(238, 22); - this.userSettingsToolStripMenuItem.Text = "User Settings..."; - this.userSettingsToolStripMenuItem.Click += new System.EventHandler(this.userSettingsToolStripMenuItem_Click); + userSettingsToolStripMenuItem.Name = "userSettingsToolStripMenuItem"; + userSettingsToolStripMenuItem.Size = new System.Drawing.Size(238, 22); + userSettingsToolStripMenuItem.Text = "User Settings..."; + userSettingsToolStripMenuItem.Click += userSettingsToolStripMenuItem_Click; // // navigateBackwardToolStripMenuItem // - this.navigateBackwardToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("navigateBackwardToolStripMenuItem.Image"))); - this.navigateBackwardToolStripMenuItem.Name = "navigateBackwardToolStripMenuItem"; - this.navigateBackwardToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+-"; - this.navigateBackwardToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.OemMinus))); - this.navigateBackwardToolStripMenuItem.Size = new System.Drawing.Size(238, 22); - this.navigateBackwardToolStripMenuItem.Text = "Navigate Backward"; - this.navigateBackwardToolStripMenuItem.Click += new System.EventHandler(this.navigateBackwardToolStripMenuItem_Click); + navigateBackwardToolStripMenuItem.Image = (System.Drawing.Image)resources.GetObject("navigateBackwardToolStripMenuItem.Image"); + navigateBackwardToolStripMenuItem.Name = "navigateBackwardToolStripMenuItem"; + navigateBackwardToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+-"; + navigateBackwardToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.OemMinus; + navigateBackwardToolStripMenuItem.Size = new System.Drawing.Size(238, 22); + navigateBackwardToolStripMenuItem.Text = "Navigate Backward"; + navigateBackwardToolStripMenuItem.Click += navigateBackwardToolStripMenuItem_Click; // // navigateForwardToolStripMenuItem // - this.navigateForwardToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("navigateForwardToolStripMenuItem.Image"))); - this.navigateForwardToolStripMenuItem.Name = "navigateForwardToolStripMenuItem"; - this.navigateForwardToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Shift+-"; - this.navigateForwardToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.OemMinus))); - this.navigateForwardToolStripMenuItem.Size = new System.Drawing.Size(238, 22); - this.navigateForwardToolStripMenuItem.Text = "Navigate Forward"; - this.navigateForwardToolStripMenuItem.Click += new System.EventHandler(this.navigateForwardToolStripMenuItem_Click); + navigateForwardToolStripMenuItem.Image = (System.Drawing.Image)resources.GetObject("navigateForwardToolStripMenuItem.Image"); + navigateForwardToolStripMenuItem.Name = "navigateForwardToolStripMenuItem"; + navigateForwardToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Shift+-"; + navigateForwardToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.OemMinus; + navigateForwardToolStripMenuItem.Size = new System.Drawing.Size(238, 22); + navigateForwardToolStripMenuItem.Text = "Navigate Forward"; + navigateForwardToolStripMenuItem.Click += navigateForwardToolStripMenuItem_Click; + // + // viewHistoryToolStripMenuItem + // + viewHistoryToolStripMenuItem.Image = (System.Drawing.Image)resources.GetObject("viewHistoryToolStripMenuItem.Image"); + viewHistoryToolStripMenuItem.Name = "viewHistoryToolStripMenuItem"; + viewHistoryToolStripMenuItem.Size = new System.Drawing.Size(238, 22); + viewHistoryToolStripMenuItem.Text = "View History..."; + viewHistoryToolStripMenuItem.Click += viewHistoryToolStripMenuItem_Click; // // issuesToolStripMenuItem // - this.issuesToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.generateReportToolStripMenuItem}); - this.issuesToolStripMenuItem.Name = "issuesToolStripMenuItem"; - this.issuesToolStripMenuItem.Size = new System.Drawing.Size(59, 20); - this.issuesToolStripMenuItem.Text = "Reports"; + issuesToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { generateReportToolStripMenuItem }); + issuesToolStripMenuItem.Name = "issuesToolStripMenuItem"; + issuesToolStripMenuItem.Size = new System.Drawing.Size(59, 20); + issuesToolStripMenuItem.Text = "Reports"; // // generateReportToolStripMenuItem // - this.generateReportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.metadataReportToolStripMenuItem, - this.governanceReportToolStripMenuItem, - this.dITAExtractionToolStripMenuItem}); - this.generateReportToolStripMenuItem.Name = "generateReportToolStripMenuItem"; - this.generateReportToolStripMenuItem.Size = new System.Drawing.Size(130, 22); - this.generateReportToolStripMenuItem.Text = "Generate..."; + generateReportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { metadataReportToolStripMenuItem, governanceReportToolStripMenuItem, dITAExtractionToolStripMenuItem }); + generateReportToolStripMenuItem.Name = "generateReportToolStripMenuItem"; + generateReportToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + generateReportToolStripMenuItem.Text = "Generate..."; // // metadataReportToolStripMenuItem // - this.metadataReportToolStripMenuItem.Name = "metadataReportToolStripMenuItem"; - this.metadataReportToolStripMenuItem.Size = new System.Drawing.Size(175, 22); - this.metadataReportToolStripMenuItem.Text = "Metadata Report..."; - this.metadataReportToolStripMenuItem.Click += new System.EventHandler(this.metadataReportToolStripMenuItem_Click); + metadataReportToolStripMenuItem.Name = "metadataReportToolStripMenuItem"; + metadataReportToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + metadataReportToolStripMenuItem.Text = "Metadata Report..."; + metadataReportToolStripMenuItem.Click += metadataReportToolStripMenuItem_Click; // // governanceReportToolStripMenuItem // - this.governanceReportToolStripMenuItem.Name = "governanceReportToolStripMenuItem"; - this.governanceReportToolStripMenuItem.Size = new System.Drawing.Size(175, 22); - this.governanceReportToolStripMenuItem.Text = "Governance Report"; - this.governanceReportToolStripMenuItem.Click += new System.EventHandler(this.governanceReportToolStripMenuItem_Click); + governanceReportToolStripMenuItem.Name = "governanceReportToolStripMenuItem"; + governanceReportToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + governanceReportToolStripMenuItem.Text = "Governance Report"; + governanceReportToolStripMenuItem.Click += governanceReportToolStripMenuItem_Click; // // dITAExtractionToolStripMenuItem // - this.dITAExtractionToolStripMenuItem.Name = "dITAExtractionToolStripMenuItem"; - this.dITAExtractionToolStripMenuItem.Size = new System.Drawing.Size(175, 22); - this.dITAExtractionToolStripMenuItem.Text = "DITA Extraction..."; - this.dITAExtractionToolStripMenuItem.Click += new System.EventHandler(this.dITAExtractionToolStripMenuItem_Click); + dITAExtractionToolStripMenuItem.Name = "dITAExtractionToolStripMenuItem"; + dITAExtractionToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + dITAExtractionToolStripMenuItem.Text = "DITA Extraction..."; + dITAExtractionToolStripMenuItem.Click += dITAExtractionToolStripMenuItem_Click; // // testsToolStripMenuItem // - this.testsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.generateTestDataToolStripMenuItem, - this.pluginsToolStripMenuItem, - this.toolStripSeparator1, - this.showPerformanceCounterToolStripMenuItem, - this.lastCommandMonitorToolStripMenuItem, - this.openExeDirectoryToolStripMenuItem, - this.queryToolStripMenuItem, - this.restartApplicationToolStripMenuItem, - this.terminateProcessToolStripMenuItem}); - this.testsToolStripMenuItem.Name = "testsToolStripMenuItem"; - this.testsToolStripMenuItem.Size = new System.Drawing.Size(80, 20); - this.testsToolStripMenuItem.Text = "Diagnostics"; + testsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { generateTestDataToolStripMenuItem, pluginsToolStripMenuItem, toolStripSeparator1, showPerformanceCounterToolStripMenuItem, lastCommandMonitorToolStripMenuItem, openExeDirectoryToolStripMenuItem, queryToolStripMenuItem, restartApplicationToolStripMenuItem, terminateProcessToolStripMenuItem }); + testsToolStripMenuItem.Name = "testsToolStripMenuItem"; + testsToolStripMenuItem.Size = new System.Drawing.Size(80, 20); + testsToolStripMenuItem.Text = "Diagnostics"; // // generateTestDataToolStripMenuItem // - this.generateTestDataToolStripMenuItem.Name = "generateTestDataToolStripMenuItem"; - this.generateTestDataToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.G))); - this.generateTestDataToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.generateTestDataToolStripMenuItem.Text = "Generate Test Data..."; - this.generateTestDataToolStripMenuItem.Click += new System.EventHandler(this.generateTestDataToolStripMenuItem_Click); + generateTestDataToolStripMenuItem.Name = "generateTestDataToolStripMenuItem"; + generateTestDataToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.G; + generateTestDataToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + generateTestDataToolStripMenuItem.Text = "Generate Test Data..."; + generateTestDataToolStripMenuItem.Click += generateTestDataToolStripMenuItem_Click; // // pluginsToolStripMenuItem // - this.pluginsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.codeGenerationToolStripMenuItem, - this.listAllTypesToolStripMenuItem}); - this.pluginsToolStripMenuItem.Name = "pluginsToolStripMenuItem"; - this.pluginsToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.pluginsToolStripMenuItem.Text = "Plugins"; + pluginsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { codeGenerationToolStripMenuItem, listAllTypesToolStripMenuItem }); + pluginsToolStripMenuItem.Name = "pluginsToolStripMenuItem"; + pluginsToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + pluginsToolStripMenuItem.Text = "Plugins"; // // codeGenerationToolStripMenuItem // - this.codeGenerationToolStripMenuItem.Name = "codeGenerationToolStripMenuItem"; - this.codeGenerationToolStripMenuItem.Size = new System.Drawing.Size(172, 22); - this.codeGenerationToolStripMenuItem.Text = "Code Generation..."; - this.codeGenerationToolStripMenuItem.Click += new System.EventHandler(this.codeGenerationToolStripMenuItem_Click); + codeGenerationToolStripMenuItem.Name = "codeGenerationToolStripMenuItem"; + codeGenerationToolStripMenuItem.Size = new System.Drawing.Size(172, 22); + codeGenerationToolStripMenuItem.Text = "Code Generation..."; + codeGenerationToolStripMenuItem.Click += codeGenerationToolStripMenuItem_Click; // // listAllTypesToolStripMenuItem // - this.listAllTypesToolStripMenuItem.Name = "listAllTypesToolStripMenuItem"; - this.listAllTypesToolStripMenuItem.Size = new System.Drawing.Size(172, 22); - this.listAllTypesToolStripMenuItem.Text = "List All Types"; - this.listAllTypesToolStripMenuItem.Click += new System.EventHandler(this.ListAllTypesToolStripMenuItem_Click); + listAllTypesToolStripMenuItem.Name = "listAllTypesToolStripMenuItem"; + listAllTypesToolStripMenuItem.Size = new System.Drawing.Size(172, 22); + listAllTypesToolStripMenuItem.Text = "List All Types"; + listAllTypesToolStripMenuItem.Click += ListAllTypesToolStripMenuItem_Click; // // toolStripSeparator1 // - this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(226, 6); + toolStripSeparator1.Name = "toolStripSeparator1"; + toolStripSeparator1.Size = new System.Drawing.Size(226, 6); // // showPerformanceCounterToolStripMenuItem // - this.showPerformanceCounterToolStripMenuItem.Name = "showPerformanceCounterToolStripMenuItem"; - this.showPerformanceCounterToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.showPerformanceCounterToolStripMenuItem.Text = "Show Performance Counter..."; - this.showPerformanceCounterToolStripMenuItem.Click += new System.EventHandler(this.showPerformanceCounterToolStripMenuItem_Click); + showPerformanceCounterToolStripMenuItem.Name = "showPerformanceCounterToolStripMenuItem"; + showPerformanceCounterToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + showPerformanceCounterToolStripMenuItem.Text = "Show Performance Counter..."; + showPerformanceCounterToolStripMenuItem.Click += showPerformanceCounterToolStripMenuItem_Click; // // lastCommandMonitorToolStripMenuItem // - this.lastCommandMonitorToolStripMenuItem.Name = "lastCommandMonitorToolStripMenuItem"; - this.lastCommandMonitorToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.lastCommandMonitorToolStripMenuItem.Text = "Last Command Monitor"; - this.lastCommandMonitorToolStripMenuItem.Click += new System.EventHandler(this.lastCommandMonitorToolStripMenuItem_Click); + lastCommandMonitorToolStripMenuItem.Name = "lastCommandMonitorToolStripMenuItem"; + lastCommandMonitorToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + lastCommandMonitorToolStripMenuItem.Text = "Last Command Monitor"; + lastCommandMonitorToolStripMenuItem.Click += lastCommandMonitorToolStripMenuItem_Click; // // openExeDirectoryToolStripMenuItem // - this.openExeDirectoryToolStripMenuItem.Name = "openExeDirectoryToolStripMenuItem"; - this.openExeDirectoryToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.openExeDirectoryToolStripMenuItem.Text = "Open exe Directory"; - this.openExeDirectoryToolStripMenuItem.Click += new System.EventHandler(this.openExeDirectoryToolStripMenuItem_Click); + openExeDirectoryToolStripMenuItem.Name = "openExeDirectoryToolStripMenuItem"; + openExeDirectoryToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + openExeDirectoryToolStripMenuItem.Text = "Open exe Directory"; + openExeDirectoryToolStripMenuItem.Click += openExeDirectoryToolStripMenuItem_Click; // // queryToolStripMenuItem // - this.queryToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.queryCatalogue, - this.queryDataExport}); - this.queryToolStripMenuItem.Name = "queryToolStripMenuItem"; - this.queryToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.queryToolStripMenuItem.Text = "Query"; + queryToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { queryCatalogue, queryDataExport }); + queryToolStripMenuItem.Name = "queryToolStripMenuItem"; + queryToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + queryToolStripMenuItem.Text = "Query"; // // queryCatalogue // - this.queryCatalogue.Name = "queryCatalogue"; - this.queryCatalogue.Size = new System.Drawing.Size(144, 22); - this.queryCatalogue.Text = "Catalogue..."; - this.queryCatalogue.Click += new System.EventHandler(this.queryCatalogue_Click); + queryCatalogue.Name = "queryCatalogue"; + queryCatalogue.Size = new System.Drawing.Size(144, 22); + queryCatalogue.Text = "Catalogue..."; + queryCatalogue.Click += queryCatalogue_Click; // // queryDataExport // - this.queryDataExport.Name = "queryDataExport"; - this.queryDataExport.Size = new System.Drawing.Size(144, 22); - this.queryDataExport.Text = "Data Export..."; - this.queryDataExport.Click += new System.EventHandler(this.queryDataExport_Click); + queryDataExport.Name = "queryDataExport"; + queryDataExport.Size = new System.Drawing.Size(144, 22); + queryDataExport.Text = "Data Export..."; + queryDataExport.Click += queryDataExport_Click; // // restartApplicationToolStripMenuItem // - this.restartApplicationToolStripMenuItem.Name = "restartApplicationToolStripMenuItem"; - this.restartApplicationToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.restartApplicationToolStripMenuItem.Text = "Restart Application"; - this.restartApplicationToolStripMenuItem.Click += new System.EventHandler(this.restartApplicationToolStripMenuItem_Click); + restartApplicationToolStripMenuItem.Name = "restartApplicationToolStripMenuItem"; + restartApplicationToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + restartApplicationToolStripMenuItem.Text = "Restart Application"; + restartApplicationToolStripMenuItem.Click += restartApplicationToolStripMenuItem_Click; // // terminateProcessToolStripMenuItem // - this.terminateProcessToolStripMenuItem.Name = "terminateProcessToolStripMenuItem"; - this.terminateProcessToolStripMenuItem.Size = new System.Drawing.Size(229, 22); - this.terminateProcessToolStripMenuItem.Text = "Terminate Process"; - this.terminateProcessToolStripMenuItem.Click += new System.EventHandler(this.terminateProcessToolStripMenuItem_Click); + terminateProcessToolStripMenuItem.Name = "terminateProcessToolStripMenuItem"; + terminateProcessToolStripMenuItem.Size = new System.Drawing.Size(229, 22); + terminateProcessToolStripMenuItem.Text = "Terminate Process"; + terminateProcessToolStripMenuItem.Click += terminateProcessToolStripMenuItem_Click; // // helpToolStripMenuItem // - this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.userManualToolStripMenuItem, - this.generateClassTableSummaryToolStripMenuItem, - this.showHelpToolStripMenuItem, - this.tutorialsToolStripMenuItem, - this.licenseToolStripMenuItem, - this.checkForUpdatesToolStripMenuItem}); - this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; - this.helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20); - this.helpToolStripMenuItem.Text = "Help"; + helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { userManualToolStripMenuItem, generateClassTableSummaryToolStripMenuItem, showHelpToolStripMenuItem, tutorialsToolStripMenuItem, licenseToolStripMenuItem, checkForUpdatesToolStripMenuItem }); + helpToolStripMenuItem.Name = "helpToolStripMenuItem"; + helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + helpToolStripMenuItem.Text = "Help"; // // userManualToolStripMenuItem // - this.userManualToolStripMenuItem.Name = "userManualToolStripMenuItem"; - this.userManualToolStripMenuItem.Size = new System.Drawing.Size(174, 22); - this.userManualToolStripMenuItem.Text = "Show User Manual"; - this.userManualToolStripMenuItem.Click += new System.EventHandler(this.userManualToolStripMenuItem_Click); + userManualToolStripMenuItem.Name = "userManualToolStripMenuItem"; + userManualToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + userManualToolStripMenuItem.Text = "Show User Manual"; + userManualToolStripMenuItem.Click += userManualToolStripMenuItem_Click; // // generateClassTableSummaryToolStripMenuItem // - this.generateClassTableSummaryToolStripMenuItem.Name = "generateClassTableSummaryToolStripMenuItem"; - this.generateClassTableSummaryToolStripMenuItem.Size = new System.Drawing.Size(174, 22); - this.generateClassTableSummaryToolStripMenuItem.Text = "Show Objects Help"; - this.generateClassTableSummaryToolStripMenuItem.ToolTipText = "Lists all RDMP objects (e.g. Catalogue) and what they model within the system."; - this.generateClassTableSummaryToolStripMenuItem.Click += new System.EventHandler(this.generateClassTableSummaryToolStripMenuItem_Click); + generateClassTableSummaryToolStripMenuItem.Name = "generateClassTableSummaryToolStripMenuItem"; + generateClassTableSummaryToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + generateClassTableSummaryToolStripMenuItem.Text = "Show Objects Help"; + generateClassTableSummaryToolStripMenuItem.ToolTipText = "Lists all RDMP objects (e.g. Catalogue) and what they model within the system."; + generateClassTableSummaryToolStripMenuItem.Click += generateClassTableSummaryToolStripMenuItem_Click; // // showHelpToolStripMenuItem // - this.showHelpToolStripMenuItem.Name = "showHelpToolStripMenuItem"; - this.showHelpToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F1; - this.showHelpToolStripMenuItem.Size = new System.Drawing.Size(174, 22); - this.showHelpToolStripMenuItem.Text = "Show Help"; - this.showHelpToolStripMenuItem.Click += new System.EventHandler(this.showHelpToolStripMenuItem_Click); + showHelpToolStripMenuItem.Name = "showHelpToolStripMenuItem"; + showHelpToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F1; + showHelpToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + showHelpToolStripMenuItem.Text = "Show Help"; + showHelpToolStripMenuItem.Click += showHelpToolStripMenuItem_Click; // // tutorialsToolStripMenuItem // - this.tutorialsToolStripMenuItem.Name = "tutorialsToolStripMenuItem"; - this.tutorialsToolStripMenuItem.Size = new System.Drawing.Size(174, 22); - this.tutorialsToolStripMenuItem.Text = "Tutorials"; + tutorialsToolStripMenuItem.Name = "tutorialsToolStripMenuItem"; + tutorialsToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + tutorialsToolStripMenuItem.Text = "Tutorials"; // // licenseToolStripMenuItem // - this.licenseToolStripMenuItem.Name = "licenseToolStripMenuItem"; - this.licenseToolStripMenuItem.Size = new System.Drawing.Size(174, 22); - this.licenseToolStripMenuItem.Text = "License"; - this.licenseToolStripMenuItem.Click += new System.EventHandler(this.licenseToolStripMenuItem_Click); + licenseToolStripMenuItem.Name = "licenseToolStripMenuItem"; + licenseToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + licenseToolStripMenuItem.Text = "License"; + licenseToolStripMenuItem.Click += licenseToolStripMenuItem_Click; // // checkForUpdatesToolStripMenuItem // - this.checkForUpdatesToolStripMenuItem.Name = "checkForUpdatesToolStripMenuItem"; - this.checkForUpdatesToolStripMenuItem.Size = new System.Drawing.Size(174, 22); - this.checkForUpdatesToolStripMenuItem.Text = "Check for updates"; - this.checkForUpdatesToolStripMenuItem.Click += new System.EventHandler(this.checkForUpdatesToolStripMenuItem_Click); + checkForUpdatesToolStripMenuItem.Name = "checkForUpdatesToolStripMenuItem"; + checkForUpdatesToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + checkForUpdatesToolStripMenuItem.Text = "Check for updates"; + checkForUpdatesToolStripMenuItem.Click += checkForUpdatesToolStripMenuItem_Click; // // rdmpTaskBar1 // - this.rdmpTaskBar1.Dock = System.Windows.Forms.DockStyle.Top; - this.rdmpTaskBar1.Location = new System.Drawing.Point(0, 24); - this.rdmpTaskBar1.Margin = new System.Windows.Forms.Padding(5, 3, 5, 3); - this.rdmpTaskBar1.Name = "rdmpTaskBar1"; - this.rdmpTaskBar1.Size = new System.Drawing.Size(1353, 29); - this.rdmpTaskBar1.TabIndex = 57; + rdmpTaskBar1.Dock = System.Windows.Forms.DockStyle.Top; + rdmpTaskBar1.Location = new System.Drawing.Point(0, 24); + rdmpTaskBar1.Margin = new System.Windows.Forms.Padding(5, 3, 5, 3); + rdmpTaskBar1.Name = "rdmpTaskBar1"; + rdmpTaskBar1.Size = new System.Drawing.Size(1353, 29); + rdmpTaskBar1.TabIndex = 57; // - // viewHistoryToolStripMenuItem + // instanceSettingsToolStripMenuItem // - this.viewHistoryToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("viewHistoryToolStripMenuItem.Image"))); - this.viewHistoryToolStripMenuItem.Name = "viewHistoryToolStripMenuItem"; - this.viewHistoryToolStripMenuItem.Size = new System.Drawing.Size(238, 22); - this.viewHistoryToolStripMenuItem.Text = "View History..."; - this.viewHistoryToolStripMenuItem.Click += new System.EventHandler(this.viewHistoryToolStripMenuItem_Click); + instanceSettingsToolStripMenuItem.Name = "instanceSettingsToolStripMenuItem"; + instanceSettingsToolStripMenuItem.Size = new System.Drawing.Size(238, 22); + instanceSettingsToolStripMenuItem.Text = "Instance Settings..."; + instanceSettingsToolStripMenuItem.Click += instanceSettingsToolStripMenuItem_Click; // // RDMPTopMenuStripUI // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.rdmpTaskBar1); - this.Controls.Add(this.menuStrip1); - this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.Name = "RDMPTopMenuStripUI"; - this.Size = new System.Drawing.Size(1353, 55); - this.menuStrip1.ResumeLayout(false); - this.menuStrip1.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + Controls.Add(rdmpTaskBar1); + Controls.Add(menuStrip1); + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + Name = "RDMPTopMenuStripUI"; + Size = new System.Drawing.Size(1353, 55); + menuStrip1.ResumeLayout(false); + menuStrip1.PerformLayout(); + ResumeLayout(false); + PerformLayout(); } #endregion @@ -592,5 +547,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem terminateProcessToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem findMultipleToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem viewHistoryToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem instanceSettingsToolStripMenuItem; } } diff --git a/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.cs b/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.cs index 34b5eac8ca..592dbfa38e 100644 --- a/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.cs +++ b/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.cs @@ -407,6 +407,12 @@ private void userSettingsToolStripMenuItem_Click(object sender, EventArgs e) settings.Show(); } + private void instanceSettingsToolStripMenuItem_Click(object sender, EventArgs e) + { + var settings = new InstanceSettings(Activator); + settings.Show(); + } + private void licenseToolStripMenuItem_Click(object sender, EventArgs e) { var l = new LicenseUI(); @@ -457,6 +463,8 @@ private void navigateForwardToolStripMenuItem_Click(object sender, EventArgs e) _windowManager.Navigation.Forward(true); } + + private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e) { // AutoUpdater.NET is Windows-only for now: diff --git a/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.resx b/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.resx index 9bcc9342ac..0565e26b2b 100644 --- a/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.resx +++ b/Application/ResearchDataManagementPlatform/Menus/RDMPTopMenuStripUI.resx @@ -1,4 +1,64 @@ - + + + @@ -96,7 +156,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wQAADsEBuJFr7QAAAO5JREFUOE/lk7ENwjAQRSkpKRmBMSgpKBmAERiBHqQMwBAMQUFBkRkQEi0lBQX8 + wAAADsABataJCQAAAO5JREFUOE/lk7ENwjAQRSkpKRmBMSgpKBmAERiBHqQMwBAMQUFBkRkQEi0lBQX8 F/wVy4klhxZ9PeUO537O5jyqWs3FSTzEOeSD5IDCdw9LUSwHdETxTkxFFfKLKJYDtkYxRogn+TPkRXLA GfV1VotiOeBsKE5ZiWJFYWN4Fzbai0GKwkZbYbOX+LkzZDP/u7ARqXpnsl3+ymY8MbHhQUwEys6kTazY DK0F48FvbJturiHvzKRNrNQMzcRR2NR0ZtIFiPb91VvIY43FQuQ6q/1i9hxEquxM+oWhdxND1tg6t6QZ diff --git a/CHANGELOG.md b/CHANGELOG.md index 3136a1cb20..bf80a91c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,25 @@ - # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [8.2.0] - Unreleased + +## Changed + +- Add Key-Value store for instance settings +- Allow for Re-extractions of projects to a database, see [ExecuteFullExtractionToDatabaseMSSql](Documentation\DataExtractions\ExecuteFullExtractionToDatabaseMSSql.md) +- Add ability to use .rdmp plugin files +- Add the ability to store versions of cohort configurations, see [Cohort Versioning](Documentation\Cohorts\CohortVersioning.md) +- Add ability to restrict GROUPBY clause in cohort aggregate builder +- When cloning an ExtractionConfiguration with a deprecated catalogue, the GUI will ask if you want to replace the deprecated catalogue with the known replacement +- Add ability to customise LoadMetdata Folder Location. See [LoadMetadata](Documentation\DataLoadEngine\LoadMetadata.md) +- Add ability to point a catalogue to a new data source [Documentation](./Documentation/Catalogues/UpdateCatalogueDataLocation.md) +- Allow DQE graphs to be scrollable and scalable +- Allow for partial refreshes of time-based DQE charts +- Fix issue when creating PostgreSQL Logging Server + ## [8.1.7] - 2024-06-17 ## Changed @@ -59,7 +74,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump HIC.BadMedicine from 1.2.0 to 1.2.1 - Bump NPOI from 2.6.2 to 2.7.0 - ## [8.1.4] - 2024-02-19 ## Changed diff --git a/Documentation/Catalogues/UpdateCatalogueDataLocation.md b/Documentation/Catalogues/UpdateCatalogueDataLocation.md new file mode 100644 index 0000000000..d81655b7ff --- /dev/null +++ b/Documentation/Catalogues/UpdateCatalogueDataLocation.md @@ -0,0 +1,17 @@ +# Update A [Catalogue](../CodeTutorials/Glossary.md#Catalogue) Data Location +It can be useful to migrate data from one database to another. +Instead of reimporting a [catalogue](../CodeTutorials/Glossary.md#Catalogue), RDMP allows you to repoint the [catalogue](../CodeTutorials/Glossary.md#Catalogue) to the location of your moved data. + +## How to +* Right Click an a Catalogue > Catalogue Items > Update Catalogue Data Location +* This will open a new dialog +* From here, you can select which columns you wish to update and tell RDMP where the new location is +* RDMP will perform several checks to make sure the new data location is available and of the correct types +* It will inform you of any issues it comes across, otherwise will migrate the catalogue data references to the selected location + +## What Does it change? +This functionality updates the underlying [ColumnInfo](../CodeTutorials/Glossary.md#ColumnInfo) to point to the new table. It also updates the extractionInformation SelectSQL to allow for future extractions to continue to work. +If you select a never-before-seen table, RDMP will also generate a new known table record in case of future use + +[Catalogue]: ../CodeTutorials/Glossary.md#Catalogue +[ColumnInfo]: ../CodeTutorials/Glossary.md#ColumnInfo diff --git a/Documentation/CodeTutorials/FAQ.md b/Documentation/CodeTutorials/FAQ.md index fda1e96817..297ae2c094 100644 --- a/Documentation/CodeTutorials/FAQ.md +++ b/Documentation/CodeTutorials/FAQ.md @@ -412,7 +412,7 @@ Yes, [determining database types from untyped data (e.g. CSV)](./DataTableUpload ### What is the purpose of the the data load folders (ForLoading, ForArchiving etc)? -RDMP data load jobs are configured by creating a [LoadMetadata]. When you create a new [LoadMetadata] you will be prompted to choose/create a set of load folders. The current value is stored in the `LocationOfFlatFiles` field of [LoadMetadata]. The directories created have the following layout: +RDMP data load jobs are configured by creating a [LoadMetadata]. When you create a new [LoadMetadata] you will be prompted to choose/create a set of load folders. The default directories created have the following layout: ``` someFolder diff --git a/Documentation/CodeTutorials/TestPlan.md b/Documentation/CodeTutorials/TestPlan.md index 5a5afe2cf5..33d90eab68 100644 --- a/Documentation/CodeTutorials/TestPlan.md +++ b/Documentation/CodeTutorials/TestPlan.md @@ -53,4 +53,4 @@ It may be useful at this point to revisit each piece of functionality and ensure * Does the release work as epxected with a fresh install and via the upgrade path? * Is all functionality documented? * Are all version numbers bumped correctly? -* Do all of the managed plugins work with the new release without issues or warnings? \ No newline at end of file +* Do all of the managed plugins work with the new release without issues or warnings? diff --git a/Documentation/Cohorts/CohortVersioning.md b/Documentation/Cohorts/CohortVersioning.md new file mode 100644 index 0000000000..769811f791 --- /dev/null +++ b/Documentation/Cohorts/CohortVersioning.md @@ -0,0 +1,26 @@ +# Cohort Versioning +Cohort Identity Configurations can be versioned, allowing you to store copies of cohort configurations prior to making updates. +you may want to use versioning instead of cloning the cohort to keep the tree list of cohort configurations simple, as versions do not appear in the traditional RDMP tree structure. + +Cohort versioning is accessible via the Cohort creation page within RDMP, from here you can open existing versions of the cohort, or save the current configuration as a new version. + +## Command Line +Versioning can be performed by using the CreateVersionOfCohortConfiguration command. +It can be ran with the following options +``` +CreateVersionOfCohortConfiguration cic:{some_id} name:{some_name} +``` + +Cohort versions can be used in the same way as a standard cohort identification configuration, their ID is accessible via the command +``` +ListCohortVersions cic:{some_id} +``` +This command will list the names and IDs of and versions associated with a cohort configuration + +Within the CLI, cohort versions will appear alongside top-level cohort identity configurations when performing commands such as +``` +List cic:* +``` + +## Cloning a cohort with Versions +Cloning a cohort configuration with versions does not clone the versions alongside the cohort configuration diff --git a/Documentation/DataExtractions/ExecuteFullExtractionToDatabaseMSSql.md b/Documentation/DataExtractions/ExecuteFullExtractionToDatabaseMSSql.md new file mode 100644 index 0000000000..7055253144 --- /dev/null +++ b/Documentation/DataExtractions/ExecuteFullExtractionToDatabaseMSSql.md @@ -0,0 +1,33 @@ +## Execute Full Extraction To Database MSSQL +This data extraction pipeline component allows you to extract data into a known database. +You can add the database you want to extract to as an external database within RDMP. + +This component has several configurable options that are detailed below +| Configuration Option | Description | +|-------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Target Database Server | External server to create the extraction into, a new database will be created for the [Project]: ../CodeTutorials/Glossary.md#Project based on the naming pattern provided | +| Database Naming Pattern | How do you want to name datasets, use the following tokens if you need them: $p - [Project](../CodeTutorials/Glossary.md#Project) Name ('e.g. My [Project](../CodeTutorials/Glossary.md#Project)') $n - [Project](../CodeTutorials/Glossary.md#Project) Number (e.g. 234) $t - Master Ticket (e.g. 'LINK-1234') $r - Request Ticket (e.g. 'LINK-1234') $l - Release Ticket (e.g. 'LINK-1234') Default : Proj_$n_$l | +| Table Naming Pattern | How do you want to name datasets, use the following tokens if you need them: $p - [Project](../CodeTutorials/Glossary.md#Project) Name ('e.g. My [Project](../CodeTutorials/Glossary.md#Project)') $n - [Project](../CodeTutorials/Glossary.md#Project) Number (e.g. 234) $c - Configuration Name (e.g. 'Cases') $d - Dataset name (e.g. 'Prescribing') $a - Dataset acronym (e.g. 'Presc') You must have either $a or $d Default : $c_$d | +| Drop Table If Load Fails | If the extraction fails half way through AND the destination table was created during the extraction then the table will be dropped from the destination rather than being left in a half loaded state | +| Alter Timeout | Timeout to perform all ALTER TABLE operations (column resize and PK creation) | +| Copy Collations | True to copy the column collations from the source database when creating the destination database. Only works if both the source and destination have the same DatabaseType. Excludes columns which feature a transform as part of extraction. | +| Always Drop ExtractionTables | True to always drop the destination database table(s) from the destination if they already existed | +| Make Final Table Distinct When Batching Results | True to apply a distincting operation to the final table when using an [ExtractionProgress](../CodeTutorials/Glossary.md#ExtractionProgress). This prevents data duplication from failed batch resumes. | +| Append Data If Table Exists | If this extraction has already been run, it will append the extraction data into the database. There is no duplication protection with this functionality. | +| Include Timestamp | If checked, a column names 'extraction_timestamp' will be included in the extraction that denotes the time the record was added to the extraction. | +| Use Acronym For File Naming | Naming of flat files is usually based on [Catalogue](../CodeTutorials/Glossary.md#Catalogue).Name, if this is true then the [Catalogue](../CodeTutorials/Glossary.md#Catalogue).Acronym will be used instead | +| Date Format | The date format to output all datetime fields in e.g. dd/MM/yyyy for uk format yyyy-MM-dd for something more machine processable, see https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx | +| Clean Extraction Folder Before Extraction | If this is true, the dataset/globals extraction folder will be wiped clean before extracting the dataset. Useful if you suspect there are spurious files in the folder | +| Extraction Subdirectory Pattern | Overrides the extraction sub directory of datasets as they are extracted $c - Configuration Name (e.g. 'Cases') $i - Configuration ID (e.g. 459) $d - Dataset name (e.g. 'Prescribing') $a - Dataset acronym (e.g. 'Presc') $n - Dataset ID (e.g. 459) e.g. /$i/$a | + +[Project]: ../CodeTutorials/Glossary.md#Project +[ExtractionProgress]: ../CodeTutorials/Glossary.md#ExtractionProgress +[Catalogue]: ../CodeTutorials/Glossary.md#Catalogue + + +## Notes on updating an extraction +Using the "Append Data If Table Exists" option within this extraction destination component will allow you to re-extract additional information to a database, however there a number of caveats and gotchas with this. +* Archive Table + * Similar to data loads, these database tables will come with an _Archive table that will auto-populate when new extractions are ran against the database. +* Data Structure changing + * While the extractor can handle columns being removed, it does not support columns being added beyond the first extraction. For this you will need to write the data to a new database table. It is recommended that the extraction is cloned in this case. diff --git a/Documentation/DataLoadEngine/Images/lmdConfig.PNG b/Documentation/DataLoadEngine/Images/lmdConfig.PNG new file mode 100644 index 0000000000..3777da7853 Binary files /dev/null and b/Documentation/DataLoadEngine/Images/lmdConfig.PNG differ diff --git a/Documentation/DataLoadEngine/LoadMetadata.md b/Documentation/DataLoadEngine/LoadMetadata.md new file mode 100644 index 0000000000..cdeee52513 --- /dev/null +++ b/Documentation/DataLoadEngine/LoadMetadata.md @@ -0,0 +1,34 @@ +# Load Metadata +The Load Metadata stores a reference to the directory you want your data load pipeline to use. +This directory will be populated with a number of folders +``` +- MyFolder +|_ Data +| |_ Cache +| |_ ForArchiving +| |_ ForLoading +|_ Executables +``` +The ForLoading Directory is used as a holding pen for data that you are trying to load into the system + +The ForArchiving Directory is used to store a compressed version of the data involved in the data load for archiving purposes + +The Executables directory is provided for storing executable files and/or sql scripts that you might want to run during the load. + +The Cache folder is used for the RDMP caching engine (long running fetching tasks e.g. pulling images from a PACS server or reports from a webservice). + + +## Using Different Locations for each Load Metadata Directory +N.B. This is an advanced feature and should be used with caution. + +![Load Metadata Config](./Images/lmdConfig.PNG) + + +Some users may with to store their archives, for example, in another location or disk. +RDMP allows this by having the user specify the directory they wish each type of data to be written to. + +This functionality will write files directly into this folder, not into a "Data" subfolder. +It is not recommended to set each folder option to the same location as RDMP will clean up the "ForLoading" directory and delete all files, including the archive, during this step. + + + diff --git a/Documentation/InstanceSettings.md b/Documentation/InstanceSettings.md new file mode 100644 index 0000000000..2fbedbd4f8 --- /dev/null +++ b/Documentation/InstanceSettings.md @@ -0,0 +1,9 @@ +# Instance Settings +RDMP can be installed so that multiple users are connecting to the same instance of the RDMP databases. +When this happens, it may be required that users share certain configuration settings. +RDMP's instance settings are to unify the user experience when operating as a multi-user instance. +Instance settings apply to all users who connect to the RDMP database, where as User Settings are used on a per-user basis. + +| Setting Name | Description | +| -------------| ----------- | +|AutoSuggestProjectNumbers | If True will automatically populate a new project number with an +1 increment from the largest existing project | diff --git a/Rdmp.Core.Tests/Caching/Integration/CachingHostTests.cs b/Rdmp.Core.Tests/Caching/Integration/CachingHostTests.cs index 91b3eb653e..91c80b48be 100644 --- a/Rdmp.Core.Tests/Caching/Integration/CachingHostTests.cs +++ b/Rdmp.Core.Tests/Caching/Integration/CachingHostTests.cs @@ -14,6 +14,7 @@ using Rdmp.Core.Curation; using Rdmp.Core.Curation.Data; using Rdmp.Core.Curation.Data.Cache; +using Rdmp.Core.Curation.Data.DataLoad; using Rdmp.Core.DataFlowPipeline; using Rdmp.Core.ReusableLibraryCode.Progress; using Tests.Common; @@ -39,7 +40,10 @@ public void CacheHostOutwithPermissionWindow() var cp = WhenIHaveA(); var loadMetadata = cp.LoadProgress.LoadMetadata; - loadMetadata.LocationOfFlatFiles = loadDirectory.RootPath.FullName; + loadMetadata.LocationOfForLoadingDirectory = Path.Combine(loadDirectory.RootPath.FullName , ((LoadMetadata)loadMetadata).DefaultForLoadingPath); + loadMetadata.LocationOfForArchivingDirectory = Path.Combine(loadDirectory.RootPath.FullName , ((LoadMetadata)loadMetadata).DefaultForArchivingPath); + loadMetadata.LocationOfExecutablesDirectory = Path.Combine(loadDirectory.RootPath.FullName , ((LoadMetadata)loadMetadata).DefaultExecutablesPath); + loadMetadata.LocationOfCacheDirectory = Path.Combine(loadDirectory.RootPath.FullName , ((LoadMetadata)loadMetadata).DefaultCachePath); // This feels a bit nasty, but quick and much better than having the test wait for an arbitrary time period. var listener = new ExpectedNotificationListener("Download not permitted at this time, sleeping for 60 seconds"); diff --git a/Rdmp.Core.Tests/Caching/Integration/CustomDateCachingTests.cs b/Rdmp.Core.Tests/Caching/Integration/CustomDateCachingTests.cs index 56aca0157b..468cf44f07 100644 --- a/Rdmp.Core.Tests/Caching/Integration/CustomDateCachingTests.cs +++ b/Rdmp.Core.Tests/Caching/Integration/CustomDateCachingTests.cs @@ -61,7 +61,10 @@ public void FetchMultipleDays_Success(bool singleDay) true); var lmd = Substitute.For(); - lmd.LocationOfFlatFiles = projDir.RootPath.FullName; + lmd.LocationOfForLoadingDirectory = Path.Combine(projDir.RootPath.FullName , Path.Combine("Data", "ForLoading")); + lmd.LocationOfForArchivingDirectory = Path.Combine(projDir.RootPath.FullName , Path.Combine("Data", "ForArchiving")); + lmd.LocationOfExecutablesDirectory = Path.Combine(projDir.RootPath.FullName ,"Executables"); + lmd.LocationOfCacheDirectory = Path.Combine(projDir.RootPath.FullName , Path.Combine("Data", "Cache")); var loadProgress = Substitute.For(); loadProgress.OriginDate.Returns(new DateTime(2001, 01, 01)); diff --git a/Rdmp.Core.Tests/CohortCreation/CohortVersioningTests.cs b/Rdmp.Core.Tests/CohortCreation/CohortVersioningTests.cs new file mode 100644 index 0000000000..96879bb078 --- /dev/null +++ b/Rdmp.Core.Tests/CohortCreation/CohortVersioningTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using NUnit.Framework; +using Rdmp.Core.CommandExecution.AtomicCommands; +using Rdmp.Core.CommandLine.Interactive; +using Rdmp.Core.Curation.Data.Cohort; +using Rdmp.Core.ReusableLibraryCode.Checks; +using Rdmp.Core.Tests.CohortCreation.QueryTests; +using System.Linq; + +namespace Rdmp.Core.Tests.CohortCreation; + +public class CohortVersioningTests : CohortQueryBuilderWithCacheTests +{ + + private CohortIdentificationConfiguration GenerateCIC() + { + var cic = new CohortIdentificationConfiguration(CatalogueRepository, "mycic") + { + QueryCachingServer_ID = externalDatabaseServer.ID + }; + cic.SaveToDatabase(); + cic.CreateRootContainerIfNotExists(); + return cic; + } + + [Test] + public void TestCreationOfNewVersionOfCohort() + { + var activator = new ConsoleInputManager(RepositoryLocator, ThrowImmediatelyCheckNotifier.Quiet) + { DisallowInput = true }; + var cic = GenerateCIC(); + var cmd = new ExecuteCommandCreateVersionOfCohortConfiguration(activator, cic); + Assert.DoesNotThrow(() => cmd.Execute()); + var newCic = CatalogueRepository.GetAllObjectsWhere("ClonedFrom_ID", cic.ID); + Assert.That(newCic, Has.Length.EqualTo(1)); + Assert.That(newCic.First().Version, Is.EqualTo(1)); + + Assert.DoesNotThrow(() => cmd.Execute()); + newCic = CatalogueRepository.GetAllObjectsWhere("ClonedFrom_ID", cic.ID); + Assert.That(newCic, Has.Length.EqualTo(2)); + Assert.Multiple(() => + { + Assert.That(newCic.First().Version, Is.EqualTo(1)); + Assert.That(newCic.Last().Version, Is.EqualTo(2)); + }); + } + + [Test] + public void TestCreatingCohortVersionWithName() + { + var activator = new ConsoleInputManager(RepositoryLocator, ThrowImmediatelyCheckNotifier.Quiet) + { DisallowInput = true }; + var cic = GenerateCIC(); + var cmd = new ExecuteCommandCreateVersionOfCohortConfiguration(activator, cic, "MyName!"); + Assert.DoesNotThrow(() => cmd.Execute()); + var newCic = CatalogueRepository.GetAllObjectsWhere("ClonedFrom_ID", cic.ID); + Assert.That(newCic, Has.Length.EqualTo(1)); + Assert.Multiple(() => + { + Assert.That(newCic.First().Version, Is.EqualTo(1)); + Assert.That(newCic.First().Name, Is.EqualTo("MyName!")); + }); + } +} diff --git a/Rdmp.Core.Tests/CommandExecution/ExecuteCommandCreateNewDataLoadDirectoryTests.cs b/Rdmp.Core.Tests/CommandExecution/ExecuteCommandCreateNewDataLoadDirectoryTests.cs index 639f2976ed..467107c2a7 100644 --- a/Rdmp.Core.Tests/CommandExecution/ExecuteCommandCreateNewDataLoadDirectoryTests.cs +++ b/Rdmp.Core.Tests/CommandExecution/ExecuteCommandCreateNewDataLoadDirectoryTests.cs @@ -31,14 +31,41 @@ public void TestCreateNewDataLoadDirectory_WithLoadMetadata() if (Directory.Exists(root)) Directory.Delete(root, true); var lmd = WhenIHaveA(); - Assert.That(lmd.LocationOfFlatFiles, Is.Null); + Assert.That(lmd.LocationOfForLoadingDirectory, Is.Null); Run("CreateNewDataLoadDirectory", $"LoadMetadata:{lmd.ID}", root); Assert.Multiple(() => { Assert.That(Directory.Exists(root)); - Assert.That(lmd.LocationOfFlatFiles, Is.EqualTo(root)); + Assert.That(lmd.LocationOfForLoadingDirectory, Is.EqualTo($"{root}\\Data\\ForLoading")); + Assert.That(lmd.LocationOfForArchivingDirectory, Is.EqualTo($"{root}\\Data\\ForArchiving")); + Assert.That(lmd.LocationOfExecutablesDirectory, Is.EqualTo($"{root}\\Executables")); + Assert.That(lmd.LocationOfCacheDirectory, Is.EqualTo($"{root}\\Data\\Cache")); + }); + } + + [Test] + public void TestCreateNewDataLoadDirectory_WithSplitLoadMetadata() + { + var root = Path.Combine(TestContext.CurrentContext.WorkDirectory, "def"); + if (Directory.Exists(root)) Directory.Delete(root, true); + var lmd = WhenIHaveA(); + + Assert.That(lmd.LocationOfForLoadingDirectory, Is.Null); + Assert.That(lmd.LocationOfForArchivingDirectory, Is.Null); + Assert.That(lmd.LocationOfExecutablesDirectory, Is.Null); + Assert.That(lmd.LocationOfCacheDirectory, Is.Null); + + Run("CreateNewSplitDataLoadDirectory", $"LoadMetadata:{lmd.ID}", root, root, root, root); + + Assert.Multiple(() => + { + Assert.That(lmd.LocationOfForLoadingDirectory, Is.EqualTo(root)); + Assert.That(lmd.LocationOfForArchivingDirectory, Is.EqualTo(root)); + Assert.That(lmd.LocationOfExecutablesDirectory, Is.EqualTo(root)); + Assert.That(lmd.LocationOfCacheDirectory, Is.EqualTo(root)); + }); } } \ No newline at end of file diff --git a/Rdmp.Core.Tests/CommandExecution/ExecuteCommandUpdateCatalogueDataLocationTests.cs b/Rdmp.Core.Tests/CommandExecution/ExecuteCommandUpdateCatalogueDataLocationTests.cs new file mode 100644 index 0000000000..ee6b3e2691 --- /dev/null +++ b/Rdmp.Core.Tests/CommandExecution/ExecuteCommandUpdateCatalogueDataLocationTests.cs @@ -0,0 +1,229 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using FAnsi; +using FAnsi.Discovery; +using NUnit.Framework; +using Rdmp.Core.CommandExecution; +using Rdmp.Core.CommandExecution.AtomicCommands; +using Rdmp.Core.CommandExecution.AtomicCommands.CatalogueCreationCommands; +using Rdmp.Core.CommandLine.DatabaseCreation; +using Rdmp.Core.Curation.Data; +using Rdmp.Core.Curation.Data.Pipelines; +using Tests.Common; + +namespace Rdmp.Core.Tests.CommandExecution; + +public class ExecuteCommandUpdateCatalogueDataLocationTests : DatabaseTests +{ + protected DiscoveredDatabase RemoteDatabase { get; set; } + protected DiscoveredTable RemoteTable { get; set; } + protected string RemoteDatabaseName = TestDatabaseNames.GetConsistentName("rdb"); + + private DiscoveredDatabase db; + + private int goodCatalogueID; + private int originalTableInfoID; + + private void CreateDatabase() + { + RemoteDatabase = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(RemoteDatabaseName); + RemoteDatabase.Create(true); + using (var dt = new DataTable()) + { + dt.Columns.Add("Column1", typeof(string)); + dt.Columns.Add("Column2", typeof(int)); + + var dr = dt.NewRow(); + dr["Column1"] = "d"; + dr["Column2"] = 100; + dt.Rows.Add(dr); + RemoteTable = RemoteDatabase.CreateTable("SomeTable", dt); + } + } + + private void CreateCatalogue() + { + var fileName = Path.GetTempPath() + Guid.NewGuid() + ".csv"; + using (var outputFile = new StreamWriter(fileName, true)) + { + outputFile.WriteLine("Column1,Column2"); + outputFile.WriteLine("b,1"); + outputFile.WriteLine("c,2"); + } + + var info = new FileInfo(fileName); + db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + + var creator = new CataloguePipelinesAndReferencesCreation( + RepositoryLocator, UnitTestLoggingConnectionString, DataQualityEngineConnectionString); + if (CatalogueRepository.GetAllObjects().Length == 0) + creator.CreatePipelines(new PlatformDatabaseCreationOptions()); + + var pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + var cmd = new ExecuteCommandCreateNewCatalogueByImportingFile(new ThrowImmediatelyActivator(RepositoryLocator), + info, "Column1", db, pipe, null); + cmd.Execute(); + originalTableInfoID = RepositoryLocator.CatalogueRepository.GetAllObjects().First().ColumnInfo + .TableInfo_ID; + var column1 = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column1").First(); + column1.ExtractionInformation.SelectSQL = column1.ExtractionInformation.SelectSQL + " as SOME_ALIAS"; + column1.ExtractionInformation.SaveToDatabase(); + } + + private void CreateSecondaryCatalogue() + { + var fileName = Path.GetTempPath() + Guid.NewGuid() + ".csv"; + using (var outputFile = new StreamWriter(fileName, true)) + { + outputFile.WriteLine("Column,Other"); + outputFile.WriteLine("b,1"); + } + + var info = new FileInfo(fileName); + + // find the excel loading pipeline + var pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + var cmd = new ExecuteCommandCreateNewCatalogueByImportingFile(new ThrowImmediatelyActivator(RepositoryLocator), + info, "Other", db, pipe, null); + cmd.Execute(); + } + + [OneTimeSetUp] + protected override void OneTimeSetUp() + { + base.OneTimeSetUp(); + CreateDatabase(); + CreateCatalogue(); + CreateSecondaryCatalogue(); + } + + + [Test] + public void UpdateLocationCheckNoItems() + { + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + new List().ToArray(), RemoteTable, null); + Assert.That(cmd.Check(), Is.EqualTo("Must select at least one catalogue item to modify")); + } + + [Test] + public void UpdateLocationCheckBadMapping() + { + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects(), RemoteTable, "badMaping"); + Assert.That(cmd.Check(), Is.EqualTo("Column Mapping must contain the string '$column'")); + } + + [Test] + public void UpdateLocationColumnNotExist() + { + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects().Where(c => c.Name == "Column") + .ToArray(), RemoteTable, "$column"); + Assert.That(cmd.Check(), Is.EqualTo("Unable to find column '[Column]' in selected table")); + } + + [Test] + public void UpdateLocationColumnBadType() + { + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects().Where(c => c.Name == "Column") + .ToArray(), RemoteTable, "$column2"); + Assert.That(cmd.Check(), + Is.EqualTo( + "The data type of column '[Column2]' is of type 'int'. This does not match the current type of 'varchar(1)'")); + } + + [Test] + public void UpdateLocationCheckOK() + { + goodCatalogueID = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column2").First().Catalogue_ID; + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Catalogue_ID == goodCatalogueID).ToArray(), RemoteTable, null); + Assert.That(cmd.Check(), Is.EqualTo(null)); + } + + [Test] + public void UpdateLocationExecuteNoCheck_Bad() + { + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects(), RemoteTable, "badMapping"); + var ex = Assert.Throws(() => cmd.Execute()); + Assert.That(ex.Message, + Is.EqualTo( + "Unable to execute ExecuteCommandUpdateCatalogueDataLocation as the checks returned: Column Mapping must contain the string '$column'")); + } + + [Test] + public void UpdateLocationExecuteNoCheck_OK() + { + goodCatalogueID = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column2").First().Catalogue_ID; + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Catalogue_ID == goodCatalogueID).ToArray(), RemoteTable, null); + Assert.DoesNotThrow(() => cmd.Execute()); + var ci = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column2" && c.Catalogue_ID == goodCatalogueID).First(); + Assert.That(ci.ColumnInfo.Name, Is.EqualTo(RemoteTable.GetFullyQualifiedName() + ".[Column2]")); + Assert.That(ci.ExtractionInformation.SelectSQL, Is.EqualTo(RemoteTable.GetFullyQualifiedName() + ".[Column2]")); + Assert.That(ci.ColumnInfo.TableInfo_ID, Is.Not.EqualTo(originalTableInfoID)); + } + + [Test] + public void UpdateLocationExecuteNoCheck_AliasCheck() + { + goodCatalogueID = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column2").First().Catalogue_ID; + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Catalogue_ID == goodCatalogueID).ToArray(), RemoteTable, null); + Assert.DoesNotThrow(() => cmd.Execute()); + var ci = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column1" && c.Catalogue_ID == goodCatalogueID).First(); + Assert.That(ci.ColumnInfo.Name, Is.EqualTo(RemoteTable.GetFullyQualifiedName() + ".[Column1]")); + var ei = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(e => e.ID == ci.ExtractionInformation.ID).First(); + Assert.That(ei.SelectSQL, Is.EqualTo(RemoteTable.GetFullyQualifiedName() + ".[Column1] as SOME_ALIAS")); + Assert.That(ci.ColumnInfo.TableInfo_ID, Is.Not.EqualTo(originalTableInfoID)); + } + + [Test] + public void UpdateLocationWithMultipleExtractionIdentifiers() + { + goodCatalogueID = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column2").First().Catalogue_ID; + var ci = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column2" && c.Catalogue_ID == goodCatalogueID).First(); + + var otherci = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Name == "Column").First(); + var cmd1 = new ExecuteCommandAddNewCatalogueItem(new ThrowImmediatelyActivator(RepositoryLocator), ci.Catalogue, + new List { otherci.ColumnInfo }.ToArray()); + Assert.DoesNotThrow(() => cmd1.Execute()); + + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(new ThrowImmediatelyActivator(RepositoryLocator), + RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(c => c.Catalogue_ID == goodCatalogueID && c.Name == "Column2").ToArray(), RemoteTable, null); + Assert.DoesNotThrow(() => cmd.Execute()); + Assert.That(ci.ColumnInfo.Name, Is.EqualTo(RemoteTable.GetFullyQualifiedName() + ".[Column2]")); + var ei = RepositoryLocator.CatalogueRepository.GetAllObjects() + .Where(e => e.ID == ci.ExtractionInformation.ID).First(); + Assert.That(ei.SelectSQL, Is.EqualTo(RemoteTable.GetFullyQualifiedName() + ".[Column2]")); + Assert.That(ci.ColumnInfo.TableInfo_ID, Is.Not.EqualTo(originalTableInfoID)); + } +} \ No newline at end of file diff --git a/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndCacheTest.cs b/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndCacheTest.cs index 381bc2f0c6..076f36a837 100644 --- a/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndCacheTest.cs +++ b/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndCacheTest.cs @@ -49,7 +49,10 @@ protected override void SetUp() _LoadDirectory = LoadDirectory.CreateDirectoryStructure(new DirectoryInfo(TestContext.CurrentContext.TestDirectory), @"EndToEndCacheTest", true); - _lmd.LocationOfFlatFiles = _LoadDirectory.RootPath.FullName; + _lmd.LocationOfForLoadingDirectory = Path.Combine(_LoadDirectory.RootPath.FullName , _lmd.DefaultForLoadingPath); + _lmd.LocationOfForArchivingDirectory = Path.Combine(_LoadDirectory.RootPath.FullName , _lmd.DefaultForArchivingPath); + _lmd.LocationOfExecutablesDirectory = Path.Combine(_LoadDirectory.RootPath.FullName , _lmd.DefaultExecutablesPath); + _lmd.LocationOfCacheDirectory = Path.Combine(_LoadDirectory.RootPath.FullName , _lmd.DefaultCachePath); _lmd.SaveToDatabase(); Clear(_LoadDirectory); diff --git a/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndDLETest.cs b/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndDLETest.cs index bd7cd5e8bd..2772b7f2af 100644 --- a/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndDLETest.cs +++ b/Rdmp.Core.Tests/CommandLine/AutomationLoopTests/EndToEndDLETest.cs @@ -13,6 +13,7 @@ using Tests.Common; using Tests.Common.Scenarios; using TypeGuesser; +using System.IO; namespace Rdmp.Core.Tests.CommandLine.AutomationLoopTests; @@ -44,10 +45,11 @@ public void TestDle_DodgyColumnNames(DatabaseType dbType) }); var cata = Import(tbl); - var lmd = new LoadMetadata(CatalogueRepository, nameof(TestDle_DodgyColumnNames)) - { - LocationOfFlatFiles = LoadDirectory.RootPath.FullName - }; + var lmd = new LoadMetadata(CatalogueRepository, nameof(TestDle_DodgyColumnNames)); + lmd.LocationOfForLoadingDirectory = Path.Combine(LoadDirectory.RootPath.FullName , lmd.DefaultForLoadingPath); + lmd.LocationOfForArchivingDirectory = Path.Combine(LoadDirectory.RootPath.FullName , lmd.DefaultForArchivingPath); + lmd.LocationOfExecutablesDirectory = Path.Combine(LoadDirectory.RootPath.FullName , lmd.DefaultExecutablesPath); + lmd.LocationOfCacheDirectory = Path.Combine(LoadDirectory.RootPath.FullName , lmd.DefaultCachePath); lmd.SaveToDatabase(); CreateFlatFileAttacher(lmd, "Troll.csv", cata.GetTableInfoList(false).Single()); diff --git a/Rdmp.Core.Tests/Curation/Integration/LoadMetadataTests.cs b/Rdmp.Core.Tests/Curation/Integration/LoadMetadataTests.cs index db8467999b..167488c598 100644 --- a/Rdmp.Core.Tests/Curation/Integration/LoadMetadataTests.cs +++ b/Rdmp.Core.Tests/Curation/Integration/LoadMetadataTests.cs @@ -5,7 +5,9 @@ // You should have received a copy of the GNU General Public License along with RDMP. If not, see . using System; +using System.IO; using NUnit.Framework; +using Rdmp.Core.Curation; using Rdmp.Core.Curation.Data.DataLoad; using Rdmp.Core.DataLoad.Engine.Checks.Checkers; using Rdmp.Core.DataLoad.Engine.DatabaseManagement.EntityNaming; @@ -24,11 +26,17 @@ public void CreateNewAndGetBackFromDatabase() try { - loadMetadata.LocationOfFlatFiles = TestContext.CurrentContext.TestDirectory; + loadMetadata.LocationOfForLoadingDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultForLoadingPath); + loadMetadata.LocationOfForArchivingDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultForArchivingPath); + loadMetadata.LocationOfExecutablesDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultExecutablesPath); + loadMetadata.LocationOfCacheDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultCachePath); loadMetadata.SaveToDatabase(); var loadMetadataWithIdAfterwards = CatalogueRepository.GetObjectByID(loadMetadata.ID); - Assert.That(TestContext.CurrentContext.TestDirectory, Is.EqualTo(loadMetadataWithIdAfterwards.LocationOfFlatFiles)); + Assert.That(Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultForLoadingPath), Is.EqualTo(loadMetadataWithIdAfterwards.LocationOfForLoadingDirectory)); + Assert.That(Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultForArchivingPath), Is.EqualTo(loadMetadataWithIdAfterwards.LocationOfForArchivingDirectory)); + Assert.That(Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultExecutablesPath), Is.EqualTo(loadMetadataWithIdAfterwards.LocationOfExecutablesDirectory)); + Assert.That(Path.Combine(TestContext.CurrentContext.TestDirectory, loadMetadata.DefaultCachePath), Is.EqualTo(loadMetadataWithIdAfterwards.LocationOfCacheDirectory)); } finally { diff --git a/Rdmp.Core.Tests/Curation/Integration/LoadProgressUnitTests.cs b/Rdmp.Core.Tests/Curation/Integration/LoadProgressUnitTests.cs index 7d8764548d..7f1e9bdbbf 100644 --- a/Rdmp.Core.Tests/Curation/Integration/LoadProgressUnitTests.cs +++ b/Rdmp.Core.Tests/Curation/Integration/LoadProgressUnitTests.cs @@ -14,6 +14,7 @@ using Rdmp.Core.ReusableLibraryCode.Checks; using Rdmp.Core.ReusableLibraryCode.Progress; using Tests.Common; +using Rdmp.Core.Curation.Data.DataLoad; namespace Rdmp.Core.Tests.Curation.Integration; @@ -69,7 +70,10 @@ public void LoadProgress_JobFactory_NoDates() "LoadProgress_JobFactory_NoDates", true); var lmd = lp.LoadMetadata; - lmd.LocationOfFlatFiles = dir.RootPath.FullName; + lmd.LocationOfForLoadingDirectory = Path.Combine(dir.RootPath.FullName , ((LoadMetadata)lmd).DefaultForLoadingPath); + lmd.LocationOfForArchivingDirectory = Path.Combine(dir.RootPath.FullName , ((LoadMetadata)lmd).DefaultForArchivingPath); + lmd.LocationOfExecutablesDirectory = Path.Combine(dir.RootPath.FullName , ((LoadMetadata)lmd).DefaultExecutablesPath); + lmd.LocationOfCacheDirectory = Path.Combine(dir.RootPath.FullName , ((LoadMetadata)lmd).DefaultCachePath); foreach (var cata in lmd.GetAllCatalogues()) { diff --git a/Rdmp.Core.Tests/DataExport/DataExtraction/ExecuteFullExtractionToDatabaseMSSqlDestinationReExtractionTest.cs b/Rdmp.Core.Tests/DataExport/DataExtraction/ExecuteFullExtractionToDatabaseMSSqlDestinationReExtractionTest.cs new file mode 100644 index 0000000000..29bc80951b --- /dev/null +++ b/Rdmp.Core.Tests/DataExport/DataExtraction/ExecuteFullExtractionToDatabaseMSSqlDestinationReExtractionTest.cs @@ -0,0 +1,1324 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using SynthEHR; +using SynthEHR.Datasets; +using FAnsi.Discovery; +using NUnit.Framework; +using Rdmp.Core.CommandExecution; +using Rdmp.Core.CommandExecution.AtomicCommands.CatalogueCreationCommands; +using Rdmp.Core.CommandLine.Options; +using Rdmp.Core.CommandLine.Runners; +using Rdmp.Core.Curation.Data; +using Rdmp.Core.Curation.Data.Pipelines; +using Rdmp.Core.DataExport.Data; +using Rdmp.Core.DataExport.DataExtraction.Pipeline.Destinations; +using Rdmp.Core.DataExport.DataExtraction.Pipeline.Sources; +using Rdmp.Core.DataFlowPipeline; +using Rdmp.Core.ReusableLibraryCode.Checks; +using Rdmp.Core.ReusableLibraryCode.Progress; +using Tests.Common; +using Rdmp.Core.Curation.Data.Cohort; +using Rdmp.Core.Curation.Data.Aggregation; +using Rdmp.Core.CommandLine.DatabaseCreation; +using Rdmp.Core.CommandExecution.AtomicCommands.CohortCreationCommands; +using FAnsi; +using FAnsi.Discovery.QuerySyntax; +using TypeGuesser; +using Rdmp.Core.DataLoad.Triggers; + +namespace Rdmp.Core.Tests.DataExport.DataExtraction; + +public class ExecuteFullExtractionToDatabaseMSSqlDestinationReExtractionTest : DatabaseTests +{ + + private FileInfo CreateFileInForLoading(string filename, int rows, Random r) + { + var fi = new FileInfo(Path.Combine(Path.GetTempPath(), Path.GetFileName(filename))); + + var demog = new Demography(r); + var people = new PersonCollection(); + people.GeneratePeople(500, r); + + demog.GenerateTestDataFile(people, fi, rows); + + return fi; + } + + [Test] + public void ReExtractToADatabaseWithNoNewData() + { + var db = GetCleanedServer(FAnsi.DatabaseType.MicrosoftSQLServer); + + //create catalogue from file + var csvFile = CreateFileInForLoading("bob.csv", 1, new Random(5000)); + // Create the 'out of the box' RDMP pipelines (which includes an excel bulk importer pipeline) + var creator = new CataloguePipelinesAndReferencesCreation( + RepositoryLocator, UnitTestLoggingConnectionString, DataQualityEngineConnectionString); + + // find the excel loading pipeline + var pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + + if (pipe is null) + { + creator.CreatePipelines(new PlatformDatabaseCreationOptions { }); + pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + } + + // run an import of the file using the pipeline + var cmd = new ExecuteCommandCreateNewCatalogueByImportingFile( + new ThrowImmediatelyActivator(RepositoryLocator), + csvFile, + null, db, pipe, null); + + cmd.Execute(); + var catalogue = CatalogueRepository.GetAllObjects().Where(c => c.Name == "bob").FirstOrDefault(); + var chiColumnInfo = catalogue.CatalogueItems.Where(ci => ci.Name == "chi").First(); + var ei = chiColumnInfo.ExtractionInformation; + ei.IsExtractionIdentifier = true; + ei.SaveToDatabase(); + var project = new Project(DataExportRepository, "MyProject") + { + ProjectNumber = 500 + }; + project.ExtractionDirectory = Path.GetTempPath(); + project.SaveToDatabase(); + CohortIdentificationConfiguration cic = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic.CreateRootContainerIfNotExists(); + var agg1 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + var conf = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg1, 0); + cic.SaveToDatabase(); + var dim = new AggregateDimension(CatalogueRepository, ei, agg1); + dim.SaveToDatabase(); + agg1.SaveToDatabase(); + + string CohortDatabaseName = TestDatabaseNames.GetConsistentName("CohortDatabase"); + string cohortTableName = "Cohort"; + string definitionTableName = "CohortDefinition"; + string ExternalCohortTableNameInCatalogue = "CohortTests"; + const string ReleaseIdentifierFieldName = "ReleaseId"; + const string DefinitionTableForeignKeyField = "cohortDefinition_id"; + DiscoveredDatabase _cohortDatabase = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(CohortDatabaseName); + if (_cohortDatabase.Exists()) + DeleteTables(_cohortDatabase); + else + _cohortDatabase.Create(); + + var definitionTable = _cohortDatabase.CreateTable("CohortDefinition", new[] + { + new DatabaseColumnRequest("id", new DatabaseTypeRequest(typeof(int))) + { AllowNulls = false, IsAutoIncrement = true, IsPrimaryKey = true }, + new DatabaseColumnRequest("projectNumber", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("version", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("description", new DatabaseTypeRequest(typeof(string), 3000)) + { AllowNulls = false }, + new DatabaseColumnRequest("dtCreated", new DatabaseTypeRequest(typeof(DateTime))) + { AllowNulls = false, Default = MandatoryScalarFunctions.GetTodaysDate } + }); + var idColumn = definitionTable.DiscoverColumn("id"); + var foreignKey = + new DatabaseColumnRequest(DefinitionTableForeignKeyField, new DatabaseTypeRequest(typeof(int)), false) + { IsPrimaryKey = true }; + + _cohortDatabase.CreateTable("Cohort", new[] + { + new DatabaseColumnRequest("chi", + new DatabaseTypeRequest(typeof(string)), false) + { + IsPrimaryKey = true, + + // if there is a single collation amongst private identifier prototype references we must use that collation + // when creating the private column so that the DBMS can link them no bother + Collation = null + }, + new DatabaseColumnRequest(ReleaseIdentifierFieldName, new DatabaseTypeRequest(typeof(string), 300)) + { AllowNulls = true }, + foreignKey + }); + + var newExternal = + new ExternalCohortTable(DataExportRepository, "TestExternalCohort", DatabaseType.MicrosoftSQLServer) + { + Database = CohortDatabaseName, + Server = _cohortDatabase.Server.Name, + DefinitionTableName = definitionTableName, + TableName = cohortTableName, + Name = ExternalCohortTableNameInCatalogue, + Username = _cohortDatabase.Server.ExplicitUsernameIfAny, + Password = _cohortDatabase.Server.ExplicitPasswordIfAny, + PrivateIdentifierField = "chi", + ReleaseIdentifierField = "ReleaseId", + DefinitionTableForeignKeyField = "cohortDefinition_id" + }; + + newExternal.SaveToDatabase(); + var cohortPipeline = CatalogueRepository.GetAllObjects().Where(p => p.Name == "CREATE COHORT:By Executing Cohort Identification Configuration").First(); + var newCohortCmd = new ExecuteCommandCreateNewCohortByExecutingACohortIdentificationConfiguration( + new ThrowImmediatelyActivator(RepositoryLocator), + cic, + newExternal, + "MyCohort", + project, + cohortPipeline + ); + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort = new ExtractableCohort(DataExportRepository, newExternal, 1); + + var ec = new ExtractionConfiguration(DataExportRepository, project) + { + Name = "ext1", + Cohort_ID = _extractableCohort.ID + }; + ec.AddDatasetToConfiguration(new ExtractableDataSet(DataExportRepository, catalogue)); + + ec.SaveToDatabase(); + + var extractionPipeline = new Pipeline(CatalogueRepository, "Empty extraction pipeline 1"); + var component = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteFullExtractionToDatabaseMSSql), 0, "MS SQL Destination"); + var destinationArguments = component.CreateArgumentsForClassIfNotExists() + .ToList(); + var argumentServer = destinationArguments.Single(a => a.Name == "TargetDatabaseServer"); + var argumentDbNamePattern = destinationArguments.Single(a => a.Name == "DatabaseNamingPattern"); + var argumentTblNamePattern = destinationArguments.Single(a => a.Name == "TableNamingPattern"); + var reExtract = destinationArguments.Single(a => a.Name == "AppendDataIfTableExists"); + Assert.That(argumentServer.Name, Is.EqualTo("TargetDatabaseServer")); + ExternalDatabaseServer _extractionServer = new ExternalDatabaseServer(CatalogueRepository, "myserver", null) + { + Server = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.Name, + Username = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitUsernameIfAny, + Password = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitPasswordIfAny + }; + _extractionServer.SaveToDatabase(); + + argumentServer.SetValue(_extractionServer); + argumentServer.SaveToDatabase(); + argumentDbNamePattern.SetValue($"{TestDatabaseNames.Prefix}$p_$n"); + argumentDbNamePattern.SaveToDatabase(); + argumentTblNamePattern.SetValue("$c_$d"); + argumentTblNamePattern.SaveToDatabase(); + reExtract.SetValue(true); + reExtract.SaveToDatabase(); + + var component2 = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteCrossServerDatasetExtractionSource), -1, "Source"); + var arguments2 = component2.CreateArgumentsForClassIfNotExists() + .ToArray(); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SetValue(false); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SaveToDatabase(); + + //configure the component as the destination + extractionPipeline.DestinationPipelineComponent_ID = component.ID; + extractionPipeline.SourcePipelineComponent_ID = component2.ID; + extractionPipeline.SaveToDatabase(); + + + var dbname = TestDatabaseNames.GetConsistentName($"{project.Name}_{project.ProjectNumber}"); + var dbToExtractTo = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(dbname); + if (dbToExtractTo.Exists()) + dbToExtractTo.Drop(); + dbToExtractTo.Create(); + var runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + var returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + + + var destinationTable = dbToExtractTo.ExpectTable("ext1_bob"); + Assert.That(destinationTable.Exists()); + + var dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(1)); + + runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + Assert.That(destinationTable.Exists()); + + dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(1)); + + + } + + [Test] + public void ReExtractToADatabaseWithNewDataAndNoPKs() + { + var db = GetCleanedServer(FAnsi.DatabaseType.MicrosoftSQLServer); + + //create catalogue from file + var csvFile = CreateFileInForLoading("bob.csv", 1, new Random(5000)); + // Create the 'out of the box' RDMP pipelines (which includes an excel bulk importer pipeline) + var creator = new CataloguePipelinesAndReferencesCreation( + RepositoryLocator, UnitTestLoggingConnectionString, DataQualityEngineConnectionString); + + // find the excel loading pipeline + var pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + if (pipe is null) + { + creator.CreatePipelines(new PlatformDatabaseCreationOptions { }); + pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + } + // run an import of the file using the pipeline + var cmd = new ExecuteCommandCreateNewCatalogueByImportingFile( + new ThrowImmediatelyActivator(RepositoryLocator), + csvFile, + null, db, pipe, null); + + cmd.Execute(); + var catalogue = CatalogueRepository.GetAllObjects().Where(c => c.Name == "bob").FirstOrDefault(); + var chiColumnInfo = catalogue.CatalogueItems.Where(ci => ci.Name == "chi").First(); + var ei = chiColumnInfo.ExtractionInformation; + ei.IsExtractionIdentifier = true; + ei.SaveToDatabase(); + var project = new Project(DataExportRepository, "MyProject") + { + ProjectNumber = 500 + }; + project.ExtractionDirectory = Path.GetTempPath(); + project.SaveToDatabase(); + CohortIdentificationConfiguration cic = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic.CreateRootContainerIfNotExists(); + var agg1 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + var conf = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg1, 0); + cic.SaveToDatabase(); + var dim = new AggregateDimension(CatalogueRepository, ei, agg1); + dim.SaveToDatabase(); + agg1.SaveToDatabase(); + + string CohortDatabaseName = TestDatabaseNames.GetConsistentName("CohortDatabase"); + string cohortTableName = "Cohort"; + string definitionTableName = "CohortDefinition"; + string ExternalCohortTableNameInCatalogue = "CohortTests"; + const string ReleaseIdentifierFieldName = "ReleaseId"; + const string DefinitionTableForeignKeyField = "cohortDefinition_id"; + DiscoveredDatabase _cohortDatabase = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(CohortDatabaseName); + if (_cohortDatabase.Exists()) + DeleteTables(_cohortDatabase); + else + _cohortDatabase.Create(); + + var definitionTable = _cohortDatabase.CreateTable("CohortDefinition", new[] + { + new DatabaseColumnRequest("id", new DatabaseTypeRequest(typeof(int))) + { AllowNulls = false, IsAutoIncrement = true, IsPrimaryKey = true }, + new DatabaseColumnRequest("projectNumber", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("version", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("description", new DatabaseTypeRequest(typeof(string), 3000)) + { AllowNulls = false }, + new DatabaseColumnRequest("dtCreated", new DatabaseTypeRequest(typeof(DateTime))) + { AllowNulls = false, Default = MandatoryScalarFunctions.GetTodaysDate } + }); + var foreignKey = + new DatabaseColumnRequest(DefinitionTableForeignKeyField, new DatabaseTypeRequest(typeof(int)), false) + { IsPrimaryKey = true }; + + _cohortDatabase.CreateTable("Cohort", new[] + { + new DatabaseColumnRequest("chi", + new DatabaseTypeRequest(typeof(string)), false) + { + IsPrimaryKey = true, + + // if there is a single collation amongst private identifier prototype references we must use that collation + // when creating the private column so that the DBMS can link them no bother + Collation = null + }, + new DatabaseColumnRequest(ReleaseIdentifierFieldName, new DatabaseTypeRequest(typeof(string), 300)) + { AllowNulls = true }, + foreignKey + }); + + var newExternal = + new ExternalCohortTable(DataExportRepository, "TestExternalCohort", DatabaseType.MicrosoftSQLServer) + { + Database = CohortDatabaseName, + Server = _cohortDatabase.Server.Name, + DefinitionTableName = definitionTableName, + TableName = cohortTableName, + Name = ExternalCohortTableNameInCatalogue, + Username = _cohortDatabase.Server.ExplicitUsernameIfAny, + Password = _cohortDatabase.Server.ExplicitPasswordIfAny, + PrivateIdentifierField = "chi", + ReleaseIdentifierField = "ReleaseId", + DefinitionTableForeignKeyField = "cohortDefinition_id" + }; + + newExternal.SaveToDatabase(); + var cohortPipeline = CatalogueRepository.GetAllObjects().Where(p => p.Name == "CREATE COHORT:By Executing Cohort Identification Configuration").First(); + var newCohortCmd = new ExecuteCommandCreateNewCohortByExecutingACohortIdentificationConfiguration( + new ThrowImmediatelyActivator(RepositoryLocator), + cic, + newExternal, + "MyCohort", + project, + cohortPipeline + ); + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort = new ExtractableCohort(DataExportRepository, newExternal, 1); + + var ec = new ExtractionConfiguration(DataExportRepository, project) + { + Name = "ext1", + Cohort_ID = _extractableCohort.ID + }; + ec.AddDatasetToConfiguration(new ExtractableDataSet(DataExportRepository, catalogue)); + + ec.SaveToDatabase(); + + var extractionPipeline = new Pipeline(CatalogueRepository, "Empty extraction pipeline 2"); + var component = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteFullExtractionToDatabaseMSSql), 0, "MS SQL Destination"); + var destinationArguments = component.CreateArgumentsForClassIfNotExists() + .ToList(); + var argumentServer = destinationArguments.Single(a => a.Name == "TargetDatabaseServer"); + var argumentDbNamePattern = destinationArguments.Single(a => a.Name == "DatabaseNamingPattern"); + var argumentTblNamePattern = destinationArguments.Single(a => a.Name == "TableNamingPattern"); + var reExtract = destinationArguments.Single(a => a.Name == "AppendDataIfTableExists"); + Assert.That(argumentServer.Name, Is.EqualTo("TargetDatabaseServer")); + ExternalDatabaseServer _extractionServer = new ExternalDatabaseServer(CatalogueRepository, "myserver", null) + { + Server = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.Name, + Username = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitUsernameIfAny, + Password = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitPasswordIfAny + }; + _extractionServer.SaveToDatabase(); + + argumentServer.SetValue(_extractionServer); + argumentServer.SaveToDatabase(); + argumentDbNamePattern.SetValue($"{TestDatabaseNames.Prefix}$p_$n"); + argumentDbNamePattern.SaveToDatabase(); + argumentTblNamePattern.SetValue("$c_$d"); + argumentTblNamePattern.SaveToDatabase(); + reExtract.SetValue(true); + reExtract.SaveToDatabase(); + + var component2 = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteCrossServerDatasetExtractionSource), -1, "Source"); + var arguments2 = component2.CreateArgumentsForClassIfNotExists() + .ToArray(); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SetValue(false); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SaveToDatabase(); + + //configure the component as the destination + extractionPipeline.DestinationPipelineComponent_ID = component.ID; + extractionPipeline.SourcePipelineComponent_ID = component2.ID; + extractionPipeline.SaveToDatabase(); + + + var dbname = TestDatabaseNames.GetConsistentName($"{project.Name}_{project.ProjectNumber}"); + var dbToExtractTo = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(dbname); + if (dbToExtractTo.Exists()) + dbToExtractTo.Drop(); + + var runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + var returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + + + var destinationTable = dbToExtractTo.ExpectTable("ext1_bob"); + Assert.That(destinationTable.Exists()); + + var dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(1)); + + //add new entry here + var tbl = db.DiscoverTables(false).First(); + tbl.Insert(new Dictionary + { + { "chi","1111111111"}, + {"notes","T"}, + {"dtCreated", new DateTime(2001, 1, 2) }, + {"century",19}, + {"surname","1234"}, + {"forename","yes"}, + {"sex","M"}, + }); + + CohortIdentificationConfiguration cic2 = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic2.CreateRootContainerIfNotExists(); + var agg12 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + _ = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg12, 0); + cic.SaveToDatabase(); + var dim2 = new AggregateDimension(CatalogueRepository, ei, agg12); + dim2.SaveToDatabase(); + agg12.SaveToDatabase(); + + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort2 = new ExtractableCohort(DataExportRepository, newExternal, 2); + ec.Cohort_ID = _extractableCohort2.ID; + ec.SaveToDatabase(); + + runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + Assert.That(destinationTable.Exists()); + + dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(2)); + } + + [Test] + public void ReExtractToADatabaseWithNewDataAndSinglePK() + { + var db = GetCleanedServer(FAnsi.DatabaseType.MicrosoftSQLServer); + + //create catalogue from file + var csvFile = CreateFileInForLoading("bob.csv", 1, new Random(5000)); + // Create the 'out of the box' RDMP pipelines (which includes an excel bulk importer pipeline) + var creator = new CataloguePipelinesAndReferencesCreation( + RepositoryLocator, UnitTestLoggingConnectionString, DataQualityEngineConnectionString); + + + // find the excel loading pipeline + var pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + + if (pipe is null) + { + creator.CreatePipelines(new PlatformDatabaseCreationOptions { }); + pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + } + + // run an import of the file using the pipeline + var cmd = new ExecuteCommandCreateNewCatalogueByImportingFile( + new ThrowImmediatelyActivator(RepositoryLocator), + csvFile, + null, db, pipe, null); + + cmd.Execute(); + var catalogue = CatalogueRepository.GetAllObjects().Where(c => c.Name == "bob").FirstOrDefault(); + var chiColumnInfo = catalogue.CatalogueItems.Where(ci => ci.Name == "chi").First(); + var ei = chiColumnInfo.ExtractionInformation; + ei.IsExtractionIdentifier = true; + ei.SaveToDatabase(); + + var surnameInfo = catalogue.CatalogueItems.Where(ci => ci.Name == "surname").First(); + surnameInfo.ExtractionInformation.IsPrimaryKey = true; + surnameInfo.ExtractionInformation.SaveToDatabase(); + + var project = new Project(DataExportRepository, "MyProject") + { + ProjectNumber = 500 + }; + project.ExtractionDirectory = Path.GetTempPath(); + project.SaveToDatabase(); + CohortIdentificationConfiguration cic = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic.CreateRootContainerIfNotExists(); + var agg1 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + var conf = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg1, 0); + cic.SaveToDatabase(); + var dim = new AggregateDimension(CatalogueRepository, ei, agg1); + dim.SaveToDatabase(); + agg1.SaveToDatabase(); + + string CohortDatabaseName = TestDatabaseNames.GetConsistentName("CohortDatabase"); + string cohortTableName = "Cohort"; + string definitionTableName = "CohortDefinition"; + string ExternalCohortTableNameInCatalogue = "CohortTests"; + const string ReleaseIdentifierFieldName = "ReleaseId"; + const string DefinitionTableForeignKeyField = "cohortDefinition_id"; + DiscoveredDatabase _cohortDatabase = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(CohortDatabaseName); + if (_cohortDatabase.Exists()) + DeleteTables(_cohortDatabase); + else + _cohortDatabase.Create(); + + var definitionTable = _cohortDatabase.CreateTable("CohortDefinition", new[] + { + new DatabaseColumnRequest("id", new DatabaseTypeRequest(typeof(int))) + { AllowNulls = false, IsAutoIncrement = true, IsPrimaryKey = true }, + new DatabaseColumnRequest("projectNumber", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("version", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("description", new DatabaseTypeRequest(typeof(string), 3000)) + { AllowNulls = false }, + new DatabaseColumnRequest("dtCreated", new DatabaseTypeRequest(typeof(DateTime))) + { AllowNulls = false, Default = MandatoryScalarFunctions.GetTodaysDate } + }); + var foreignKey = + new DatabaseColumnRequest(DefinitionTableForeignKeyField, new DatabaseTypeRequest(typeof(int)), false) + { IsPrimaryKey = true }; + + _cohortDatabase.CreateTable("Cohort", new[] + { + new DatabaseColumnRequest("chi", + new DatabaseTypeRequest(typeof(string)), false) + { + IsPrimaryKey = true, + + // if there is a single collation amongst private identifier prototype references we must use that collation + // when creating the private column so that the DBMS can link them no bother + Collation = null + }, + new DatabaseColumnRequest(ReleaseIdentifierFieldName, new DatabaseTypeRequest(typeof(string), 300)) + { AllowNulls = true }, + foreignKey + }); + + var newExternal = + new ExternalCohortTable(DataExportRepository, "TestExternalCohort", DatabaseType.MicrosoftSQLServer) + { + Database = CohortDatabaseName, + Server = _cohortDatabase.Server.Name, + DefinitionTableName = definitionTableName, + TableName = cohortTableName, + Name = ExternalCohortTableNameInCatalogue, + Username = _cohortDatabase.Server.ExplicitUsernameIfAny, + Password = _cohortDatabase.Server.ExplicitPasswordIfAny, + PrivateIdentifierField = "chi", + ReleaseIdentifierField = "ReleaseId", + DefinitionTableForeignKeyField = "cohortDefinition_id" + }; + + newExternal.SaveToDatabase(); + var cohortPipeline = CatalogueRepository.GetAllObjects().Where(p => p.Name == "CREATE COHORT:By Executing Cohort Identification Configuration").First(); + var newCohortCmd = new ExecuteCommandCreateNewCohortByExecutingACohortIdentificationConfiguration( + new ThrowImmediatelyActivator(RepositoryLocator), + cic, + newExternal, + "MyCohort", + project, + cohortPipeline + ); + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort = new ExtractableCohort(DataExportRepository, newExternal, 1); + + var ec = new ExtractionConfiguration(DataExportRepository, project) + { + Name = "ext1", + Cohort_ID = _extractableCohort.ID + }; + ec.AddDatasetToConfiguration(new ExtractableDataSet(DataExportRepository, catalogue)); + + ec.SaveToDatabase(); + + var extractionPipeline = new Pipeline(CatalogueRepository, "Empty extraction pipeline"); + var component = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteFullExtractionToDatabaseMSSql), 0, "MS SQL Destination"); + var destinationArguments = component.CreateArgumentsForClassIfNotExists() + .ToList(); + var argumentServer = destinationArguments.Single(a => a.Name == "TargetDatabaseServer"); + var argumentDbNamePattern = destinationArguments.Single(a => a.Name == "DatabaseNamingPattern"); + var argumentTblNamePattern = destinationArguments.Single(a => a.Name == "TableNamingPattern"); + var reExtract = destinationArguments.Single(a => a.Name == "AppendDataIfTableExists"); + Assert.That(argumentServer.Name, Is.EqualTo("TargetDatabaseServer")); + ExternalDatabaseServer _extractionServer = new ExternalDatabaseServer(CatalogueRepository, "myserver", null) + { + Server = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.Name, + Username = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitUsernameIfAny, + Password = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitPasswordIfAny + }; + _extractionServer.SaveToDatabase(); + + argumentServer.SetValue(_extractionServer); + argumentServer.SaveToDatabase(); + argumentDbNamePattern.SetValue($"{TestDatabaseNames.Prefix}$p_$n"); + argumentDbNamePattern.SaveToDatabase(); + argumentTblNamePattern.SetValue("$c_$d"); + argumentTblNamePattern.SaveToDatabase(); + reExtract.SetValue(true); + reExtract.SaveToDatabase(); + + var component2 = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteCrossServerDatasetExtractionSource), -1, "Source"); + var arguments2 = component2.CreateArgumentsForClassIfNotExists() + .ToArray(); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SetValue(false); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SaveToDatabase(); + + //configure the component as the destination + extractionPipeline.DestinationPipelineComponent_ID = component.ID; + extractionPipeline.SourcePipelineComponent_ID = component2.ID; + extractionPipeline.SaveToDatabase(); + + + var dbname = TestDatabaseNames.GetConsistentName($"{project.Name}_{project.ProjectNumber}"); + var dbToExtractTo = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(dbname); + if (dbToExtractTo.Exists()) + dbToExtractTo.Drop(); + + var runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + var returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + + + var destinationTable = dbToExtractTo.ExpectTable("ext1_bob"); + Assert.That(destinationTable.Exists()); + + var dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(1)); + + //add new entry here + var tbl = db.DiscoverTables(false).First(); + tbl.Insert(new Dictionary + { + { "chi","1111111111"}, + {"notes","T"}, + {"dtCreated", new DateTime(2001, 1, 2) }, + {"century",19}, + {"surname","1234"}, + {"forename","yes"}, + {"sex","M"}, + }); + + CohortIdentificationConfiguration cic2 = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic2.CreateRootContainerIfNotExists(); + var agg12 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + _ = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic2.RootCohortAggregateContainer.AddChild(agg12, 0); + cic2.SaveToDatabase(); + var dim2 = new AggregateDimension(CatalogueRepository, ei, agg12); + dim2.SaveToDatabase(); + agg12.SaveToDatabase(); + newCohortCmd = new ExecuteCommandCreateNewCohortByExecutingACohortIdentificationConfiguration( + new ThrowImmediatelyActivator(RepositoryLocator), + cic2, + newExternal, + "MyCohort", + project, + cohortPipeline + ); + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort2 = new ExtractableCohort(DataExportRepository, newExternal, 2); + ec.Cohort_ID = _extractableCohort2.ID; + ec.SaveToDatabase(); + + runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + Assert.That(destinationTable.Exists()); + + dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(2)); + } + + [Test] + public void ReExtractToADatabaseWithNewDataAndExtractionIdentifierIsPK() + { + var db = GetCleanedServer(FAnsi.DatabaseType.MicrosoftSQLServer); + + //create catalogue from file + var csvFile = CreateFileInForLoading("bob.csv", 1, new Random(5000)); + // Create the 'out of the box' RDMP pipelines (which includes an excel bulk importer pipeline) + var creator = new CataloguePipelinesAndReferencesCreation( + RepositoryLocator, UnitTestLoggingConnectionString, DataQualityEngineConnectionString); + + + // find the excel loading pipeline + var pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + + if (pipe is null) + { + creator.CreatePipelines(new PlatformDatabaseCreationOptions { }); + pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + } + + // run an import of the file using the pipeline + var cmd = new ExecuteCommandCreateNewCatalogueByImportingFile( + new ThrowImmediatelyActivator(RepositoryLocator), + csvFile, + null, db, pipe, null); + + cmd.Execute(); + var catalogue = CatalogueRepository.GetAllObjects().Where(c => c.Name == "bob").FirstOrDefault(); + var chiColumnInfo = catalogue.CatalogueItems.Where(ci => ci.Name == "chi").First(); + var ei = chiColumnInfo.ExtractionInformation; + ei.IsPrimaryKey = true; + ei.IsExtractionIdentifier = true; + ei.SaveToDatabase(); + + var project = new Project(DataExportRepository, "MyProject") + { + ProjectNumber = 500 + }; + project.ExtractionDirectory = Path.GetTempPath(); + project.SaveToDatabase(); + CohortIdentificationConfiguration cic = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic.CreateRootContainerIfNotExists(); + var agg1 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + var conf = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg1, 0); + cic.SaveToDatabase(); + var dim = new AggregateDimension(CatalogueRepository, ei, agg1); + dim.SaveToDatabase(); + agg1.SaveToDatabase(); + + string CohortDatabaseName = TestDatabaseNames.GetConsistentName("CohortDatabase"); + string cohortTableName = "Cohort"; + string definitionTableName = "CohortDefinition"; + string ExternalCohortTableNameInCatalogue = "CohortTests"; + const string ReleaseIdentifierFieldName = "ReleaseId"; + const string DefinitionTableForeignKeyField = "cohortDefinition_id"; + DiscoveredDatabase _cohortDatabase = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(CohortDatabaseName); + if (_cohortDatabase.Exists()) + DeleteTables(_cohortDatabase); + else + _cohortDatabase.Create(); + + var definitionTable = _cohortDatabase.CreateTable("CohortDefinition", new[] + { + new DatabaseColumnRequest("id", new DatabaseTypeRequest(typeof(int))) + { AllowNulls = false, IsAutoIncrement = true, IsPrimaryKey = true }, + new DatabaseColumnRequest("projectNumber", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("version", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("description", new DatabaseTypeRequest(typeof(string), 3000)) + { AllowNulls = false }, + new DatabaseColumnRequest("dtCreated", new DatabaseTypeRequest(typeof(DateTime))) + { AllowNulls = false, Default = MandatoryScalarFunctions.GetTodaysDate } + }); + var idColumn = definitionTable.DiscoverColumn("id"); + var foreignKey = + new DatabaseColumnRequest(DefinitionTableForeignKeyField, new DatabaseTypeRequest(typeof(int)), false) + { IsPrimaryKey = true }; + + _cohortDatabase.CreateTable("Cohort", new[] + { + new DatabaseColumnRequest("chi", + new DatabaseTypeRequest(typeof(string)), false) + { + IsPrimaryKey = true, + + // if there is a single collation amongst private identifier prototype references we must use that collation + // when creating the private column so that the DBMS can link them no bother + Collation = null + }, + new DatabaseColumnRequest(ReleaseIdentifierFieldName, new DatabaseTypeRequest(typeof(string), 300)) + { AllowNulls = true }, + foreignKey + }); + + var newExternal = + new ExternalCohortTable(DataExportRepository, "TestExternalCohort", DatabaseType.MicrosoftSQLServer) + { + Database = CohortDatabaseName, + Server = _cohortDatabase.Server.Name, + DefinitionTableName = definitionTableName, + TableName = cohortTableName, + Name = ExternalCohortTableNameInCatalogue, + Username = _cohortDatabase.Server.ExplicitUsernameIfAny, + Password = _cohortDatabase.Server.ExplicitPasswordIfAny, + PrivateIdentifierField = "chi", + ReleaseIdentifierField = "ReleaseId", + DefinitionTableForeignKeyField = "cohortDefinition_id" + }; + + newExternal.SaveToDatabase(); + var cohortPipeline = CatalogueRepository.GetAllObjects().Where(p => p.Name == "CREATE COHORT:By Executing Cohort Identification Configuration").First(); + var newCohortCmd = new ExecuteCommandCreateNewCohortByExecutingACohortIdentificationConfiguration( + new ThrowImmediatelyActivator(RepositoryLocator), + cic, + newExternal, + "MyCohort", + project, + cohortPipeline + ); + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort = new ExtractableCohort(DataExportRepository, newExternal, 1); + + var ec = new ExtractionConfiguration(DataExportRepository, project) + { + Name = "ext1", + Cohort_ID = _extractableCohort.ID + }; + ec.AddDatasetToConfiguration(new ExtractableDataSet(DataExportRepository, catalogue)); + + ec.SaveToDatabase(); + + var extractionPipeline = new Pipeline(CatalogueRepository, "Empty extraction pipeline 3"); + var component = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteFullExtractionToDatabaseMSSql), 0, "MS SQL Destination"); + var destinationArguments = component.CreateArgumentsForClassIfNotExists() + .ToList(); + var argumentServer = destinationArguments.Single(a => a.Name == "TargetDatabaseServer"); + var argumentDbNamePattern = destinationArguments.Single(a => a.Name == "DatabaseNamingPattern"); + var argumentTblNamePattern = destinationArguments.Single(a => a.Name == "TableNamingPattern"); + var reExtract = destinationArguments.Single(a => a.Name == "AppendDataIfTableExists"); + Assert.That(argumentServer.Name, Is.EqualTo("TargetDatabaseServer")); + ExternalDatabaseServer _extractionServer = new ExternalDatabaseServer(CatalogueRepository, "myserver", null) + { + Server = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.Name, + Username = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitUsernameIfAny, + Password = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitPasswordIfAny + }; + _extractionServer.SaveToDatabase(); + + argumentServer.SetValue(_extractionServer); + argumentServer.SaveToDatabase(); + argumentDbNamePattern.SetValue($"{TestDatabaseNames.Prefix}$p_$n"); + argumentDbNamePattern.SaveToDatabase(); + argumentTblNamePattern.SetValue("$c_$d"); + argumentTblNamePattern.SaveToDatabase(); + reExtract.SetValue(true); + reExtract.SaveToDatabase(); + + var component2 = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteCrossServerDatasetExtractionSource), -1, "Source"); + var arguments2 = component2.CreateArgumentsForClassIfNotExists() + .ToArray(); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SetValue(false); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SaveToDatabase(); + + //configure the component as the destination + extractionPipeline.DestinationPipelineComponent_ID = component.ID; + extractionPipeline.SourcePipelineComponent_ID = component2.ID; + extractionPipeline.SaveToDatabase(); + + + var dbname = TestDatabaseNames.GetConsistentName($"{project.Name}_{project.ProjectNumber}"); + var dbToExtractTo = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(dbname); + if (dbToExtractTo.Exists()) + dbToExtractTo.Drop(); + + var runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + var returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + + + var destinationTable = dbToExtractTo.ExpectTable("ext1_bob"); + Assert.That(destinationTable.Exists()); + + var dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(1)); + + //add new entry here + var tbl = db.DiscoverTables(false).First(); + tbl.Insert(new Dictionary + { + { "chi","1111111111"}, + {"notes","T"}, + {"dtCreated", new DateTime(2001, 1, 2) }, + {"century",19}, + {"surname","1234"}, + {"forename","yes"}, + {"sex","M"}, + }); + + CohortIdentificationConfiguration cic2 = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic2.CreateRootContainerIfNotExists(); + var agg12 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + _ = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg12, 0); + cic.SaveToDatabase(); + var dim2 = new AggregateDimension(CatalogueRepository, ei, agg12); + dim2.SaveToDatabase(); + agg12.SaveToDatabase(); + + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort2 = new ExtractableCohort(DataExportRepository, newExternal, 2); + ec.Cohort_ID = _extractableCohort2.ID; + ec.SaveToDatabase(); + + runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + Assert.That(destinationTable.Exists()); + + dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(2)); + } + + [Test] + public void ExtractToDatabaseUseTriggers() + { + var db = GetCleanedServer(FAnsi.DatabaseType.MicrosoftSQLServer); + + //create catalogue from file + var csvFile = CreateFileInForLoading("bob.csv", 1, new Random(5000)); + // Create the 'out of the box' RDMP pipelines (which includes an excel bulk importer pipeline) + var creator = new CataloguePipelinesAndReferencesCreation( + RepositoryLocator, UnitTestLoggingConnectionString, DataQualityEngineConnectionString); + + + // find the excel loading pipeline + var pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + + if (pipe is null) + { + creator.CreatePipelines(new PlatformDatabaseCreationOptions { }); + pipe = CatalogueRepository.GetAllObjects().OrderByDescending(p => p.ID) + .FirstOrDefault(p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + } + + // run an import of the file using the pipeline + var cmd = new ExecuteCommandCreateNewCatalogueByImportingFile( + new ThrowImmediatelyActivator(RepositoryLocator), + csvFile, + null, db, pipe, null); + + cmd.Execute(); + var catalogue = CatalogueRepository.GetAllObjects().Where(c => c.Name == "bob").FirstOrDefault(); + var chiColumnInfo = catalogue.CatalogueItems.Where(ci => ci.Name == "chi").First(); + var ei = chiColumnInfo.ExtractionInformation; + ei.IsPrimaryKey = true; + ei.IsExtractionIdentifier = true; + ei.SaveToDatabase(); + + var project = new Project(DataExportRepository, "MyProject") + { + ProjectNumber = 500 + }; + project.ExtractionDirectory = Path.GetTempPath(); + project.SaveToDatabase(); + CohortIdentificationConfiguration cic = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic.CreateRootContainerIfNotExists(); + var agg1 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + var conf = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg1, 0); + cic.SaveToDatabase(); + var dim = new AggregateDimension(CatalogueRepository, ei, agg1); + dim.SaveToDatabase(); + agg1.SaveToDatabase(); + + string CohortDatabaseName = TestDatabaseNames.GetConsistentName("CohortDatabase"); + string cohortTableName = "Cohort"; + string definitionTableName = "CohortDefinition"; + string ExternalCohortTableNameInCatalogue = "CohortTests"; + const string ReleaseIdentifierFieldName = "ReleaseId"; + const string DefinitionTableForeignKeyField = "cohortDefinition_id"; + DiscoveredDatabase _cohortDatabase = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(CohortDatabaseName); + if (_cohortDatabase.Exists()) + DeleteTables(_cohortDatabase); + else + _cohortDatabase.Create(); + + var definitionTable = _cohortDatabase.CreateTable("CohortDefinition", new[] + { + new DatabaseColumnRequest("id", new DatabaseTypeRequest(typeof(int))) + { AllowNulls = false, IsAutoIncrement = true, IsPrimaryKey = true }, + new DatabaseColumnRequest("projectNumber", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("version", new DatabaseTypeRequest(typeof(int))) { AllowNulls = false }, + new DatabaseColumnRequest("description", new DatabaseTypeRequest(typeof(string), 3000)) + { AllowNulls = false }, + new DatabaseColumnRequest("dtCreated", new DatabaseTypeRequest(typeof(DateTime))) + { AllowNulls = false, Default = MandatoryScalarFunctions.GetTodaysDate } + }); + var idColumn = definitionTable.DiscoverColumn("id"); + var foreignKey = + new DatabaseColumnRequest(DefinitionTableForeignKeyField, new DatabaseTypeRequest(typeof(int)), false) + { IsPrimaryKey = true }; + + _cohortDatabase.CreateTable("Cohort", new[] + { + new DatabaseColumnRequest("chi", + new DatabaseTypeRequest(typeof(string)), false) + { + IsPrimaryKey = true, + + // if there is a single collation amongst private identifier prototype references we must use that collation + // when creating the private column so that the DBMS can link them no bother + Collation = null + }, + new DatabaseColumnRequest(ReleaseIdentifierFieldName, new DatabaseTypeRequest(typeof(string), 300)) + { AllowNulls = true }, + foreignKey + }); + + var newExternal = + new ExternalCohortTable(DataExportRepository, "TestExternalCohort", DatabaseType.MicrosoftSQLServer) + { + Database = CohortDatabaseName, + Server = _cohortDatabase.Server.Name, + DefinitionTableName = definitionTableName, + TableName = cohortTableName, + Name = ExternalCohortTableNameInCatalogue, + Username = _cohortDatabase.Server.ExplicitUsernameIfAny, + Password = _cohortDatabase.Server.ExplicitPasswordIfAny, + PrivateIdentifierField = "chi", + ReleaseIdentifierField = "ReleaseId", + DefinitionTableForeignKeyField = "cohortDefinition_id" + }; + + newExternal.SaveToDatabase(); + var cohortPipeline = CatalogueRepository.GetAllObjects().Where(p => p.Name == "CREATE COHORT:By Executing Cohort Identification Configuration").First(); + var newCohortCmd = new ExecuteCommandCreateNewCohortByExecutingACohortIdentificationConfiguration( + new ThrowImmediatelyActivator(RepositoryLocator), + cic, + newExternal, + "MyCohort", + project, + cohortPipeline + ); + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort = new ExtractableCohort(DataExportRepository, newExternal, 1); + + var ec = new ExtractionConfiguration(DataExportRepository, project) + { + Name = "ext1", + Cohort_ID = _extractableCohort.ID + }; + ec.AddDatasetToConfiguration(new ExtractableDataSet(DataExportRepository, catalogue)); + + ec.SaveToDatabase(); + + var extractionPipeline = new Pipeline(CatalogueRepository, "Empty extraction pipeline 4"); + var component = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteFullExtractionToDatabaseMSSql), 0, "MS SQL Destination"); + var destinationArguments = component.CreateArgumentsForClassIfNotExists() + .ToList(); + var argumentServer = destinationArguments.Single(a => a.Name == "TargetDatabaseServer"); + var argumentDbNamePattern = destinationArguments.Single(a => a.Name == "DatabaseNamingPattern"); + var argumentTblNamePattern = destinationArguments.Single(a => a.Name == "TableNamingPattern"); + var reExtract = destinationArguments.Single(a => a.Name == "AppendDataIfTableExists"); + Assert.That(argumentServer.Name, Is.EqualTo("TargetDatabaseServer")); + ExternalDatabaseServer _extractionServer = new ExternalDatabaseServer(CatalogueRepository, "myserver", null) + { + Server = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.Name, + Username = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitUsernameIfAny, + Password = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExplicitPasswordIfAny + }; + _extractionServer.SaveToDatabase(); + + argumentServer.SetValue(_extractionServer); + argumentServer.SaveToDatabase(); + argumentDbNamePattern.SetValue($"{TestDatabaseNames.Prefix}$p_$n"); + argumentDbNamePattern.SaveToDatabase(); + argumentTblNamePattern.SetValue("$c_$d"); + argumentTblNamePattern.SaveToDatabase(); + reExtract.SetValue(true); + reExtract.SaveToDatabase(); + + var component2 = new PipelineComponent(CatalogueRepository, extractionPipeline, + typeof(ExecuteCrossServerDatasetExtractionSource), -1, "Source"); + var arguments2 = component2.CreateArgumentsForClassIfNotExists() + .ToArray(); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SetValue(false); + arguments2.Single(a => a.Name.Equals("AllowEmptyExtractions")).SaveToDatabase(); + + //configure the component as the destination + extractionPipeline.DestinationPipelineComponent_ID = component.ID; + extractionPipeline.SourcePipelineComponent_ID = component2.ID; + extractionPipeline.SaveToDatabase(); + + + var dbname = TestDatabaseNames.GetConsistentName($"{project.Name}_{project.ProjectNumber}"); + var dbToExtractTo = DiscoveredServerICanCreateRandomDatabasesAndTablesOn.ExpectDatabase(dbname); + if (dbToExtractTo.Exists()) + dbToExtractTo.Drop(); + + var runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + var returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + + + var destinationTable = dbToExtractTo.ExpectTable("ext1_bob"); + Assert.That(destinationTable.Exists()); + + var dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(1)); + var columns = dt.Columns.Cast().Select(c => c.ColumnName).ToList(); + Assert.That(columns, Does.Contain(SpecialFieldNames.DataLoadRunID)); + Assert.That(columns, Does.Contain(SpecialFieldNames.ValidFrom)); + ////add new entry here + var tbl = db.DiscoverTables(false).First(); + tbl.Insert(new Dictionary + { + { "chi","1111111111"}, + {"notes","T"}, + {"dtCreated", new DateTime(2001, 1, 2) }, + {"century",19}, + {"surname","1234"}, + {"forename","yes"}, + {"sex","M"}, + }); + + CohortIdentificationConfiguration cic2 = new CohortIdentificationConfiguration(CatalogueRepository, "Cohort1"); + cic2.CreateRootContainerIfNotExists(); + var agg12 = new AggregateConfiguration(CatalogueRepository, catalogue, "agg1"); + _ = new AggregateConfiguration(CatalogueRepository, catalogue, "UnitTestShortcutAggregate"); + conf.SaveToDatabase(); + agg1.SaveToDatabase(); + cic.RootCohortAggregateContainer.AddChild(agg12, 0); + cic.SaveToDatabase(); + var dim2 = new AggregateDimension(CatalogueRepository, ei, agg12); + dim2.SaveToDatabase(); + agg12.SaveToDatabase(); + + newCohortCmd.Execute(); + ExtractableCohort _extractableCohort2 = new ExtractableCohort(DataExportRepository, newExternal, 2); + ec.Cohort_ID = _extractableCohort2.ID; + ec.SaveToDatabase(); + + runner = new ExtractionRunner(new ThrowImmediatelyActivator(RepositoryLocator), new ExtractionOptions + { + Command = CommandLineActivity.run, + ExtractionConfiguration = ec.ID.ToString(), + ExtractGlobals = true, + Pipeline = extractionPipeline.ID.ToString() + }); + + returnCode = runner.Run( + RepositoryLocator, + ThrowImmediatelyDataLoadEventListener.Quiet, + ThrowImmediatelyCheckNotifier.Quiet, + new GracefulCancellationToken()); + + Assert.That(returnCode, Is.EqualTo(0), "Return code from runner was non zero"); + + Assert.That(destinationTable.Exists()); + + dt = destinationTable.GetDataTable(); + + Assert.That(dt.Rows, Has.Count.EqualTo(2)); + } +} \ No newline at end of file diff --git a/Rdmp.Core.Tests/DataExport/DataExtraction/ExecuteFullExtractionToDatabaseMSSqlDestinationTest.cs b/Rdmp.Core.Tests/DataExport/DataExtraction/ExecuteFullExtractionToDatabaseMSSqlDestinationTest.cs index 31c934055f..6ba4b87db2 100644 --- a/Rdmp.Core.Tests/DataExport/DataExtraction/ExecuteFullExtractionToDatabaseMSSqlDestinationTest.cs +++ b/Rdmp.Core.Tests/DataExport/DataExtraction/ExecuteFullExtractionToDatabaseMSSqlDestinationTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. diff --git a/Rdmp.Core.Tests/DataLoad/Engine/Integration/CheckingTests/ProcessTaskCheckingTests.cs b/Rdmp.Core.Tests/DataLoad/Engine/Integration/CheckingTests/ProcessTaskCheckingTests.cs index c9ea51ff48..35bbe414d0 100644 --- a/Rdmp.Core.Tests/DataLoad/Engine/Integration/CheckingTests/ProcessTaskCheckingTests.cs +++ b/Rdmp.Core.Tests/DataLoad/Engine/Integration/CheckingTests/ProcessTaskCheckingTests.cs @@ -36,7 +36,10 @@ public void CreateTask() _dir.Create(); var hicdir = LoadDirectory.CreateDirectoryStructure(_dir, "ProjDir", true); - _lmd.LocationOfFlatFiles = hicdir.RootPath.FullName; + _lmd.LocationOfForLoadingDirectory = Path.Combine(hicdir.RootPath.FullName, _lmd.DefaultForLoadingPath); + _lmd.LocationOfForArchivingDirectory = Path.Combine(hicdir.RootPath.FullName, _lmd.DefaultForArchivingPath); + _lmd.LocationOfExecutablesDirectory = Path.Combine(hicdir.RootPath.FullName, _lmd.DefaultExecutablesPath); + _lmd.LocationOfCacheDirectory = Path.Combine(hicdir.RootPath.FullName, _lmd.DefaultCachePath); _lmd.SaveToDatabase(); var c = new Catalogue(CatalogueRepository, "c"); @@ -104,7 +107,10 @@ public void MEFIncompatibleType() [Test] public void MEFCompatibleType_NoProjectDirectory() { - _lmd.LocationOfFlatFiles = null; + _lmd.LocationOfForLoadingDirectory = null; + _lmd.LocationOfForArchivingDirectory = null; + _lmd.LocationOfExecutablesDirectory = null; + _lmd.LocationOfCacheDirectory = null; _lmd.SaveToDatabase(); _task.ProcessTaskType = ProcessTaskType.Attacher; @@ -114,7 +120,7 @@ public void MEFCompatibleType_NoProjectDirectory() _task.CreateArgumentsForClassIfNotExists(); var ex = Assert.Throws(() => _checker.Check(ThrowImmediatelyCheckNotifier.QuietPicky)); - Assert.That(ex.InnerException.Message, Is.EqualTo($@"No Project Directory (LocationOfFlatFiles) has been configured on LoadMetadata {_lmd.Name}")); + Assert.That(ex.InnerException.Message, Is.EqualTo($@"No Project Directory has been configured on LoadMetadata {_lmd.Name}")); } [Test] @@ -125,7 +131,10 @@ public void MEFCompatibleType_NoArgs() "DelMeProjDir", true); try { - _lmd.LocationOfFlatFiles = projDir.RootPath.FullName; + _lmd.LocationOfForLoadingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForLoadingPath); + _lmd.LocationOfForArchivingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForArchivingPath); + _lmd.LocationOfExecutablesDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultExecutablesPath); + _lmd.LocationOfCacheDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultCachePath); _task.ProcessTaskType = ProcessTaskType.Attacher; _task.LoadStage = LoadStage.Mounting; _task.Path = typeof(AnySeparatorFileAttacher).FullName; @@ -152,7 +161,10 @@ public void MEFCompatibleType_Passes() "DelMeProjDir", true); try { - _lmd.LocationOfFlatFiles = projDir.RootPath.FullName; + _lmd.LocationOfForLoadingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForLoadingPath); + _lmd.LocationOfForArchivingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForArchivingPath); + _lmd.LocationOfExecutablesDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultExecutablesPath); + _lmd.LocationOfCacheDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultCachePath); _task.ProcessTaskType = ProcessTaskType.Attacher; _task.LoadStage = LoadStage.Mounting; _task.Path = typeof(AnySeparatorFileAttacher).FullName; diff --git a/Rdmp.Core.Tests/DataLoad/Engine/Integration/CrossDatabaseTypeTests/CrossDatabaseDataLoadTests.cs b/Rdmp.Core.Tests/DataLoad/Engine/Integration/CrossDatabaseTypeTests/CrossDatabaseDataLoadTests.cs index bf0f2c1657..2dab13fe1c 100644 --- a/Rdmp.Core.Tests/DataLoad/Engine/Integration/CrossDatabaseTypeTests/CrossDatabaseDataLoadTests.cs +++ b/Rdmp.Core.Tests/DataLoad/Engine/Integration/CrossDatabaseTypeTests/CrossDatabaseDataLoadTests.cs @@ -292,7 +292,10 @@ public void Load(DatabaseType databaseType, TestCase testCase) } finally { - Directory.Delete(lmd.LocationOfFlatFiles, true); + Directory.Delete(lmd.LocationOfForLoadingDirectory, true); + Directory.Delete(lmd.LocationOfForArchivingDirectory, true); + Directory.Delete(lmd.LocationOfExecutablesDirectory, true); + Directory.Delete(lmd.LocationOfCacheDirectory, true); foreach (var c in RepositoryLocator.CatalogueRepository.GetAllObjects()) c.DeleteInDatabase(); @@ -461,7 +464,10 @@ public void DLELoadTwoTables(DatabaseType databaseType) } finally { - Directory.Delete(lmd.LocationOfFlatFiles, true); + Directory.Delete(lmd.LocationOfForLoadingDirectory, true); + Directory.Delete(lmd.LocationOfForArchivingDirectory, true); + Directory.Delete(lmd.LocationOfExecutablesDirectory, true); + Directory.Delete(lmd.LocationOfCacheDirectory, true); foreach (var c in RepositoryLocator.CatalogueRepository.GetAllObjects()) c.DeleteInDatabase(); diff --git a/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataLoadEngineTestsBase.cs b/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataLoadEngineTestsBase.cs index d2c40eaaad..454fae0e12 100644 --- a/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataLoadEngineTestsBase.cs +++ b/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataLoadEngineTestsBase.cs @@ -72,7 +72,10 @@ protected static LoadDirectory SetupLoadDirectory(LoadMetadata lmd) var projectDirectory = LoadDirectory.CreateDirectoryStructure(new DirectoryInfo(TestContext.CurrentContext.TestDirectory), "MyLoadDir", true); - lmd.LocationOfFlatFiles = projectDirectory.RootPath.FullName; + lmd.LocationOfForLoadingDirectory = Path.Combine(projectDirectory.RootPath.FullName, lmd.DefaultForLoadingPath); + lmd.LocationOfForArchivingDirectory = Path.Combine(projectDirectory.RootPath.FullName, lmd.DefaultForArchivingPath); + lmd.LocationOfExecutablesDirectory = Path.Combine(projectDirectory.RootPath.FullName, lmd.DefaultExecutablesPath); + lmd.LocationOfCacheDirectory = Path.Combine(projectDirectory.RootPath.FullName, lmd.DefaultCachePath); lmd.SaveToDatabase(); return projectDirectory; diff --git a/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataTableUploadDestinationTests.cs b/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataTableUploadDestinationTests.cs index 758604e6c5..27f38c6a30 100644 --- a/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataTableUploadDestinationTests.cs +++ b/Rdmp.Core.Tests/DataLoad/Engine/Integration/DataTableUploadDestinationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -7,13 +7,16 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.Common; using System.Linq; using FAnsi; using FAnsi.Discovery; using FAnsi.Discovery.TableCreation; using NUnit.Framework; +using NUnit.Framework.Internal; using Rdmp.Core.DataFlowPipeline; using Rdmp.Core.DataLoad.Engine.Pipeline.Destinations; +using Rdmp.Core.DataLoad.Triggers; using Rdmp.Core.ReusableLibraryCode; using Rdmp.Core.ReusableLibraryCode.Progress; using Tests.Common; @@ -86,7 +89,7 @@ public void DataTableChangesLengths_RandomColumnOrder(bool createIdentity, int n "age varchar(50)," }; - if(createIdentity) + if (createIdentity) leftToCreate.Add("id int IDENTITY(1,1),"); var invalid = false; @@ -1401,5 +1404,445 @@ public void TwoBatch_ExplicitRealDataType() }); } + + [Test] + public void CreateIndex_OK() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" } + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Rows.Add(new[] { "Fish" }); + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + using (var con = db.Server.GetConnection()) + { + con.Open(); + DbCommand cmd = db.Server.GetCommand("select * from sys.indexes where name = 'CreateIndex_OK'", con); + var r = cmd.ExecuteReader(); + Assert.That(r.HasRows); + r.Read(); + Assert.That(r["name"], Is.EqualTo("CreateIndex_OK")); + } + } + + [Test] + public void CreateIndex_NO_PK() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK" + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Rows.Add(new[] { "Fish" }); + dt1.TableName = "DataTableUploadDestinationTests"; + + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + using (var con = db.Server.GetConnection()) + { + con.Open(); + DbCommand cmd = db.Server.GetCommand("select * from sys.indexes where name = 'CreateIndex_OK'", con); + var r = cmd.ExecuteReader(); + Assert.That(r.HasRows, Is.EqualTo(false)); + } + } + + [Test] + public void CreateIndex_RecreateExistingFail() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" } + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Rows.Add(new[] { "Fish" }); + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + using (var con = db.Server.GetConnection()) + { + con.Open(); + DbCommand cmd = db.Server.GetCommand("select * from sys.indexes where name = 'CreateIndex_OK'", con); + var r = cmd.ExecuteReader(); + Assert.That(r.HasRows); + r.Read(); + Assert.That(r["name"], Is.EqualTo("CreateIndex_OK")); + Assert.That(r.Read(), Is.EqualTo(false));//only 1 row + } + } + + [Test] + public void UseTriggerwithoutPrimaryKey() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + UseTrigger = true + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Rows.Add(new[] { "Fish" }); + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + var t = db.DiscoverTables(false).Select(t => t.GetRuntimeName()).ToList(); + Assert.That(t.Contains("DataTableUploadDestinationTests_Archive"), Is.EqualTo(false)); + var destinationTable = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests").First(); + var columns = destinationTable.DiscoverColumns().Select(c => c.GetRuntimeName()); + Assert.That(columns.Contains(SpecialFieldNames.DataLoadRunID), Is.EqualTo(false)); + } + + [Test] + public void UseTriggerwithPrimaryKey() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + UseTrigger = true + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Rows.Add(new[] { "Fish" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + var t = db.DiscoverTables(false).Select(t => t.GetRuntimeName()).ToList(); + Assert.That(t.Contains("DataTableUploadDestinationTests_Archive"), Is.EqualTo(true)); + var destinationTable = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests").First(); + var columns = destinationTable.DiscoverColumns().Select(c => c.GetRuntimeName()); + Assert.That(columns.Contains(SpecialFieldNames.DataLoadRunID), Is.EqualTo(true)); + } + + [Test] + public void DataTableUploadClashSameRow() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" }, + AppendDataIfTableExists = true + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Friend" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Friend" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; ; + Assert.Throws(()=>destination.ProcessPipelineData(dt1, toConsole, token)); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + var table = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests").First(); + var resultDT = table.GetDataTable(); + Assert.That(resultDT.Rows.Count, Is.EqualTo(1)); + } + [Test] + public void DataTableUploadClashSameRowWithTrigger() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" }, + AppendDataIfTableExists = true, + UseTrigger = true + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Friend" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Friend" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; ; + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + var table = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests").First(); + var resultDT = table.GetDataTable(); + Assert.That(resultDT.Rows.Count, Is.EqualTo(1)); + } + [Test] + public void DataTableUploadClashUpdateNoTrigger() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" }, + AppendDataIfTableExists = true + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Friend" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Enemy" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; + destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" }, + AppendDataIfTableExists = true + }; + destination.PreInitialize(db, toConsole); + Assert.Throws(() => destination.ProcessPipelineData(dt1, toConsole, token)); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + var table = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests").First(); + var resultDT = table.GetDataTable(); + Assert.That(resultDT.Rows.Count, Is.EqualTo(1)); + Assert.That(resultDT.Rows[0].ItemArray[0], Is.EqualTo("Fish")); + Assert.That(resultDT.Rows[0].ItemArray[1], Is.EqualTo("Friend")); + } + [Test] + public void DataTableUploadClashUpdateWithTrigger() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" }, + AppendDataIfTableExists = true, + UseTrigger = true + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Friend" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Enemy" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; ; + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + var table = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests").First(); + var resultDT = table.GetDataTable(); + Assert.That(resultDT.Rows.Count, Is.EqualTo(1)); + Assert.That(resultDT.Rows[0].ItemArray[0], Is.EqualTo("Fish")); + Assert.That(resultDT.Rows[0].ItemArray[1], Is.EqualTo("Enemy")); + table = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests_Archive").First(); + resultDT = table.GetDataTable(); + Assert.That(resultDT.Rows.Count, Is.EqualTo(1)); + Assert.That(resultDT.Rows[0].ItemArray[0], Is.EqualTo("Fish")); + } + //need to test rows to modify ln 354 + //test when we drop a column + [Test] + public void DataTableUploadClashUpdateDropColumnWithTrigger() + { + var token = new GracefulCancellationToken(); + var db = GetCleanedServer(DatabaseType.MicrosoftSQLServer); + var toConsole = ThrowImmediatelyDataLoadEventListener.Quiet; + + var destination = new DataTableUploadDestination + { + IndexTables = true, + IndexTableName = "CreateIndex_OK", + UserDefinedIndexes = new List() { "name" }, + AppendDataIfTableExists = true, + UseTrigger = true + }; + destination.PreInitialize(db, toConsole); + + var dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish", "Friend" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; + try + { + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + dt1 = new DataTable(); + dt1.Columns.Add("name", typeof(string)); + dt1.Columns.Add("other", typeof(string)); + dt1.Rows.Add(new[] { "Fish" }); + dt1.PrimaryKey = new[] { dt1.Columns[0] }; + dt1.TableName = "DataTableUploadDestinationTests"; ; + destination.ProcessPipelineData(dt1, toConsole, token); + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, null); + } + catch (Exception ex) + { + destination.Dispose(ThrowImmediatelyDataLoadEventListener.Quiet, ex); + throw; + } + var table = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests").First(); + var resultDT = table.GetDataTable(); + Assert.That(resultDT.Rows.Count, Is.EqualTo(1)); + Assert.That(resultDT.Rows[0].ItemArray[0], Is.EqualTo("Fish")); + Assert.That(resultDT.Rows[0].ItemArray[1], Is.EqualTo(string.Empty)); + table = db.DiscoverTables(false).Where(t => t.GetRuntimeName() == "DataTableUploadDestinationTests_Archive").First(); + resultDT = table.GetDataTable(); + Assert.That(resultDT.Rows.Count, Is.EqualTo(1)); + Assert.That(resultDT.Rows[0].ItemArray[0], Is.EqualTo("Fish")); + } + + + #endregion } \ No newline at end of file diff --git a/Rdmp.Core.Tests/DataLoad/Engine/Integration/HICPipelineTests.cs b/Rdmp.Core.Tests/DataLoad/Engine/Integration/HICPipelineTests.cs index dc87c28ca8..8d3090f788 100644 --- a/Rdmp.Core.Tests/DataLoad/Engine/Integration/HICPipelineTests.cs +++ b/Rdmp.Core.Tests/DataLoad/Engine/Integration/HICPipelineTests.cs @@ -72,10 +72,11 @@ public void Create(CatalogueRepository repository, DiscoveredDatabase database, }; ColumnInfo.SaveToDatabase(); - LoadMetadata = new LoadMetadata(repository, "HICLoadPipelineTests") - { - LocationOfFlatFiles = directory.RootPath.FullName - }; + LoadMetadata = new LoadMetadata(repository, "HICLoadPipelineTests"); + LoadMetadata.LocationOfForLoadingDirectory = Path.Combine(directory.RootPath.FullName, LoadMetadata.DefaultForLoadingPath); + LoadMetadata.LocationOfForArchivingDirectory = Path.Combine(directory.RootPath.FullName, LoadMetadata.DefaultForArchivingPath); + LoadMetadata.LocationOfExecutablesDirectory = Path.Combine(directory.RootPath.FullName, LoadMetadata.DefaultExecutablesPath); + LoadMetadata.LocationOfCacheDirectory = Path.Combine(directory.RootPath.FullName, LoadMetadata.DefaultCachePath); LoadMetadata.SaveToDatabase(); Catalogue = new Catalogue(repository, "HICLoadPipelineTests") diff --git a/Rdmp.Core.Tests/DataLoad/Engine/Integration/JobDateGenerationStrategyFactoryTestsIntegration.cs b/Rdmp.Core.Tests/DataLoad/Engine/Integration/JobDateGenerationStrategyFactoryTestsIntegration.cs index 712ce5eda9..8ce1f15643 100644 --- a/Rdmp.Core.Tests/DataLoad/Engine/Integration/JobDateGenerationStrategyFactoryTestsIntegration.cs +++ b/Rdmp.Core.Tests/DataLoad/Engine/Integration/JobDateGenerationStrategyFactoryTestsIntegration.cs @@ -127,7 +127,10 @@ public void CacheProvider_NoPipeline() var projDir = LoadDirectory.CreateDirectoryStructure(new DirectoryInfo(TestContext.CurrentContext.TestDirectory), "delme", true); - _lmd.LocationOfFlatFiles = projDir.RootPath.FullName; + _lmd.LocationOfForLoadingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForLoadingPath); + _lmd.LocationOfForArchivingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForArchivingPath); + _lmd.LocationOfExecutablesDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultExecutablesPath); + _lmd.LocationOfCacheDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultCachePath); _lmd.SaveToDatabase(); try { @@ -154,7 +157,10 @@ public void CacheProvider_NoCacheProgress() var projDir = LoadDirectory.CreateDirectoryStructure(new DirectoryInfo(TestContext.CurrentContext.TestDirectory), "delme", true); - _lmd.LocationOfFlatFiles = projDir.RootPath.FullName; + _lmd.LocationOfForLoadingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForLoadingPath); + _lmd.LocationOfForArchivingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForArchivingPath); + _lmd.LocationOfExecutablesDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultExecutablesPath); + _lmd.LocationOfCacheDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultCachePath); _lmd.SaveToDatabase(); var pipeAssembler = new TestDataPipelineAssembler("CacheProvider_Normal", CatalogueRepository); @@ -192,7 +198,10 @@ public void CacheProvider_Normal() var projDir = LoadDirectory.CreateDirectoryStructure(new DirectoryInfo(TestContext.CurrentContext.TestDirectory), "delme", true); - _lmd.LocationOfFlatFiles = projDir.RootPath.FullName; + _lmd.LocationOfForLoadingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForLoadingPath); + _lmd.LocationOfForArchivingDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultForArchivingPath); + _lmd.LocationOfExecutablesDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultExecutablesPath); + _lmd.LocationOfCacheDirectory = Path.Combine(projDir.RootPath.FullName, _lmd.DefaultCachePath); _lmd.SaveToDatabase(); var pipeAssembler = new TestDataPipelineAssembler("CacheProvider_Normal", CatalogueRepository); diff --git a/Rdmp.Core.Tests/DataLoad/Engine/Integration/PayloadTest.cs b/Rdmp.Core.Tests/DataLoad/Engine/Integration/PayloadTest.cs index 69761f887c..3792edb53b 100644 --- a/Rdmp.Core.Tests/DataLoad/Engine/Integration/PayloadTest.cs +++ b/Rdmp.Core.Tests/DataLoad/Engine/Integration/PayloadTest.cs @@ -37,12 +37,14 @@ public void TestPayloadInjection() b.SetupTestData(); b.ImportAsCatalogue(); - var lmd = new LoadMetadata(CatalogueRepository, "Loading") - { - LocationOfFlatFiles = LoadDirectory + var lmd = new LoadMetadata(CatalogueRepository, "Loading"); + var filePath = LoadDirectory .CreateDirectoryStructure(new DirectoryInfo(TestContext.CurrentContext.TestDirectory), "delme", true) - .RootPath.FullName - }; + .RootPath.FullName; + lmd.LocationOfForLoadingDirectory = Path.Combine(filePath , lmd.DefaultForLoadingPath); + lmd.LocationOfForArchivingDirectory = Path.Combine(filePath , lmd.DefaultForArchivingPath); + lmd.LocationOfExecutablesDirectory = Path.Combine(filePath , lmd.DefaultExecutablesPath); + lmd.LocationOfCacheDirectory = Path.Combine(filePath , lmd.DefaultCachePath); lmd.SaveToDatabase(); MEF.AddTypeToCatalogForTesting(typeof(TestPayloadAttacher)); diff --git a/Rdmp.Core.Tests/Rdmp.Core.Tests.csproj b/Rdmp.Core.Tests/Rdmp.Core.Tests.csproj index c4d54b0cb8..0b29d487a7 100644 --- a/Rdmp.Core.Tests/Rdmp.Core.Tests.csproj +++ b/Rdmp.Core.Tests/Rdmp.Core.Tests.csproj @@ -70,8 +70,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -80,7 +80,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Rdmp.Core.Tests/Setting/SettingValidationTest.cs b/Rdmp.Core.Tests/Setting/SettingValidationTest.cs new file mode 100644 index 0000000000..db39bfa322 --- /dev/null +++ b/Rdmp.Core.Tests/Setting/SettingValidationTest.cs @@ -0,0 +1,59 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see .using Amazon.Auth.AccessControlPolicy; + +using Microsoft.Data.SqlClient; +using NUnit.Framework; +using System; +using System.Linq; +using Tests.Common; + +namespace Rdmp.Core.Tests.Setting; + +public class SettingValidationTest : DatabaseTests +{ + + [Test] + public void SettingManagement() + { + //create new setting + var setting = new Core.Setting.Setting(RepositoryLocator.CatalogueRepository, "Key", "Value"); + Assert.DoesNotThrow(()=>setting.SaveToDatabase()); + var knownSettings = RepositoryLocator.CatalogueRepository.GetAllObjects().ToList(); + Assert.That(knownSettings.Count, Is.EqualTo(1)); + Assert.That(knownSettings[0].Key, Is.EqualTo("Key")); + Assert.That(knownSettings[0].Value, Is.EqualTo("Value")); + //update value + setting.Value = "OtherValue"; + Assert.DoesNotThrow(() => setting.SaveToDatabase()); + knownSettings = RepositoryLocator.CatalogueRepository.GetAllObjects().ToList(); + Assert.That(knownSettings.Count, Is.EqualTo(1)); + Assert.That(knownSettings[0].Key, Is.EqualTo("Key")); + Assert.That(knownSettings[0].Value, Is.EqualTo("OtherValue")); + //add a second setting + setting = new Core.Setting.Setting(RepositoryLocator.CatalogueRepository, "SecondKey", "SecondValue"); + Assert.DoesNotThrow(() => setting.SaveToDatabase()); + knownSettings = RepositoryLocator.CatalogueRepository.GetAllObjects().ToList(); + Assert.That(knownSettings.Count, Is.EqualTo(2)); + Assert.That(knownSettings[0].Key, Is.EqualTo("Key")); + Assert.That(knownSettings[0].Value, Is.EqualTo("OtherValue")); + Assert.That(knownSettings[1].Key, Is.EqualTo("SecondKey")); + Assert.That(knownSettings[1].Value, Is.EqualTo("SecondValue")); + //create a key that already exists + Assert.Throws(() => { + setting = new Core.Setting.Setting(RepositoryLocator.CatalogueRepository, "Key", "Value"); + }); + Assert.That(knownSettings.Count, Is.EqualTo(2)); + Assert.That(knownSettings[0].Key, Is.EqualTo("Key")); + Assert.That(knownSettings[0].Value, Is.EqualTo("OtherValue")); + Assert.That(knownSettings[1].Key, Is.EqualTo("SecondKey")); + Assert.That(knownSettings[1].Value, Is.EqualTo("SecondValue")); + foreach(Core.Setting.Setting s in knownSettings) + { + s.DeleteInDatabase(); + } + + } +} diff --git a/Rdmp.Core/Caching/Pipeline/CachingPipelineUseCase.cs b/Rdmp.Core/Caching/Pipeline/CachingPipelineUseCase.cs index 75cb332660..dd92878929 100644 --- a/Rdmp.Core/Caching/Pipeline/CachingPipelineUseCase.cs +++ b/Rdmp.Core/Caching/Pipeline/CachingPipelineUseCase.cs @@ -67,7 +67,7 @@ public CachingPipelineUseCase(ICacheProgress cacheProgress, bool ignorePermissio // Get the LoadDirectory for the engine initialization var lmd = _cacheProgress.LoadProgress.LoadMetadata; - if (string.IsNullOrWhiteSpace(lmd.LocationOfFlatFiles)) + if (string.IsNullOrWhiteSpace(lmd.LocationOfForLoadingDirectory) || string.IsNullOrWhiteSpace(lmd.LocationOfForArchivingDirectory) || string.IsNullOrWhiteSpace(lmd.LocationOfCacheDirectory) || string.IsNullOrWhiteSpace(lmd.LocationOfExecutablesDirectory)) { if (throwIfNoPipeline) throw new Exception( @@ -75,7 +75,8 @@ public CachingPipelineUseCase(ICacheProgress cacheProgress, bool ignorePermissio } else { - AddInitializationObject(new LoadDirectory(lmd.LocationOfFlatFiles)); + + AddInitializationObject(new LoadDirectory(lmd.LocationOfForLoadingDirectory, lmd.LocationOfForArchivingDirectory, lmd.LocationOfExecutablesDirectory, lmd.LocationOfCacheDirectory)); } AddInitializationObject(providerIfAny1); diff --git a/Rdmp.Core/CohortCommitting/Pipeline/Destinations/IdentifierAllocation/ProjectConsistentGuidReleaseIdentifierAllocator.cs b/Rdmp.Core/CohortCommitting/Pipeline/Destinations/IdentifierAllocation/ProjectConsistentGuidReleaseIdentifierAllocator.cs index 7ee580198a..b4b5df6370 100644 --- a/Rdmp.Core/CohortCommitting/Pipeline/Destinations/IdentifierAllocation/ProjectConsistentGuidReleaseIdentifierAllocator.cs +++ b/Rdmp.Core/CohortCommitting/Pipeline/Destinations/IdentifierAllocation/ProjectConsistentGuidReleaseIdentifierAllocator.cs @@ -80,7 +80,6 @@ private Dictionary GetReleaseMap() if (toReturn.TryGetValue(r[priv], out var oldValue)) throw new Exception( $"Private identifier '{r[priv]}' has more than 1 historical release identifier ({string.Join(",", oldValue, r[rel])}"); - toReturn.Add(r[priv], r[rel]); } diff --git a/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs b/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs index 30fb077df2..310e8d18a6 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs @@ -126,7 +126,6 @@ public IEnumerable CreateCommands(object o) SuggestedCategory = Add, OverrideCommandName = "New Supporting Document" }; - if (!isApiCall) { yield return new ExecuteCommandChangeExtractability(_activator, c) @@ -236,7 +235,6 @@ public IEnumerable CreateCommands(object o) yield return new ExecuteCommandChangeExtractionCategory(_activator, new[] { ci.ExtractionInformation }); yield return new ExecuteCommandImportCatalogueItemDescription(_activator, ci) { SuggestedShortcut = "I", Ctrl = true }; - var ciExtractionInfo = ci.ExtractionInformation; if (ciExtractionInfo != null) { @@ -552,8 +550,6 @@ public IEnumerable CreateCommands(object o) if (Is(o, out LoadDirectoryNode ldn)) { - yield return new ExecuteCommandSet(_activator, ldn.LoadMetadata, - typeof(LoadMetadata).GetProperty(nameof(LoadMetadata.LocationOfFlatFiles))); yield return new ExecuteCommandCreateNewDataLoadDirectory(_activator, ldn.LoadMetadata, null); } diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneCohortIdentificationConfiguration.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneCohortIdentificationConfiguration.cs index e841e4c08c..c19374a455 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneCohortIdentificationConfiguration.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneCohortIdentificationConfiguration.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -20,6 +20,9 @@ public class ExecuteCommandCloneCohortIdentificationConfiguration : BasicCommand { private CohortIdentificationConfiguration _cic; private Project _project; + private readonly string _name; + private readonly int? _version; + private readonly bool _autoConfirm; /// /// The clone that was created this command or null if it has not been executed/failed @@ -28,10 +31,13 @@ public class ExecuteCommandCloneCohortIdentificationConfiguration : BasicCommand [UseWithObjectConstructor] public ExecuteCommandCloneCohortIdentificationConfiguration(IBasicActivateItems activator, - CohortIdentificationConfiguration cic) + CohortIdentificationConfiguration cic, string name = null, int? version = null, bool autoConfirm = false) : base(activator) { _cic = cic; + _name = name; + _version = version; + _autoConfirm = autoConfirm; } public override string GetCommandHelp() => @@ -69,10 +75,19 @@ public override void Execute() return; // Confirm creating yes/no (assuming activator is interactive) - if (BasicActivator.IsInteractive && !YesNo( + if (!_autoConfirm && BasicActivator.IsInteractive && !YesNo( "This will create a 100% copy of the entire CohortIdentificationConfiguration including all datasets, filters, parameters and set operations. Are you sure this is what you want?", "Confirm Cloning")) return; CloneCreatedIfAny = _cic.CreateClone(ThrowImmediatelyCheckNotifier.Quiet); + if (CloneCreatedIfAny != null) + { + CloneCreatedIfAny.Version = _version; + //If no name is provided, use the existing name, but repalce the "(Clone) with the version number" + CloneCreatedIfAny.Name = _name ?? $"{CloneCreatedIfAny.Name[..^7]}:{CloneCreatedIfAny.Version}"; + if (_version is not null) + CloneCreatedIfAny.Frozen = true; + CloneCreatedIfAny.SaveToDatabase(); + } if (_project != null) // clone the association _ = new ProjectCohortIdentificationConfigurationAssociation( diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneExtractionConfiguration.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneExtractionConfiguration.cs index 5715ae412a..2fcb3707f1 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneExtractionConfiguration.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCloneExtractionConfiguration.cs @@ -1,9 +1,11 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with RDMP. If not, see . +using System; +using System.Collections.Generic; using System.Linq; using Rdmp.Core.Curation.Data; using Rdmp.Core.DataExport.Data; @@ -17,12 +19,53 @@ namespace Rdmp.Core.CommandExecution.AtomicCommands; public class ExecuteCommandCloneExtractionConfiguration : BasicCommandExecution, IAtomicCommand { private readonly ExtractionConfiguration _extractionConfiguration; + private readonly IBasicActivateItems _activeItems; + private readonly List toRemove = []; + private readonly List toAdd = []; + private void CheckForDeprecatedCatalogues() + { + if (_extractionConfiguration.SelectedDataSets.Any(sd => sd.GetCatalogue().IsDeprecated) && _activeItems.IsInteractive) + { + if (YesNo("There are Deprecated catalogues in this Extraction Configuration. Would you like to replace them with their replacement (where available)?", "Replace Deprecated Catalogues")) + { + var repo = _activeItems.RepositoryLocator.CatalogueRepository; + var DeprecatedDatasets = _extractionConfiguration.SelectedDataSets.Where(sd => sd.GetCatalogue().IsDeprecated).ToList(); + var replacedBy = repo.GetExtendedProperties(ExtendedProperty.ReplacedBy); + foreach (ISelectedDataSets ds in DeprecatedDatasets) + { + var replacement = replacedBy.Where(rb => rb.ReferencedObjectID == ds.GetCatalogue().ID).FirstOrDefault(); + if (replacement is not null) + { + var replacementCatalogue = repo.GetObjectByID(Int32.Parse(replacement.Value)); + while (replacementCatalogue.IsDeprecated) + { + var replacementCatalogueIsReplacedBy = replacedBy.Where(rb => rb.ReferencedObjectID == replacementCatalogue.ID).FirstOrDefault(); + if(replacementCatalogueIsReplacedBy is not null) + { + //have found further down the tree + replacementCatalogue = repo.GetObjectByID(Int32.Parse(replacementCatalogueIsReplacedBy.Value)); + } + else + { + //there is no replacement + break; + } + } + toRemove.Add(ds.ExtractableDataSet); + toAdd.Add(replacementCatalogue); + } + } + + + } + } + } public ExecuteCommandCloneExtractionConfiguration(IBasicActivateItems activator, ExtractionConfiguration extractionConfiguration) : base(activator) { _extractionConfiguration = extractionConfiguration; - + _activeItems = activator; if (!_extractionConfiguration.SelectedDataSets.Any()) SetImpossible("ExtractionConfiguration does not have any selected datasets"); } @@ -36,9 +79,24 @@ public override Image GetImage(IIconProvider iconProvider) => public override void Execute() { base.Execute(); + CheckForDeprecatedCatalogues(); var clone = _extractionConfiguration.DeepCloneWithNewIDs(); - + foreach (ExtractableDataSet ds in toRemove) + { + clone.RemoveDatasetFromConfiguration(ds); + } + foreach (Catalogue c in toAdd) + { + //check if the eds already exis + var eds = _activeItems.RepositoryLocator.DataExportRepository.GetAllObjectsWhere("Catalogue_ID", c.ID).FirstOrDefault(); + if (eds is null) + { + eds = new ExtractableDataSet(_activeItems.RepositoryLocator.DataExportRepository, c); + eds.SaveToDatabase(); + } + clone.AddDatasetToConfiguration(eds); + } Publish((DatabaseEntity)clone.Project); Emphasise(clone); } diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewDataLoadDirectory.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewDataLoadDirectory.cs index 8a865583ee..9642a2356b 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewDataLoadDirectory.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewDataLoadDirectory.cs @@ -29,7 +29,7 @@ public class ExecuteCommandCreateNewDataLoadDirectory : BasicCommandExecution public ExecuteCommandCreateNewDataLoadDirectory(IBasicActivateItems activator, [DemandsInitialization( - "Optional load for which you are creating the folder structure. Will have its LocationOfFlatFiles set to the new dir if passed")] + "Optional load for which you are creating the folder structure. Will have its directory locations set to the new dir if passed")] LoadMetadata load, [DemandsInitialization("The directory to create new load folders in.")] DirectoryInfo dir) : base(activator) @@ -67,7 +67,10 @@ public override void Execute() // if we have a load then update the path to this location we just created if (LoadMetadata != null) { - LoadMetadata.LocationOfFlatFiles = loadDir.RootPath.FullName; + LoadMetadata.LocationOfForLoadingDirectory = Path.Combine(loadDir.RootPath.FullName ,LoadMetadata.DefaultForLoadingPath); + LoadMetadata.LocationOfForArchivingDirectory = Path.Combine(loadDir.RootPath.FullName, LoadMetadata.DefaultForArchivingPath); + LoadMetadata.LocationOfExecutablesDirectory = Path.Combine(loadDir.RootPath.FullName , LoadMetadata.DefaultExecutablesPath); + LoadMetadata.LocationOfCacheDirectory = Path.Combine(loadDir.RootPath.FullName , LoadMetadata.DefaultCachePath); LoadMetadata.SaveToDatabase(); Publish(LoadMetadata); } diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewFileBasedProcessTask.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewFileBasedProcessTask.cs index b7d223a4c7..880f8e8fad 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewFileBasedProcessTask.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewFileBasedProcessTask.cs @@ -35,10 +35,11 @@ public ExecuteCommandCreateNewFileBasedProcessTask(IBasicActivateItems activator try { - _loadDirectory = new LoadDirectory(_loadMetadata.LocationOfFlatFiles); + _loadDirectory = new LoadDirectory(_loadMetadata.LocationOfForLoadingDirectory, _loadMetadata.LocationOfForArchivingDirectory, _loadMetadata.LocationOfExecutablesDirectory, _loadMetadata.LocationOfCacheDirectory ); } - catch (Exception) + catch (Exception e ) { + Console.WriteLine(e.Message); SetImpossible("Could not construct LoadDirectory"); } diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewSplitDataLoadDirectory.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewSplitDataLoadDirectory.cs new file mode 100644 index 0000000000..b2f838c735 --- /dev/null +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateNewSplitDataLoadDirectory.cs @@ -0,0 +1,125 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using System.IO; +using Rdmp.Core.Curation; +using Rdmp.Core.Curation.Data; +using Rdmp.Core.Curation.Data.DataLoad; + +namespace Rdmp.Core.CommandExecution.AtomicCommands; + +/// +/// Creates the expected and required flat file layout for a +/// +public class ExecuteCommandCreateNewSplitDataLoadDirectory : BasicCommandExecution +{ + /// + /// The load if any to create the folder structure for + /// + public LoadMetadata LoadMetadata { get; } + + /// + /// The directory to create or null to do the operation + /// interactively. + /// + public DirectoryInfo ForLoadingDir { get; } + /// + /// The directory to create or null to do the operation + /// interactively. + /// + public DirectoryInfo ForArchivingDir { get; } + /// + /// The directory to create or null to do the operation + /// interactively. + /// + public DirectoryInfo ExecutablesDir { get; } + /// + /// The directory to create or null to do the operation + /// interactively. + /// + public DirectoryInfo CacheDir { get; } + + public ExecuteCommandCreateNewSplitDataLoadDirectory(IBasicActivateItems activator, + [DemandsInitialization( + "Optional load for which you are creating the folder structure. Will have its directory locations set to the new dir if passed")] + LoadMetadata load, + [DemandsInitialization("The directory to create new loads in.")] + DirectoryInfo forLoadingDir, + [DemandsInitialization("The directory to create new archives in.")] + DirectoryInfo forArchivingDir, + [DemandsInitialization("The directory to create new executables in.")] + DirectoryInfo executablesDir, + [DemandsInitialization("The directory to create new caches in.")] + DirectoryInfo cacheDir + ) : base(activator) + { + LoadMetadata = load; + ForLoadingDir = forLoadingDir; + ForArchivingDir = forArchivingDir; + ExecutablesDir = executablesDir; + CacheDir = cacheDir; + } + + public override void Execute() + { + base.Execute(); + + var fl = ForLoadingDir; + var fa = ForArchivingDir; + var e = ExecutablesDir; + var c = CacheDir; + + // if called with an explicit full dir then that is where we create load folders + // otherwise get them to pick something that exists and then name a new folder to + // create + + if (fl == null) + { + fl = BasicActivator.SelectDirectory("Directory to load files in from"); + + if (fl == null) + return; + + } + if (fa == null) + { + fa = BasicActivator.SelectDirectory("Directory to store the archives in"); + + if (fa == null) + return; + + } + if (e== null) + { + e = BasicActivator.SelectDirectory("Directory to store executables"); + + if (e == null) + return; + + } + if (c == null) + { + c = BasicActivator.SelectDirectory("Directory to store caches"); + + if (c == null) + return; + + } + + var loadDir = new LoadDirectory(fl.FullName, fa.FullName, e.FullName, c.FullName); + + // if we have a load then update the path to this location we just created + if (LoadMetadata != null) + { + LoadMetadata.LocationOfForLoadingDirectory = loadDir.ForLoading.FullName; + LoadMetadata.LocationOfForArchivingDirectory = loadDir.ForArchiving.FullName; + LoadMetadata.LocationOfExecutablesDirectory = loadDir.ExecutablesPath.FullName; + LoadMetadata.LocationOfCacheDirectory = loadDir.Cache.FullName; + LoadMetadata.SaveToDatabase(); + Publish(LoadMetadata); + } + } +} \ No newline at end of file diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateVersionOfCohortConfiguration.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateVersionOfCohortConfiguration.cs new file mode 100644 index 0000000000..0fe48fb990 --- /dev/null +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateVersionOfCohortConfiguration.cs @@ -0,0 +1,42 @@ +using Rdmp.Core.CohortCommitting.Pipeline.Sources; +using Rdmp.Core.Curation.Data; +using Rdmp.Core.Curation.Data.Cohort; +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . +using Spectre.Console; +using System.Linq; + + +namespace Rdmp.Core.CommandExecution.AtomicCommands; + +public class ExecuteCommandCreateVersionOfCohortConfiguration : BasicCommandExecution, IAtomicCommand +{ + readonly CohortIdentificationConfiguration _cic; + readonly IBasicActivateItems _activator; + readonly string _name; + + public ExecuteCommandCreateVersionOfCohortConfiguration(IBasicActivateItems activator, CohortIdentificationConfiguration cic, string name = null) : base(activator) + { + _cic = cic; + _activator = activator; + _name = name; + } + + + public override void Execute() + { + base.Execute(); + int? version = 1; + + var previousClones = _activator.RepositoryLocator.CatalogueRepository.GetAllObjectsWhere("ClonedFrom_ID", _cic.ID).Where(cic => cic.Version != null); + if (previousClones.Any()) + { + version = previousClones.Select(pc => pc.Version).Where(v => v != null).Max() + 1; + } + var cmd = new ExecuteCommandCloneCohortIdentificationConfiguration(_activator, _cic, _name, version, true); + cmd.Execute(); + } +} diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandDeprecate.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandDeprecate.cs index 86851ecc09..acdd6f89f0 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandDeprecate.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandDeprecate.cs @@ -7,6 +7,7 @@ using Rdmp.Core.Curation.Data; using Rdmp.Core.MapsDirectlyToDatabaseTable; using Rdmp.Core.Repositories.Construction; +using System.Linq; namespace Rdmp.Core.CommandExecution.AtomicCommands; @@ -14,6 +15,8 @@ public class ExecuteCommandDeprecate : BasicCommandExecution { private readonly IMightBeDeprecated[] _o; private readonly bool _desiredState; + private readonly IBasicActivateItems _activeItems; + [UseWithObjectConstructor] public ExecuteCommandDeprecate(IBasicActivateItems itemActivator, @@ -24,6 +27,7 @@ public ExecuteCommandDeprecate(IBasicActivateItems itemActivator, { _o = o; _desiredState = desiredState; + _activeItems = itemActivator; } public override string GetCommandName() => !string.IsNullOrEmpty(OverrideCommandName) ? OverrideCommandName : @@ -45,8 +49,17 @@ private void ExecuteImpl() { o.IsDeprecated = _desiredState; o.SaveToDatabase(); + if (!_desiredState && o.GetType() == typeof(Catalogue))//false is not-depricated + { + var c = (Catalogue) o; + var replacedBy = _activeItems.RepositoryLocator.CatalogueRepository.GetExtendedProperties(ExtendedProperty.ReplacedBy); + var replacement = replacedBy.Where(rb => rb.ReferencedObjectID == c.ID).FirstOrDefault(); + replacement.DeleteInDatabase(); + } } + + if (!BasicActivator.IsInteractive || _o.Length != 1 || _o[0] is not Catalogue || !_desiredState || !BasicActivator.YesNo("Do you have a replacement Catalogue you want to link?", "Replacement")) return; var cmd = new ExecuteCommandReplacedBy(BasicActivator, _o[0], null) diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandListCohortVersions.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandListCohortVersions.cs new file mode 100644 index 0000000000..0d997f3201 --- /dev/null +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandListCohortVersions.cs @@ -0,0 +1,41 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . +using Rdmp.Core.Curation.Data.Cohort; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rdmp.Core.CommandExecution.AtomicCommands; + +public class ExecuteCommandListCohortVersions: BasicCommandExecution, IAtomicCommand +{ + readonly CohortIdentificationConfiguration _cic; + readonly IBasicActivateItems _activator; + + public ExecuteCommandListCohortVersions(IBasicActivateItems activator, CohortIdentificationConfiguration cic):base(activator) + { + _cic = cic; + _activator = activator; + } + + public override void Execute() + { + base.Execute(); + var versions = _cic.GetVersions(); + var outputDictionary = new Dictionary(); + foreach ( var version in versions ) + { + outputDictionary.Add(version.Name, version.ID.ToString()); + } + + var output = string.Join(Environment.NewLine, + outputDictionary.Select(kvp => $"{kvp.Value}:{kvp.Key}") + .OrderBy(s => s)); + BasicActivator.Show(output); + + } + +} diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandUpdateCatalogueDataLocation.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandUpdateCatalogueDataLocation.cs new file mode 100644 index 0000000000..63e5eed8c4 --- /dev/null +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandUpdateCatalogueDataLocation.cs @@ -0,0 +1,156 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using System; +using System.Linq; +using FAnsi.Discovery; +using Rdmp.Core.Curation.Data; + +namespace Rdmp.Core.CommandExecution.AtomicCommands; + +public class ExecuteCommandUpdateCatalogueDataLocation : BasicCommandExecution, IAtomicCommand +{ + private readonly IBasicActivateItems _activator; + private readonly DiscoveredTable _table; + private readonly CatalogueItem[] _selectedCatalogueItems; + private readonly string _catalogueMapping; + + public readonly string CatalogueMappingIdentifier = "$column"; + + private bool _checksPassed; + + public ExecuteCommandUpdateCatalogueDataLocation(IBasicActivateItems activator, + CatalogueItem[] selectedCatalogueItems, DiscoveredTable table, string catalogueMapping) + { + _activator = activator; + _table = table; + _selectedCatalogueItems = selectedCatalogueItems; + _catalogueMapping = catalogueMapping; + } + + public string Check() + { + //check the server is alive + if (_table is null) return "No table has been set"; + _table.Database.Server.TestConnection(); + //must modify at least + if (_selectedCatalogueItems.Length == 0) return "Must select at least one catalogue item to modify"; + // check the mapping isn't junk + if (!string.IsNullOrWhiteSpace(_catalogueMapping) && !_catalogueMapping.Contains(CatalogueMappingIdentifier)) + return "Column Mapping must contain the string '$column'"; + // check the columns actually exist & that the types match + foreach (var item in _selectedCatalogueItems) + { + string newColumn; + try + { + newColumn = GrabColumnName(item.ColumnInfo.Name); + } + catch (Exception ex) + { + return ex.Message; + } + + var discoveredColumns = _table.DiscoverColumns(); + + var foundColumn = discoveredColumns.AsEnumerable() + .Where(dc => dc.GetFullyQualifiedName().Contains(newColumn)).FirstOrDefault(); + if (foundColumn is null) return $"Unable to find column '{newColumn}' in selected table"; + if (foundColumn.DataType.ToString() != item.ColumnInfo.Data_type) + return + $"The data type of column '{newColumn}' is of type '{foundColumn.DataType}'. This does not match the current type of '{item.ColumnInfo.Data_type}'"; + } + + _checksPassed = true; + return null; + } + + private string GrabTableQualifier(string name) + { + return _table.GetFullyQualifiedName(); + } + + private string GrabColumnName(string name) + { + return MutilateColumnWithMapping(name.Split('.')[^1]); + } + + private string MutilateColumnWithMapping(string columnName) + { + var useParenthesis = false; + if (columnName.StartsWith('[') && columnName.EndsWith("]")) + { + useParenthesis = true; + columnName = columnName.Substring(1, columnName.Length - 2); + } + + if (!string.IsNullOrWhiteSpace(_catalogueMapping)) + { + if (!_catalogueMapping.Contains(CatalogueMappingIdentifier)) + throw new Exception("Column Mapping is invalid. Add '$column' to the mapping string to fix this."); + columnName = _catalogueMapping.Replace(CatalogueMappingIdentifier, columnName); + } + + if (useParenthesis) columnName = '[' + columnName + ']'; + return columnName; + } + + + private string GenerateNewSQLPath(string path) + { + var qualifier = GrabTableQualifier(path); + var updatedName = qualifier + '.' + GrabColumnName(path); + return updatedName; + } + + private TableInfo TableIsAlreadyKnown() + { + return _activator.RepositoryLocator.CatalogueRepository.GetAllObjects().Where(ti => + { + return ti.Name == _table.GetFullyQualifiedName() && + ti.Server == _table.Database.Server.Name && + ti.Database == _table.Database.GetRuntimeName(); + }).FirstOrDefault(); + } + + + public override void Execute() + { + if (!_checksPassed) + { + var checkResults = Check(); + if (checkResults != null) + throw new Exception( + $"Unable to execute ExecuteCommandUpdateCatalogueDataLocation as the checks returned: {checkResults}"); + } + + foreach (var selectedCatalogueItem in _selectedCatalogueItems) + { + var existingTable = TableIsAlreadyKnown(); + if (existingTable is not null) + { + selectedCatalogueItem.ColumnInfo.TableInfo_ID = existingTable.ID; + } + else + { + var tblInfo = new TableInfo(_activator.RepositoryLocator.CatalogueRepository, + _table.GetFullyQualifiedName()); + tblInfo.Server = _table.Database.Server.Name; + tblInfo.Database = _table.Database.GetRuntimeName(); + tblInfo.SaveToDatabase(); + selectedCatalogueItem.ColumnInfo.TableInfo_ID = tblInfo.ID; + } + + selectedCatalogueItem.ColumnInfo.Name = GenerateNewSQLPath(selectedCatalogueItem.ColumnInfo.Name); + selectedCatalogueItem.ColumnInfo.SaveToDatabase(); + foreach (var ei in selectedCatalogueItem.ColumnInfo.ExtractionInformations) + { + ei.SelectSQL = GenerateNewSQLPath(selectedCatalogueItem.ExtractionInformation.SelectSQL); + ei.SaveToDatabase(); + } + } + } +} \ No newline at end of file diff --git a/Rdmp.Core/CommandLine/DatabaseCreation/CataloguePipelinesAndReferencesCreation.cs b/Rdmp.Core/CommandLine/DatabaseCreation/CataloguePipelinesAndReferencesCreation.cs index ac27c55721..e6cffd643d 100644 --- a/Rdmp.Core/CommandLine/DatabaseCreation/CataloguePipelinesAndReferencesCreation.cs +++ b/Rdmp.Core/CommandLine/DatabaseCreation/CataloguePipelinesAndReferencesCreation.cs @@ -35,6 +35,7 @@ public class CataloguePipelinesAndReferencesCreation private readonly SqlConnectionStringBuilder _dqe; private ExternalDatabaseServer _edsLogging; + public CataloguePipelinesAndReferencesCreation(IRDMPPlatformRepositoryServiceLocator repositoryLocator, SqlConnectionStringBuilder logging, SqlConnectionStringBuilder dqe) { diff --git a/Rdmp.Core/CommandLine/DatabaseCreation/PlatformDatabaseCreation.cs b/Rdmp.Core/CommandLine/DatabaseCreation/PlatformDatabaseCreation.cs index f7641de854..5a6755292e 100644 --- a/Rdmp.Core/CommandLine/DatabaseCreation/PlatformDatabaseCreation.cs +++ b/Rdmp.Core/CommandLine/DatabaseCreation/PlatformDatabaseCreation.cs @@ -44,6 +44,7 @@ public static void CreatePlatformDatabases(PlatformDatabaseCreationOptions optio { logging = Create(DefaultLoggingDatabaseName, new LoggingDatabasePatcher(), options); } + CatalogueRepository.SuppressHelpLoading = true; var repo = new PlatformDatabaseCreationRepositoryFinder(options); @@ -87,4 +88,4 @@ private static SqlConnectionStringBuilder Create(string databaseName, IPatcher p return builder; } -} \ No newline at end of file +} diff --git a/Rdmp.Core/CommandLine/Runners/PackPluginRunner.cs b/Rdmp.Core/CommandLine/Runners/PackPluginRunner.cs index 1981393641..5381b51ca1 100644 --- a/Rdmp.Core/CommandLine/Runners/PackPluginRunner.cs +++ b/Rdmp.Core/CommandLine/Runners/PackPluginRunner.cs @@ -27,7 +27,7 @@ namespace Rdmp.Core.CommandLine.Runners; public sealed partial class PackPluginRunner : IRunner { private readonly PackOptions _packOpts; - public const string PluginPackageSuffix = ".nupkg"; + public const string PluginPackageSuffix = ".rdmp"; private const string PluginPackageManifest = ".nuspec"; private static readonly Regex VersionSuffix = VersionSuffixRe(); diff --git a/Rdmp.Core/Curation/Data/Aggregation/AggregateDimension.cs b/Rdmp.Core/Curation/Data/Aggregation/AggregateDimension.cs index e310b30d3a..79bd68c059 100644 --- a/Rdmp.Core/Curation/Data/Aggregation/AggregateDimension.cs +++ b/Rdmp.Core/Curation/Data/Aggregation/AggregateDimension.cs @@ -34,6 +34,7 @@ public class AggregateDimension : DatabaseEntity, ISaveable, IDeleteable, IColum private string _alias; private string _selectSQL; private int _order; + private bool _groupBy; /// @@ -46,6 +47,17 @@ public int AggregateConfiguration_ID set => SetField(ref _aggregateConfigurationID, value); } + + /// + /// An is a column in the SELECT, GROUP BY and ORDER BY sections of an . + /// This property returns if the dimention should be added to any GROUP BY section of an . + /// + public bool GroupBy + { + get => _groupBy; + set => SetField(ref _groupBy, value); + } + /// /// An is a column in the SELECT, GROUP BY and/or ORDER BY sections of an . The column must have /// come from an extractable column in the parent . The Catalogue column definition is an and documents the @@ -157,7 +169,8 @@ public AggregateDimension(ICatalogueRepository repository, ExtractionInformation { "ExtractionInformation_ID", basedOnColumn.ID }, { "SelectSQL", basedOnColumn.SelectSQL }, { "Alias", alias }, - { "Order", basedOnColumn.Order } + { "Order", basedOnColumn.Order }, + {"GroupBy",true } }); ClearAllInjections(); @@ -172,6 +185,7 @@ internal AggregateDimension(ICatalogueRepository repository, DbDataReader r) : b Alias = r["Alias"] as string; Order = int.Parse(r["Order"].ToString()); + GroupBy = int.Parse(r["GroupBy"].ToString()) == 1; ClearAllInjections(); } diff --git a/Rdmp.Core/Curation/Data/Cohort/CohortIdentificationConfiguration.cs b/Rdmp.Core/Curation/Data/Cohort/CohortIdentificationConfiguration.cs index dda41c4765..4e5dd0ff8f 100644 --- a/Rdmp.Core/Curation/Data/Cohort/CohortIdentificationConfiguration.cs +++ b/Rdmp.Core/Curation/Data/Cohort/CohortIdentificationConfiguration.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -40,6 +40,18 @@ public class CohortIdentificationConfiguration : DatabaseEntity, ICollectSqlPara /// public const string CICPrefix = "cic_"; + + private int? _version; + + public int? Version + { + get => _version; + set => _version = value; + } + + + + #region Database Properties private string _name; @@ -179,6 +191,12 @@ public string Folder #endregion + + public List GetVersions() + { + return CatalogueRepository.GetAllObjectsWhere("ClonedFrom_ID", ID).Where(cic => cic.Version != null).ToList(); + } + public CohortIdentificationConfiguration() { } @@ -214,7 +232,7 @@ internal CohortIdentificationConfiguration(ICatalogueRepository repository, DbDa FrozenBy = r["FrozenBy"] as string; FrozenDate = ObjectToNullableDateTime(r["FrozenDate"]); ClonedFrom_ID = ObjectToNullableInt(r["ClonedFrom_ID"]); - + Version = ObjectToNullableInt(r["Version"]); Folder = r["Folder"] as string ?? FolderHelper.Root; } diff --git a/Rdmp.Core/Curation/Data/ColumnInfo.cs b/Rdmp.Core/Curation/Data/ColumnInfo.cs index c0f192138f..71205d23fc 100644 --- a/Rdmp.Core/Curation/Data/ColumnInfo.cs +++ b/Rdmp.Core/Curation/Data/ColumnInfo.cs @@ -61,7 +61,7 @@ public class ColumnInfo : DatabaseEntity, IComparable, IResolveDuplication, IHas public int TableInfo_ID { get => _tableInfoID; - private set => SetField(ref _tableInfoID, value); + set => SetField(ref _tableInfoID, value); } /// diff --git a/Rdmp.Core/Curation/Data/DataLoad/ILoadMetadata.cs b/Rdmp.Core/Curation/Data/DataLoad/ILoadMetadata.cs index d47eb0c1c9..c851546a97 100644 --- a/Rdmp.Core/Curation/Data/DataLoad/ILoadMetadata.cs +++ b/Rdmp.Core/Curation/Data/DataLoad/ILoadMetadata.cs @@ -29,10 +29,22 @@ public interface ILoadMetadata : INamed, ILoggedActivityRootObject ILoadProgress[] LoadProgresses { get; } /// - /// The root working directory for a load. Should have subdirectories like Data, Executables etc - /// For structured access to this use a new + /// Working Directory for Loading Data /// - string LocationOfFlatFiles { get; set; } + string LocationOfForLoadingDirectory { get; set; } + /// + /// Working Directory for archiving data + /// + string LocationOfForArchivingDirectory { get; set; } + /// + /// Working Directory for storing executalbes + /// + string LocationOfExecutablesDirectory { get; set; } + /// + /// Woring Directory for caching data + /// + string LocationOfCacheDirectory { get; set; } + /// /// Set to true to ignore the requirement for live tables to need the backup archive trigger diff --git a/Rdmp.Core/Curation/Data/DataLoad/LoadMetadata.cs b/Rdmp.Core/Curation/Data/DataLoad/LoadMetadata.cs index d76e233a30..6ad00f2319 100644 --- a/Rdmp.Core/Curation/Data/DataLoad/LoadMetadata.cs +++ b/Rdmp.Core/Curation/Data/DataLoad/LoadMetadata.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Data.Common; +using System.IO; using System.Linq; using FAnsi.Discovery; using FAnsi.Discovery.QuerySyntax; @@ -47,7 +48,10 @@ public class LoadMetadata : DatabaseEntity, ILoadMetadata, IHasDependencies, IHa { #region Database Properties - private string _locationOfFlatFiles; + private string _locationOfForLoadingDirectory; + private string _locationOfForArchivingDirectory; + private string _locationOfExecutablesDirectory; + private string _locationOfCacheDirectory; private string _anonymisationEngineClass; private string _name; private string _description; @@ -57,12 +61,54 @@ public class LoadMetadata : DatabaseEntity, ILoadMetadata, IHasDependencies, IHa private string _folder; private DateTime? _lastLoadTime; - /// - [AdjustableLocation] - public string LocationOfFlatFiles + + public string DefaultForLoadingPath = Path.Combine("Data", "ForLoading"); + public string DefaultForArchivingPath = Path.Combine("Data", "ForArchiving"); + public string DefaultExecutablesPath = "Executables"; + public string DefaultCachePath = Path.Combine("Data", "Cache"); + + public DirectoryInfo GetRootDirectory() + { + if (!string.IsNullOrWhiteSpace(_locationOfForLoadingDirectory) && !string.IsNullOrWhiteSpace(_locationOfForArchivingDirectory) && !string.IsNullOrWhiteSpace(_locationOfExecutablesDirectory) && !string.IsNullOrWhiteSpace(_locationOfCacheDirectory)) + { + var forLoadingRoot = _locationOfForLoadingDirectory.Replace(DefaultForLoadingPath, ""); + var forArchivingRoot = _locationOfForArchivingDirectory.Replace(DefaultForArchivingPath, ""); + var forExecutablesRoot = _locationOfExecutablesDirectory.Replace(DefaultExecutablesPath, ""); + var forCacheRoot = _locationOfCacheDirectory.Replace(DefaultCachePath, ""); + if (forLoadingRoot == forArchivingRoot && forExecutablesRoot == forCacheRoot && forArchivingRoot == forExecutablesRoot) + { + return new DirectoryInfo(forLoadingRoot); + } + } + return null; + } + + /// + public string LocationOfForLoadingDirectory { - get => _locationOfFlatFiles; - set => SetField(ref _locationOfFlatFiles, value); + get => _locationOfForLoadingDirectory; + set => SetField(ref _locationOfForLoadingDirectory, value); + } + + /// + public string LocationOfForArchivingDirectory + { + get => _locationOfForArchivingDirectory; + set => SetField(ref _locationOfForArchivingDirectory, value); + } + + /// + public string LocationOfExecutablesDirectory + { + get => _locationOfExecutablesDirectory; + set => SetField(ref _locationOfExecutablesDirectory, value); + } + + /// + public string LocationOfCacheDirectory + { + get => _locationOfCacheDirectory; + set => SetField(ref _locationOfCacheDirectory, value); } /// @@ -193,7 +239,10 @@ public LoadMetadata(ICatalogueRepository repository, string name = null) internal LoadMetadata(ICatalogueRepository repository, DbDataReader r) : base(repository, r) { - LocationOfFlatFiles = r["LocationOfFlatFiles"].ToString(); + LocationOfForLoadingDirectory = r["LocationOfForLoadingDirectory"].ToString(); + LocationOfForArchivingDirectory = r["LocationOfForArchivingDirectory"].ToString(); + LocationOfExecutablesDirectory = r["LocationOfExecutablesDirectory"].ToString(); + LocationOfCacheDirectory = r["LocationOfCacheDirectory"].ToString(); Name = r["Name"] as string; AnonymisationEngineClass = r["AnonymisationEngineClass"].ToString(); Name = r["Name"].ToString(); @@ -202,7 +251,7 @@ internal LoadMetadata(ICatalogueRepository repository, DbDataReader r) OverrideRAWServer_ID = ObjectToNullableInt(r["OverrideRAWServer_ID"]); IgnoreTrigger = ObjectToNullableBool(r["IgnoreTrigger"]) ?? false; Folder = r["Folder"] as string ?? FolderHelper.Root; - LastLoadTime = string.IsNullOrWhiteSpace(r["LastLoadTime"].ToString()) ?null: DateTime.Parse(r["LastLoadTime"].ToString()); + LastLoadTime = string.IsNullOrWhiteSpace(r["LastLoadTime"].ToString()) ? null : DateTime.Parse(r["LastLoadTime"].ToString()); } internal LoadMetadata(ShareManager shareManager, ShareDefinition shareDefinition) : base() @@ -210,14 +259,15 @@ internal LoadMetadata(ShareManager shareManager, ShareDefinition shareDefinition shareManager.UpsertAndHydrate(this, shareDefinition); } - public void LinkToCatalogue(ICatalogue catalogue) { - var linkage = new LoadMetadataCatalogueLinkage(CatalogueRepository,this,catalogue); + public void LinkToCatalogue(ICatalogue catalogue) + { + var linkage = new LoadMetadataCatalogueLinkage(CatalogueRepository, this, catalogue); linkage.SaveToDatabase(); } public void UnlinkFromCatalogue(ICatalogue catalogue) { - foreach(var l in CatalogueRepository.GetAllObjects().Where(link => link.CatalogueID == catalogue.ID && link.LoadMetadataID == this.ID)) + foreach (var l in CatalogueRepository.GetAllObjects().Where(link => link.CatalogueID == catalogue.ID && link.LoadMetadataID == this.ID)) { l.DeleteInDatabase(); } @@ -239,7 +289,8 @@ public override void DeleteInDatabase() public override string ToString() => Name; /// - public IEnumerable GetAllCatalogues() { + public IEnumerable GetAllCatalogues() + { var catalogueLinkIDs = Repository.GetAllObjectsWhere("LoadMetadataID", ID).Select(l => l.CatalogueID); return Repository.GetAllObjects().Where(cat => catalogueLinkIDs.Contains(cat.ID)); } diff --git a/Rdmp.Core/Curation/Data/ExtractionInformation.cs b/Rdmp.Core/Curation/Data/ExtractionInformation.cs index 705cfc42d8..c0cc5911ff 100644 --- a/Rdmp.Core/Curation/Data/ExtractionInformation.cs +++ b/Rdmp.Core/Curation/Data/ExtractionInformation.cs @@ -42,6 +42,7 @@ public class ExtractionInformation : ConcreteColumn, IHasDependencies, IInjectKn private int _catalogueItemID; private ExtractionCategory _extractionCategory; + private bool _groupBy = true; /// /// The virtual column (description, name etc) to which this provides extraction SELECT SQL for. @@ -52,6 +53,15 @@ public int CatalogueItem_ID set => SetField(ref _catalogueItemID, value); } + /// + /// If the should be used in any GROUP BY clause + /// + public bool GroupBy + { + get => _groupBy; + set => SetField(ref _groupBy, value); + } + /// /// Which governance conditions is this column/transform extractable under (e.g. Core, SpecialApprovalRequired etc) /// @@ -174,6 +184,7 @@ internal ExtractionInformation(ICatalogueRepository repository, DbDataReader r) IsExtractionIdentifier = (bool)r["IsExtractionIdentifier"]; IsPrimaryKey = (bool)r["IsPrimaryKey"]; CatalogueItem_ID = (int)r["CatalogueItem_ID"]; + GroupBy = int.Parse(r["GroupBy"].ToString()) == 1; ClearAllInjections(); } diff --git a/Rdmp.Core/Curation/Data/LoadModuleAssembly.cs b/Rdmp.Core/Curation/Data/LoadModuleAssembly.cs index 26316f1f60..3c328ef9bb 100644 --- a/Rdmp.Core/Curation/Data/LoadModuleAssembly.cs +++ b/Rdmp.Core/Curation/Data/LoadModuleAssembly.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Xml; using ICSharpCode.SharpZipLib.Zip; using Rdmp.Core.ReusableLibraryCode.Checks; @@ -31,12 +32,98 @@ private LoadModuleAssembly(FileInfo file) _file = file; } + + private static bool IsReleaseCandidcate(string filename) + { + return filename.Split('.')[^2].Contains("-rc"); + } + + private static int GetReleaseCandidateId(string filename) + { + //expects the rc to be something like My.Plugin.3.0.0-rc2 + int.TryParse(filename.Split("-rc")[1].Split('.')[0], out var result); + return result; + } + + private static Dictionary HandlePluginVersioning() + { + var PluginVersionLookup = new Dictionary(); + var PluginPathLookup = new Dictionary(); + var pluginFiles = Directory.EnumerateFiles(AppDomain.CurrentDomain.BaseDirectory, "*.rdmp"); + foreach (var pluginFile in pluginFiles) + { + using FileStream fs = new(pluginFile, FileMode.Open); + using ZipFile zip = new(fs); + foreach (ZipEntry ze in zip) + { + if (ze.Name.Contains(".nuspec")) + { + using StreamReader sr = new StreamReader(zip.GetInputStream(ze.ZipFileIndex)); + var content = sr.ReadToEnd(); + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(content); + + var name = xmlDoc.GetElementsByTagName("id").Item(0).InnerText; + var version = xmlDoc.GetElementsByTagName("version").Item(0).InnerText; + if (PluginVersionLookup.TryGetValue(name, out var currentVersion)) + { + //already found a version + var result = new Version(currentVersion).CompareTo(new Version(version)); + if (result < 0) + { + //this version is the latest version + PluginVersionLookup[name] = version; + PluginPathLookup[name] = pluginFile; + } + if(result == 0) + { + //check for RC in the name + var currentIsReleaseCandidate = IsReleaseCandidcate(PluginPathLookup[name]); + var newIsReleaseCandidate = IsReleaseCandidcate(pluginFile); + if(newIsReleaseCandidate && currentIsReleaseCandidate) + { + var newIsLatest = GetReleaseCandidateId(pluginFile) > GetReleaseCandidateId(PluginPathLookup[name]); + if (newIsLatest) + { + PluginVersionLookup[name] = version; + PluginPathLookup[name] = pluginFile; + } + } else if (newIsReleaseCandidate) + { + //new version is the latest + PluginVersionLookup[name] = version; + PluginPathLookup[name] = pluginFile; + } + } + } + else + { + PluginVersionLookup[name] = version; + PluginPathLookup[name] = pluginFile; + } + break; + } + + } + + } + + return PluginPathLookup; + } + /// /// List the plugin files to load /// /// internal static IEnumerable PluginFiles() { + if (Directory.EnumerateFiles(AppDomain.CurrentDomain.BaseDirectory, "*.rdmp").Count() > 0) + { + //use the modern .RDMP plugins + var plugins = HandlePluginVersioning(); + return plugins.Values.ToArray(); + + } return File.Exists(PluginsList) ? File.ReadAllLines(PluginsList) .Select(static name => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, name)) diff --git a/Rdmp.Core/Curation/KeywordHelp.txt b/Rdmp.Core/Curation/KeywordHelp.txt index 83b0f1437a..e9cc5a02bd 100644 --- a/Rdmp.Core/Curation/KeywordHelp.txt +++ b/Rdmp.Core/Curation/KeywordHelp.txt @@ -94,3 +94,4 @@ FK_RowState_Evaluation: Ensures that when a DQE Evaluation is deleted all child FK_PeriodicityState_Evaluation: Ensures that when a DQE Evaluation is deleted all child database objects are also deleted FK_DQEGraphAnnotation_Evaluation: Ensures that when a DQE Evaluation is deleted all child database objects are also deleted FK_Memento_Commit: Ensures that when a Commit is deleted all its child database objects are also deleted +UNIQUE_SettingKey: Ensures the uniqueness of key values in the setting table \ No newline at end of file diff --git a/Rdmp.Core/Curation/LoadDirectory.cs b/Rdmp.Core/Curation/LoadDirectory.cs index c9419bbe5a..8b678e2b71 100644 --- a/Rdmp.Core/Curation/LoadDirectory.cs +++ b/Rdmp.Core/Curation/LoadDirectory.cs @@ -51,13 +51,16 @@ public class LoadDirectory : ILoadDirectory /// Use static method if you want to create a new folder hierarchy on disk /// /// - public LoadDirectory(string rootPath) + /// + public LoadDirectory(string rootPath, bool validate=true) { if (string.IsNullOrWhiteSpace(rootPath)) throw new Exception("Root path was blank, there is no LoadDirectory path specified?"); RootPath = new DirectoryInfo(rootPath); + if (!validate) return; + if (RootPath.Name.Equals("Data", StringComparison.CurrentCultureIgnoreCase)) throw new ArgumentException("LoadDirectory should be passed the root folder, not the Data folder"); @@ -66,13 +69,22 @@ public LoadDirectory(string rootPath) if (!DataPath.Exists) throw new DirectoryNotFoundException( $"Could not find directory '{DataPath.FullName}', every LoadDirectory must have a Data folder, the root folder was:{RootPath}"); - ForLoading = FindFolderInPathOrThrow(DataPath, "ForLoading"); ForArchiving = FindFolderInPathOrThrow(DataPath, "ForArchiving"); ExecutablesPath = FindFolderInPathOrThrow(RootPath, "Executables"); Cache = FindFolderInPath(DataPath, "Cache"); } + public LoadDirectory(string ForLoadingPath, string ForArchivingPath, string ExecutablesPathString, string CachePath) + { + if (string.IsNullOrWhiteSpace(ForLoadingPath) || string.IsNullOrWhiteSpace(ForArchivingPath) || string.IsNullOrWhiteSpace(ExecutablesPathString) || string.IsNullOrWhiteSpace(CachePath)) + throw new Exception($"One if the LoadDirectory Paths was blank. ForLoading: {ForLoading}. ForArchiving: {ForArchivingPath}. Cache: {CachePath}. Extractables:{ExecutablesPath}"); + ForLoading = new DirectoryInfo(ForLoadingPath); + ForArchiving = new DirectoryInfo(ForArchivingPath); + ExecutablesPath =new DirectoryInfo(ExecutablesPathString); + Cache = new DirectoryInfo(CachePath); + } + private static DirectoryInfo FindFolderInPath(DirectoryInfo path, string folderName) => path.EnumerateDirectories(folderName, SearchOption.TopDirectoryOnly).FirstOrDefault(); diff --git a/Rdmp.Core/DataExport/DataExtraction/Pipeline/Destinations/ExecuteFullExtractionToDatabaseMSSql.cs b/Rdmp.Core/DataExport/DataExtraction/Pipeline/Destinations/ExecuteFullExtractionToDatabaseMSSql.cs index 9a47c77c2c..cd70ce7dec 100644 --- a/Rdmp.Core/DataExport/DataExtraction/Pipeline/Destinations/ExecuteFullExtractionToDatabaseMSSql.cs +++ b/Rdmp.Core/DataExport/DataExtraction/Pipeline/Destinations/ExecuteFullExtractionToDatabaseMSSql.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -19,6 +19,7 @@ using Rdmp.Core.DataExport.DataRelease.Potential; using Rdmp.Core.DataFlowPipeline; using Rdmp.Core.DataLoad.Engine.Pipeline.Destinations; +using Rdmp.Core.Logging; using Rdmp.Core.MapsDirectlyToDatabaseTable; using Rdmp.Core.QueryBuilding; using Rdmp.Core.Repositories; @@ -82,6 +83,31 @@ You must have either $a or $d DefaultValue = true)] public bool MakeFinalTableDistinctWhenBatchResuming { get; set; } = true; + + [DemandsInitialization("If this extraction has already been run, it will append the extraction data into the database. There is no duplication protection with this functionality.")] + public bool AppendDataIfTableExists { get; set; } = false; + + [DemandsInitialization("If checked, a column names 'extraction_timestamp' will be included in the extraction that denotes the time the record was added to the extraction.")] + public bool IncludeTimeStamp { get; set; } = false; + + + [DemandsInitialization("If chekced, indexed will be created using the primary keys specified")] + public bool IndexTables { get; set; } = true; + + [DemandsInitialization(@"How do you want to name the created index, use the following tokens if you need them: + $p - Project Name ('e.g. My Project') + $n - Project Number (e.g. 234) + $c - Configuration Name (e.g. 'Cases') + $d - Dataset name (e.g. 'Prescribing') + $a - Dataset acronym (e.g. 'Presc') + + You must have either $a or $d + ",DefaultValue = "Index_$c_$d")] + public string IndexNamingPattern { get; set; } + + [DemandsInitialization("An optional list of columns to index on e.g \"Column1, Column2\"")] + public string UserDefinedIndex { get; set; } + private DiscoveredDatabase _destinationDatabase; private DataTableUploadDestination _destination; @@ -186,13 +212,19 @@ private DataTableUploadDestination PrepareDestination(IDataLoadEventListener lis "Failed to inspect destination for already existing datatables", e)); } - _destination = new DataTableUploadDestination(); + _destination = new DataTableUploadDestination(((IExtractDatasetCommand)_request).ExtractableCohort.ExternalCohortTable); PrimeDestinationTypesBasedOnCatalogueTypes(listener, toProcess); _destination.AllowResizingColumnsAtUploadTime = true; _destination.AlterTimeout = AlterTimeout; - + _destination.AppendDataIfTableExists = AppendDataIfTableExists; + _destination.IncludeTimeStamp = IncludeTimeStamp; + _destination.UseTrigger = AppendDataIfTableExists; + _destination.IndexTables = IndexTables; + _destination.IndexTableName = GetIndexName(); + if (UserDefinedIndex is not null) + _destination.UserDefinedIndexes = UserDefinedIndex.Split(',').Select(i => i.Trim()).ToList(); _destination.PreInitialize(_destinationDatabase, listener); return _destination; @@ -208,7 +240,7 @@ private void PrimeDestinationTypesBasedOnCatalogueTypes(IDataLoadEventListener l //for every extractable column in the Catalogue foreach (var extractionInformation in datasetCommand.ColumnsToExtract.OfType() .Select(ec => - ec.CatalogueExtractionInformation)) //.GetAllExtractionInformation(ExtractionCategory.Any)) + ec.CatalogueExtractionInformation)) { if (extractionInformation == null) continue; @@ -258,6 +290,7 @@ private void PrimeDestinationTypesBasedOnCatalogueTypes(IDataLoadEventListener l $"Set Type for {columnName} to {destinationType} (IsPrimaryKey={(addedType.IsPrimaryKey ? "true" : "false")}) to match the source table")); } + foreach (var sub in datasetCommand.QueryBuilder.SelectColumns.Select(static sc => sc.IColumn) .OfType()) { @@ -288,6 +321,29 @@ private string GetDestinationDatabaseType(ConcreteColumn col) return col.ColumnInfo.Data_type; } + private string GetIndexName() + { + string indexName = IndexNamingPattern; + var project = _request.Configuration.Project; + indexName = indexName.Replace("$p", project.Name); + indexName = indexName.Replace("$n", project.ProjectNumber.ToString()); + indexName = indexName.Replace("$c", _request.Configuration.Name); + if (_request is ExtractDatasetCommand extractDatasetCommand) + { + indexName = indexName.Replace("$d", extractDatasetCommand.DatasetBundle.DataSet.Catalogue.Name); + indexName = indexName.Replace("$a", extractDatasetCommand.DatasetBundle.DataSet.Catalogue.Acronym); + } + + if (_request is ExtractGlobalsCommand) + { + indexName = indexName.Replace("$d", ExtractionDirectory.GLOBALS_DATA_NAME); + indexName = indexName.Replace("$a", "G"); + } + + + return indexName.Replace(" ",""); + } + private string GetTableName(string suffix = null) { string tblName; @@ -645,8 +701,8 @@ public override void Check(ICheckNotifier notifier) return; } - // if the expected table exists and we are not doing a batch resume - if (tables.Any(t => t.GetRuntimeName().Equals(tableName)) && !_request.IsBatchResume) + // if the expected table exists and we are not doing a batch resume or allowing data appending + if (tables.Any(t => t.GetRuntimeName().Equals(tableName)) && !_request.IsBatchResume && !AppendDataIfTableExists) notifier.OnCheckPerformed(new CheckEventArgs(ErrorCodes.ExistingExtractionTableInDatabase, tableName, database)); } diff --git a/Rdmp.Core/DataExport/DataExtraction/Pipeline/Sources/ExecuteDatasetExtractionSource.cs b/Rdmp.Core/DataExport/DataExtraction/Pipeline/Sources/ExecuteDatasetExtractionSource.cs index b823562fd2..f21f4da12a 100644 --- a/Rdmp.Core/DataExport/DataExtraction/Pipeline/Sources/ExecuteDatasetExtractionSource.cs +++ b/Rdmp.Core/DataExport/DataExtraction/Pipeline/Sources/ExecuteDatasetExtractionSource.cs @@ -317,10 +317,13 @@ public virtual DataTable GetChunk(IDataLoadEventListener listener, GracefulCance _timeSpentBuckettingDates.Stop(); _timeSpentCalculatingDISTINCT.Start(); + var pks = new List(); + //record unique release identifiers found if (includesReleaseIdentifier) - foreach (var idx in _extractionIdentifiersidx) + foreach (var idx in _extractionIdentifiersidx.Distinct().ToList()) { + pks.Add(chunk.Columns[idx]); foreach (DataRow r in chunk.Rows) { if (r[idx] == DBNull.Value) @@ -340,6 +343,11 @@ public virtual DataTable GetChunk(IDataLoadEventListener listener, GracefulCance } _timeSpentCalculatingDISTINCT.Stop(); + foreach (string name in Request.ColumnsToExtract.Where(c => ((ExtractableColumn)(c)).CatalogueExtractionInformation.IsPrimaryKey).Select(column => ((ExtractableColumn)column).CatalogueExtractionInformation.ToString())) + { + pks.Add(chunk.Columns[name]); + } + chunk.PrimaryKey = pks.ToArray(); return chunk; } diff --git a/Rdmp.Core/DataLoad/Engine/Job/JobFactory.cs b/Rdmp.Core/DataLoad/Engine/Job/JobFactory.cs index 381b9bc7ac..8cd0cb83be 100644 --- a/Rdmp.Core/DataLoad/Engine/Job/JobFactory.cs +++ b/Rdmp.Core/DataLoad/Engine/Job/JobFactory.cs @@ -31,8 +31,10 @@ public IDataLoadJob Create(IRDMPPlatformRepositoryServiceLocator repositoryLocat HICDatabaseConfiguration configuration) { var description = _loadMetadata.Name; - var LoadDirectory = new LoadDirectory(_loadMetadata.LocationOfFlatFiles); - return new DataLoadJob(repositoryLocator, description, _logManager, _loadMetadata, LoadDirectory, listener, + LoadDirectory loadDirectory; + loadDirectory = new LoadDirectory(_loadMetadata.LocationOfForLoadingDirectory, _loadMetadata.LocationOfForArchivingDirectory, _loadMetadata.LocationOfExecutablesDirectory, _loadMetadata.LocationOfCacheDirectory); + + return new DataLoadJob(repositoryLocator, description, _logManager, _loadMetadata, loadDirectory, listener, configuration); } } \ No newline at end of file diff --git a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs index 881c3277e0..69f3f7bd4a 100644 --- a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs +++ b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs @@ -61,7 +61,7 @@ protected override ScheduledDataLoadJob CreateImpl(IRDMPPlatformRepositoryServic if (!datesToRetrieve.Any()) return null; - var LoadDirectory = new LoadDirectory(LoadMetadata.LocationOfFlatFiles); + var LoadDirectory = new LoadDirectory(LoadMetadata.LocationOfForLoadingDirectory, LoadMetadata.LocationOfForArchivingDirectory, LoadMetadata.LocationOfExecutablesDirectory, LoadMetadata.LocationOfCacheDirectory); var job = new ScheduledDataLoadJob(repositoryLocator, JobDescription, LogManager, LoadMetadata, LoadDirectory, listener, configuration) { diff --git a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs index 9260b1a37c..6986be1874 100644 --- a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs +++ b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs @@ -9,6 +9,7 @@ using Rdmp.Core.Curation.Data.DataLoad; using Rdmp.Core.DataLoad.Engine.DatabaseManagement.EntityNaming; using Rdmp.Core.Logging; +using Rdmp.Core.Providers.Nodes.LoadMetadataNodes; using Rdmp.Core.Repositories; using Rdmp.Core.ReusableLibraryCode.Progress; @@ -38,8 +39,11 @@ public override bool HasJobs() => protected override ScheduledDataLoadJob CreateImpl(IRDMPPlatformRepositoryServiceLocator repositoryLocator, IDataLoadEventListener listener, HICDatabaseConfiguration configuration) { - var LoadDirectory = new LoadDirectory(LoadMetadata.LocationOfFlatFiles); - return new ScheduledDataLoadJob(repositoryLocator, JobDescription, LogManager, LoadMetadata, LoadDirectory, + LoadDirectory loadDirectory; + + loadDirectory = new LoadDirectory(LoadMetadata.LocationOfForLoadingDirectory, LoadMetadata.LocationOfForArchivingDirectory, LoadMetadata.LocationOfExecutablesDirectory, LoadMetadata.LocationOfCacheDirectory); + + return new ScheduledDataLoadJob(repositoryLocator, JobDescription, LogManager, LoadMetadata, loadDirectory, listener, configuration) { LoadProgress = _loadProgress, diff --git a/Rdmp.Core/DataLoad/Engine/LoadExecution/Components/Arguments/LoadArgsDictionary.cs b/Rdmp.Core/DataLoad/Engine/LoadExecution/Components/Arguments/LoadArgsDictionary.cs index 2aeb173f4c..69dc96ca9b 100644 --- a/Rdmp.Core/DataLoad/Engine/LoadExecution/Components/Arguments/LoadArgsDictionary.cs +++ b/Rdmp.Core/DataLoad/Engine/LoadExecution/Components/Arguments/LoadArgsDictionary.cs @@ -26,9 +26,9 @@ public class LoadArgsDictionary public LoadArgsDictionary(ILoadMetadata loadMetadata, StandardDatabaseHelper dbDeployInfo) { - if (string.IsNullOrWhiteSpace(loadMetadata.LocationOfFlatFiles)) + if (string.IsNullOrWhiteSpace(loadMetadata.LocationOfForLoadingDirectory)) throw new Exception( - $@"No Project Directory (LocationOfFlatFiles) has been configured on LoadMetadata {loadMetadata.Name}"); + $@"No Project Directory has been configured on LoadMetadata {loadMetadata.Name}"); _dbDeployInfo = dbDeployInfo; _loadMetadata = loadMetadata; @@ -40,9 +40,14 @@ public LoadArgsDictionary(ILoadMetadata loadMetadata, StandardDatabaseHelper dbD protected IStageArgs CreateLoadArgs(LoadStage loadStage) { - return - new StageArgs(loadStage, - _dbDeployInfo[loadStage.ToLoadBubble()] - , new LoadDirectory(_loadMetadata.LocationOfFlatFiles.TrimEnd(new[] { '\\' }))); + return new StageArgs(loadStage, + _dbDeployInfo[loadStage.ToLoadBubble()] + , new LoadDirectory( + _loadMetadata.LocationOfForLoadingDirectory.TrimEnd(new[] { '\\' }), + _loadMetadata.LocationOfForArchivingDirectory.TrimEnd(new[] { '\\' }), + _loadMetadata.LocationOfExecutablesDirectory.TrimEnd(new[] { '\\' }), + _loadMetadata.LocationOfCacheDirectory.TrimEnd(new[] { '\\' }) + ) + ); } } \ No newline at end of file diff --git a/Rdmp.Core/DataLoad/Engine/Pipeline/Destinations/DataTableUploadDestination.cs b/Rdmp.Core/DataLoad/Engine/Pipeline/Destinations/DataTableUploadDestination.cs index 9a7c1a5e68..daa0fb47f1 100644 --- a/Rdmp.Core/DataLoad/Engine/Pipeline/Destinations/DataTableUploadDestination.cs +++ b/Rdmp.Core/DataLoad/Engine/Pipeline/Destinations/DataTableUploadDestination.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -15,8 +15,11 @@ using FAnsi.Discovery; using FAnsi.Discovery.TableCreation; using Rdmp.Core.Curation.Data; +using Rdmp.Core.DataExport.Data; using Rdmp.Core.DataFlowPipeline; using Rdmp.Core.DataFlowPipeline.Requirements; +using Rdmp.Core.DataLoad.Triggers.Implementations; +using Rdmp.Core.DataLoad.Triggers; using Rdmp.Core.Logging; using Rdmp.Core.Logging.Listeners; using Rdmp.Core.Repositories.Construction; @@ -25,6 +28,11 @@ using Rdmp.Core.ReusableLibraryCode.DataAccess; using Rdmp.Core.ReusableLibraryCode.Progress; using TypeGuesser; +using FAnsi; +using Terminal.Gui; +using TB.ComponentModel; +using Npgsql; +using MathNet.Numerics.LinearAlgebra; namespace Rdmp.Core.DataLoad.Engine.Pipeline.Destinations; @@ -65,6 +73,10 @@ public class DataTableUploadDestination : IPluginDataFlowComponent, I TypeOf = typeof(IDatabaseColumnRequestAdjuster))] public Type Adjuster { get; set; } + public bool AppendDataIfTableExists { get; set; } + + public bool IncludeTimeStamp { get; set; } + private CultureInfo _culture; [DemandsInitialization("The culture to use for uploading (determines date format etc)")] @@ -81,6 +93,11 @@ public CultureInfo Culture /// the table already existed e.g. data was simply added /// public bool CreatedTable { get; private set; } + public bool UseTrigger { get; set; } = false; + + public bool IndexTables { get; set; } = false; + public string IndexTableName { get; set; } + public List UserDefinedIndexes { get; set; } = new(); private IBulkCopy _bulkcopy; private int _affectedRows; @@ -103,6 +120,9 @@ public CultureInfo Culture private bool _firstTime = true; private HashSet _primaryKey = new(StringComparer.CurrentCultureIgnoreCase); private DiscoveredTable _discoveredTable; + private readonly string _extractionTimeStamp = "extraction_timestamp"; + + private readonly IExternalCohortTable _externalCohortTable; //All column values sent to server so far private Dictionary _dataTypeDictionary; @@ -118,12 +138,58 @@ public DataTableUploadDestination() ExplicitTypes = new List(); } + public DataTableUploadDestination(IExternalCohortTable externalCohortTable) + { + ExplicitTypes = new List(); + _externalCohortTable = externalCohortTable; + } + + private static object[] FilterOutItemAtIndex(object[] itemArray, int[] indexes) + { + if (indexes.Length == 0) return itemArray; + return itemArray.Where((source, idx) => !indexes.Contains(idx)).ToArray(); + } + private string GetPKValue(DataColumn pkColumn, DataRow row) + { + var pkName = pkColumn.ColumnName; + var value = row[pkName]; + if (_externalCohortTable is not null) + { + var privateIdentifierField = _externalCohortTable.PrivateIdentifierField.Split('.').Last()[1..^1];//remove the "[]" from the identifier field + var releaseIdentifierField = _externalCohortTable.ReleaseIdentifierField.Split('.').Last()[1..^1];//remove the "[]" from the identifier field + if (pkName == releaseIdentifierField) + { + //going to have to look up the previous relaseID to match + DiscoveredTable cohortTable = _externalCohortTable.DiscoverCohortTable(); + using var lookupDT = cohortTable.GetDataTable(); + var releaseIdIndex = lookupDT.Columns.IndexOf(releaseIdentifierField); + var privateIdIndex = lookupDT.Columns.IndexOf(privateIdentifierField); + var foundRow = lookupDT.Rows.Cast().Where(r => r.ItemArray[releaseIdIndex].ToString() == value.ToString()).LastOrDefault(); + if (foundRow is not null) + { + var originalValue = foundRow.ItemArray[privateIdIndex]; + var existingIDsforReleaseID = lookupDT.Rows.Cast().Where(r => r.ItemArray[privateIdIndex].ToString() == originalValue.ToString()).Select(s => s.ItemArray[releaseIdIndex].ToString()); + if (existingIDsforReleaseID.Count() > 0) + { + //we don't know what the current releae ID is ( there may be ones from multiple cohorts) + var ids = existingIDsforReleaseID.Select(id => $"'{id}'"); + return $"{pkName} in ({string.Join(',', ids)})"; + } + } + } + } + return $"{pkName} = '{value}'"; + } + + public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener listener, GracefulCancellationToken cancellationToken) { if (toProcess == null) return null; + var rowsToModify = new List(); + var pkColumns = toProcess.PrimaryKey; RemoveInvalidCharactersInSchema(toProcess); IDatabaseColumnRequestAdjuster adjuster = null; @@ -153,6 +219,11 @@ public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener StartAuditIfExists(TargetTableName); + if (IncludeTimeStamp) + { + AddTimeStampToExtractionData(toProcess); + } + if (_loggingDatabaseListener != null) listener = new ForkDataLoadEventListener(listener, _loggingDatabaseListener); @@ -178,7 +249,7 @@ public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener if (_discoveredTable.IsEmpty()) listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning, $"Found table {TargetTableName} already, normally this would forbid you from loading it (data duplication / no primary key etc) but it is empty so we are happy to load it, it will not be created")); - else + else if (!AppendDataIfTableExists) throw new Exception( $"There is already a table called {TargetTableName} at the destination {_database}"); @@ -209,19 +280,156 @@ public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener _managedConnection = _server.BeginNewTransactedConnection(); _bulkcopy = _discoveredTable.BeginBulkInsert(Culture, _managedConnection.ManagedTransaction); - _firstTime = false; } + if (IncludeTimeStamp && !_discoveredTable.DiscoverColumns().Where(c => c.GetRuntimeName() == _extractionTimeStamp).Any()) + { + _discoveredTable.AddColumn(_extractionTimeStamp, new DatabaseTypeRequest(typeof(DateTime)), true, 30000); + } + if (IndexTables) + { + var indexes = UserDefinedIndexes.Count != 0 ? UserDefinedIndexes : pkColumns.Select(c => c.ColumnName); + try + { + _discoveredTable.CreateIndex(IndexTableName, _discoveredTable.DiscoverColumns().Where(c => indexes.Contains(c.GetRuntimeName())).ToArray()); + } + catch (Exception e) + { + //We only warn about not creating the index, as it's not critical + listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning, e.Message)); + } + } + if (UseTrigger && pkColumns.Length > 0) + { + if (listener.GetType() == typeof(ForkDataLoadEventListener)) //need to add special fields to the datatable if we are logging to a database + { + var job = (ForkDataLoadEventListener)listener; + var listeners = job.GetToLoggingDatabaseDataLoadEventListenersIfany(); + foreach (var dleListener in listeners) + { + IDataLoadInfo dataLoadInfo = dleListener.DataLoadInfo; + DataColumn newColumn = new(SpecialFieldNames.DataLoadRunID, typeof(int)) + { + DefaultValue = dataLoadInfo.ID + }; + try + { + _discoveredTable.DiscoverColumn(SpecialFieldNames.DataLoadRunID); + } + catch (Exception) + { + _discoveredTable.AddColumn(SpecialFieldNames.DataLoadRunID, new DatabaseTypeRequest(typeof(int)), true, 30000); + + } + if (!toProcess.Columns.Contains(SpecialFieldNames.DataLoadRunID)) + toProcess.Columns.Add(newColumn); + foreach (DataRow dr in toProcess.Rows) + dr[SpecialFieldNames.DataLoadRunID] = dataLoadInfo.ID; + + } + } + } + try { if (AllowResizingColumnsAtUploadTime && !CreatedTable) ResizeColumnsIfRequired(toProcess, listener); - //push the data swTimeSpentWriting.Start(); + if (AppendDataIfTableExists && pkColumns.Length > 0) //assumes columns are the same + { + //drop any pk clashes + var existingData = _discoveredTable.GetDataTable(); + var rowsToDelete = new List(); + var releaseIdentifier = _externalCohortTable is not null ? _externalCohortTable.ReleaseIdentifierField.Split('.').Last()[1..^1] : "ReleaseId"; + int[] toProcessIgnoreColumns = [toProcess.Columns.IndexOf(SpecialFieldNames.DataLoadRunID), toProcess.Columns.IndexOf(releaseIdentifier)]; + int[] existingDataIgnoreColumns = [existingData.Columns.IndexOf(SpecialFieldNames.DataLoadRunID), existingData.Columns.IndexOf(releaseIdentifier), existingData.Columns.IndexOf(SpecialFieldNames.ValidFrom)]; + foreach (DataRow row in toProcess.Rows) + { + + foreach (DataColumn pkCol in pkColumns) + { + bool clash = false; + if (_externalCohortTable is not null && pkCol.ColumnName == _externalCohortTable.ReleaseIdentifierField.Split('.').Last()[1..^1]) + { + // If it's a cohort release identifier + // look up the original value and check we've not already extected the same value under a different release ID + var privateIdentifierField = _externalCohortTable.PrivateIdentifierField.Split('.').Last()[1..^1];//remove the "[]" from the identifier field + var releaseIdentifierField = _externalCohortTable.ReleaseIdentifierField.Split('.').Last()[1..^1];//remove the "[]" from the identifier field + DiscoveredTable cohortTable = _externalCohortTable.DiscoverCohortTable(); + var lookupDT = cohortTable.GetDataTable(); + var releaseIdIndex = lookupDT.Columns.IndexOf(releaseIdentifierField); + var privateIdIndex = lookupDT.Columns.IndexOf(privateIdentifierField); + var foundRow = lookupDT.Rows.Cast().Where(r => r.ItemArray[releaseIdIndex].ToString() == row[pkCol.ColumnName].ToString()).FirstOrDefault(); + if (foundRow is not null) + { + var originalValue = foundRow.ItemArray[privateIdIndex]; + var existingIDsforReleaseID = lookupDT.Rows.Cast().Where(r => r.ItemArray[privateIdIndex].ToString() == originalValue.ToString()).Select(s => s.ItemArray[releaseIdIndex].ToString()); + clash = existingData.AsEnumerable().Any(r => existingIDsforReleaseID.Contains(r[pkCol.ColumnName].ToString())); + } + } + else + { + var val = row[pkCol.ColumnName]; + clash = existingData.AsEnumerable().Any(r => r[pkCol.ColumnName].ToString() == val.ToString()); + + } + if (clash && UseTrigger) + { + if (existingData.AsEnumerable().Any(r => FilterOutItemAtIndex(r.ItemArray, existingDataIgnoreColumns).ToList().SequenceEqual(FilterOutItemAtIndex(row.ItemArray, toProcessIgnoreColumns).ToList()))) //do we have to worry about special field? what if the load ids are different? + { + //the row is the exact same,so there is no clash + clash = false; + rowsToDelete.Add(row); + } + else //row needs updated, but only if we're tracking history + { + rowsToModify.Add(row);//need to know what releaseId to replace + break; + } + } + } + } + foreach (DataRow row in rowsToDelete.Distinct()) + toProcess.Rows.Remove(row); + + } - _affectedRows += _bulkcopy.Upload(toProcess); + + foreach (DataRow row in rowsToModify.Distinct()) + { + //replace existing + var args = new DatabaseOperationArgs(); + List columns = []; + foreach (DataColumn column in toProcess.Columns) + { + //if (!pkColumns.Contains(column)) + //{ + columns.Add(column.ColumnName); + //} + } + //need to check for removed column and null them out + var existingColumns = _discoveredTable.DiscoverColumns().Select(c => c.GetRuntimeName()); + var columnsThatPreviouslyExisted = existingColumns.Where(c => !pkColumns.Select(pk => pk.ColumnName).Contains(c) && !columns.Contains(c) && c != SpecialFieldNames.DataLoadRunID && c != SpecialFieldNames.ValidFrom); + var nullEntries = string.Join(" ,", columnsThatPreviouslyExisted.Select(c => $"{c} = NULL")); + var nullText = nullEntries.Length > 0 ? $" , {nullEntries}" : ""; + var columnString = string.Join(" , ", columns.Select(col => $"{col} = '{row[col]}'").ToList()); + var pkMatch = string.Join(" AND ", pkColumns.Select(pk => GetPKValue(pk, row)).ToList()); + var sql = $"update {_discoveredTable.GetFullyQualifiedName()} set {columnString} {nullText} where {pkMatch}"; + var cmd = _discoveredTable.GetCommand(sql, args.GetManagedConnection(_discoveredTable).Connection); + cmd.ExecuteNonQuery(); + } + + foreach (DataRow row in rowsToModify.Distinct()) + { + toProcess.Rows.Remove(row); + } + if (toProcess.Rows.Count == 0 && !rowsToModify.Any()) return null; + if (toProcess.Rows.Count > 0) + { + _affectedRows += _bulkcopy.Upload(toProcess); + } swTimeSpentWriting.Stop(); listener.OnProgress(this, @@ -243,6 +451,7 @@ public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener return null; } + private static void RemoveInvalidCharactersInSchema(DataTable toProcess) { var invalidSymbols = new[] { '.' }; @@ -279,6 +488,17 @@ private void ClearPrimaryKeyFromDataTableAndExplicitWriteTypes(DataTable toProce } } + + private void AddTimeStampToExtractionData(DataTable toProcess) + { + var timeStamp = DateTime.Now; + toProcess.Columns.Add(_extractionTimeStamp); + foreach (DataRow row in toProcess.Rows) + { + row[_extractionTimeStamp] = timeStamp; + } + } + private static void EnsureTableHasDataInIt(DataTable toProcess) { if (toProcess.Columns.Count == 0) @@ -319,9 +539,15 @@ private void ResizeColumnsIfRequired(DataTable toProcess, IDataLoadEventListener //see if any have changed foreach (DataColumn column in toProcess.Columns) { + if (column.ColumnName == _extractionTimeStamp && IncludeTimeStamp) + { + continue; //skip internally generated columns + } //get what is required for the current batch and the current type that is configured in the live table - var oldSqlType = oldTypes[column.ColumnName]; - var newSqlType = typeTranslater.GetSQLDBTypeForCSharpType(_dataTypeDictionary[column.ColumnName].Guess); + oldTypes.TryGetValue(column.ColumnName, out var oldSqlType); + _dataTypeDictionary.TryGetValue(column.ColumnName, out var knownType); + + var newSqlType = knownType is not null ? typeTranslater.GetSQLDBTypeForCSharpType(knownType.Guess) : null; var changesMade = false; @@ -445,6 +671,22 @@ public void Dispose(IDataLoadEventListener listener, Exception pipelineFailureEx _discoveredTable.CreatePrimaryKey(AlterTimeout, pkColumnsToCreate); } } + if (UseTrigger && _discoveredTable.DiscoverColumns().Where(col => col.IsPrimaryKey).Any()) //can't use triggers without a PK + { + + var factory = new TriggerImplementerFactory(_database.Server.DatabaseType); + var _triggerImplementer = factory.Create(_discoveredTable); + var currentStatus = _triggerImplementer.GetTriggerStatus(); + if (currentStatus == TriggerStatus.Missing) + try + { + _triggerImplementer.CreateTrigger(ThrowImmediatelyCheckNotifier.Quiet); + } + catch (Exception e) + { + listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning, e.Message)); + } + } EndAuditIfExists(); } diff --git a/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql b/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql index 03b9979115..7e4bc622d0 100644 --- a/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql +++ b/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql @@ -115,6 +115,7 @@ CREATE TABLE [dbo].[AggregateDimension]( [Alias] [varchar](100) NULL, [Order] [int] NOT NULL, [SoftwareVersion] [nvarchar](50) NOT NULL, + [GroupBy] [int] DEFAULT 1, CONSTRAINT [PK_AggregateDimension] PRIMARY KEY CLUSTERED ( [ID] ASC @@ -512,6 +513,7 @@ CREATE TABLE [dbo].[ExtractionInformation]( [IsExtractionIdentifier] [bit] NOT NULL, [IsPrimaryKey] [bit] NOT NULL, [SoftwareVersion] [nvarchar](50) NOT NULL, + [GroupBy] [int] DEFAULT 1, CONSTRAINT [PK_ExtractionInformation] PRIMARY KEY CLUSTERED ( [ID] ASC @@ -574,7 +576,10 @@ SET ANSI_PADDING ON GO CREATE TABLE [dbo].[LoadMetadata]( [ID] [int] IDENTITY(1,1) NOT NULL, - [LocationOfFlatFiles] [varchar](3000) NULL, + LocationOfForLoadingDirectory [varchar](3000) NULL, + LocationOfForArchivingDirectory [varchar](3000) NULL, + LocationOfExecutablesDirectory [varchar](3000) NULL, + LocationOfCacheDirectory [varchar](3000) NULL, [IncludeDataset] [bit] NOT NULL, [UsesStandardisedLoadProcess] [bit] NOT NULL, [ScheduleStartDate] [datetime] NULL, @@ -1327,6 +1332,18 @@ REFERENCES [dbo].[ExternalDatabaseServer] ([ID]) GO ALTER TABLE [dbo].[TableInfo] CHECK CONSTRAINT [FK_TableInfo_ExternalDatabaseServer] GO +CREATE TABLE [dbo].[Setting]( + [ID] [int] IDENTITY(1,1) NOT NULL, + [Key] [varchar](450) NOT NULL, + [Value] [varchar](max) NOT NULL, +CONSTRAINT [UNIQUE_SettingKey] UNIQUE([Key]), + CONSTRAINT [PK_SettingKey] PRIMARY KEY CLUSTERED +( + [ID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +GO + EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Table ID' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Catalogue', @level2type=N'COLUMN',@level2name=N'ID' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'‘SMR01’ for example' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Catalogue', @level2type=N'COLUMN',@level2name=N'Acronym' diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/027_CohortManagerSupport.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/027_CohortManagerSupport.sql index 304e8cfaa4..4347f6171b 100644 Binary files a/Rdmp.Core/Databases/CatalogueDatabase/up/027_CohortManagerSupport.sql and b/Rdmp.Core/Databases/CatalogueDatabase/up/027_CohortManagerSupport.sql differ diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/070_UnicodeSupport.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/070_UnicodeSupport.sql index b37b43e9db..506e266c1a 100644 Binary files a/Rdmp.Core/Databases/CatalogueDatabase/up/070_UnicodeSupport.sql and b/Rdmp.Core/Databases/CatalogueDatabase/up/070_UnicodeSupport.sql differ diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/081_AddInstanceSettings.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/081_AddInstanceSettings.sql new file mode 100644 index 0000000000..0a141e0730 --- /dev/null +++ b/Rdmp.Core/Databases/CatalogueDatabase/up/081_AddInstanceSettings.sql @@ -0,0 +1,15 @@ +--Version: 8.2.0 +--Description: Adds unified instance settings +if not exists (select 1 from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='Setting') +BEGIN +CREATE TABLE [dbo].[Setting]( + [ID] [int] IDENTITY(1,1) NOT NULL, + [Key] [varchar](450) NOT NULL, + [Value] [varchar](max) NOT NULL, + CONSTRAINT [UNIQUE_SettingKey] UNIQUE([Key]), + CONSTRAINT [PK_SettingKey] PRIMARY KEY CLUSTERED +( + [ID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +END diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/082_AddCohortVersioning.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/082_AddCohortVersioning.sql new file mode 100644 index 0000000000..badbc3aa2d --- /dev/null +++ b/Rdmp.Core/Databases/CatalogueDatabase/up/082_AddCohortVersioning.sql @@ -0,0 +1,7 @@ +--Version: 8.2.0 +--Description: Adds cohort version numbers +if not exists(select 1 from sys.columns where name = 'Version' and OBJECT_NAME(object_id) = 'CohortIdentificationConfiguration') +BEGIN + alter table [dbo].[CohortIdentificationConfiguration] + add Version [int] + END \ No newline at end of file diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/083_AddGroupBy.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/083_AddGroupBy.sql new file mode 100644 index 0000000000..18b1f4965a --- /dev/null +++ b/Rdmp.Core/Databases/CatalogueDatabase/up/083_AddGroupBy.sql @@ -0,0 +1,12 @@ +--Version: 8.2.0 +--Description: Adds linking table to allow for multiple load metadatas per catalogue + if not exists (select 1 from sys.columns where name = 'GroupBy' and OBJECT_NAME(object_id) = 'AggregateDimension') +BEGIN +ALTER TABLE [dbo].[AggregateDimension] ADD GroupBy int DEFAULT 1 WITH VALUES +END + +if not exists (select 1 from sys.columns where name = 'GroupBy' and OBJECT_NAME(object_id) = 'ExtractionInformation') +BEGIN +ALTER TABLE [dbo].[ExtractionInformation] ADD GroupBy int DEFAULT 1 WITH VALUES +END + diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/084_AddLoadDirectorySplit.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/084_AddLoadDirectorySplit.sql new file mode 100644 index 0000000000..f33d095c06 --- /dev/null +++ b/Rdmp.Core/Databases/CatalogueDatabase/up/084_AddLoadDirectorySplit.sql @@ -0,0 +1,30 @@ +--Version:8.2.0 +--Description: Allow for dispirate locations for data load directories +if not exists (select 1 from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='LoadMetaData' and COLUMN_NAME='LocationOfForLoadingDirectory') +BEGIN + ALTER TABLE [dbo].[LoadMetadata] ADD LocationOfForLoadingDirectory varchar(3000) NULL; + ALTER TABLE [dbo].[LoadMetadata] ADD LocationOfForArchivingDirectory [varchar](3000) NULL; + ALTER TABLE [dbo].[LoadMetadata] ADD LocationOfExecutablesDirectory [varchar](3000) NULL; + ALTER TABLE [dbo].[LoadMetadata] ADD LocationOfCacheDirectory [varchar](3000) NULL; +END +GO + +if exists(select 1 from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='LoadMetaData' and COLUMN_NAME='LocationOfFlatFiles') +BEGIN + Declare @SplitMetaDataSQL varchar(max)= ' + update [dbo].[LoadMetadata] + set LocationOfForLoadingDirectory = LocationOfFlatFiles +''\Data\ForLoading\'' + where LocationOfFlatFiles is not null + update [dbo].[LoadMetadata] + set LocationOfForArchivingDirectory = LocationOfFlatFiles +''\Data\ForArchiving\'' + where LocationOfFlatFiles is not null + update [dbo].[LoadMetadata] + set LocationOfExecutablesDirectory = LocationOfFlatFiles +''\Executables\'' + where LocationOfFlatFiles is not null + update [dbo].[LoadMetadata] + set LocationOfCacheDirectory = LocationOfFlatFiles +''\Data\Cache\'' + where LocationOfFlatFiles is not null + ' + EXEC(@SplitMetaDataSQL) +END +GO \ No newline at end of file diff --git a/Rdmp.Core/Databases/LoggingDatabasePatcher.cs b/Rdmp.Core/Databases/LoggingDatabasePatcher.cs index 83fc914f4a..32ec8ead21 100644 --- a/Rdmp.Core/Databases/LoggingDatabasePatcher.cs +++ b/Rdmp.Core/Databases/LoggingDatabasePatcher.cs @@ -101,8 +101,8 @@ public override Patch GetInitialCreateScriptContents(DiscoveredDatabase db) INSERT INTO {db.ExpectTable("DataSet").GetWrappedName()} ({sh.EnsureWrapped("dataSetID")}, {sh.EnsureWrapped("name")}, {sh.EnsureWrapped("description")}, {sh.EnsureWrapped("time_period")}, {sh.EnsureWrapped("SLA_required")}, {sh.EnsureWrapped("supplier_name")}, {sh.EnsureWrapped("supplier_tel_no")}, {sh.EnsureWrapped("supplier_email")}, {sh.EnsureWrapped("contact_name")}, {sh.EnsureWrapped("contact_position")}, {sh.EnsureWrapped("currentContactInstitutions")}, {sh.EnsureWrapped("contact_tel_no")}, {sh.EnsureWrapped("contact_email")}, {sh.EnsureWrapped("frequency")}, {sh.EnsureWrapped("method")}) VALUES(N'Internal', 'Internal', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); /*create tasks*/ - INSERT INTO {db.ExpectTable("DataLoadTask").GetWrappedName()} ({sh.EnsureWrapped("ID")}, {sh.EnsureWrapped("description")}, {sh.EnsureWrapped("name")}, {sh.EnsureWrapped("userAccount")}, {sh.EnsureWrapped("statusID")}, {sh.EnsureWrapped("isTest")}, {sh.EnsureWrapped("dataSetID")}) VALUES(1, 'Internal', 'Internal', 'Thomas', 1, 0, 'Internal'); - INSERT INTO {db.ExpectTable("DataLoadTask").GetWrappedName()} ({sh.EnsureWrapped("ID")}, {sh.EnsureWrapped("description")}, {sh.EnsureWrapped("name")}, {sh.EnsureWrapped("userAccount")}, {sh.EnsureWrapped("statusID")}, {sh.EnsureWrapped("isTest")}, {sh.EnsureWrapped("dataSetID")}) VALUES(2, 'DataExtraction', 'DataExtraction', 'Thomas', 1, 0, 'DataExtraction'); + INSERT INTO {db.ExpectTable("DataLoadTask").GetWrappedName()} ({sh.EnsureWrapped("ID")}, {sh.EnsureWrapped("description")}, {sh.EnsureWrapped("name")}, {sh.EnsureWrapped("userAccount")}, {sh.EnsureWrapped("statusID")}, {sh.EnsureWrapped("isTest")}, {sh.EnsureWrapped("dataSetID")}) VALUES(1, 'Internal', 'Internal', 'Thomas', 1, {(sh.DatabaseType == FAnsi.DatabaseType.MicrosoftSQLServer ? 0 : "FALSE")}, 'Internal'); + INSERT INTO {db.ExpectTable("DataLoadTask").GetWrappedName()} ({sh.EnsureWrapped("ID")}, {sh.EnsureWrapped("description")}, {sh.EnsureWrapped("name")}, {sh.EnsureWrapped("userAccount")}, {sh.EnsureWrapped("statusID")}, {sh.EnsureWrapped("isTest")}, {sh.EnsureWrapped("dataSetID")}) VALUES(2, 'DataExtraction', 'DataExtraction', 'Thomas', 1, {(sh.DatabaseType == FAnsi.DatabaseType.MicrosoftSQLServer ? 0 : "FALSE")}, 'DataExtraction'); """); @@ -120,4 +120,4 @@ public override SortedDictionary GetAllPatchesInAssembly(Discover //this is empty because the only patch is already accounted for return new SortedDictionary(); } -} \ No newline at end of file +} diff --git a/Rdmp.Core/Icons/IconProvision/CatalogueIcons.Designer.cs b/Rdmp.Core/Icons/IconProvision/CatalogueIcons.Designer.cs index b229b7e8d7..bce3ee5236 100644 --- a/Rdmp.Core/Icons/IconProvision/CatalogueIcons.Designer.cs +++ b/Rdmp.Core/Icons/IconProvision/CatalogueIcons.Designer.cs @@ -1269,6 +1269,18 @@ public static Byte[] Dataset } } + /// + /// Looks up a localized resource of type Image. + /// + public static Byte[] Setting + { + get + { + object obj = ResourceManager.GetObject("Setting", resourceCulture); + return ((Byte[])(obj)); + } + } + /// /// Looks up a localized resource of type Image. /// diff --git a/Rdmp.Core/Icons/IconProvision/CatalogueIcons.resx b/Rdmp.Core/Icons/IconProvision/CatalogueIcons.resx index 457ebb0f7b..571c731520 100644 --- a/Rdmp.Core/Icons/IconProvision/CatalogueIcons.resx +++ b/Rdmp.Core/Icons/IconProvision/CatalogueIcons.resx @@ -757,4 +757,7 @@ ..\LoadMetadataCatalogueLinkage.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\famfamfam\cog.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/Rdmp.Core/Icons/IconProvision/RDMPConcept.cs b/Rdmp.Core/Icons/IconProvision/RDMPConcept.cs index 9e8cabf344..a814693fa9 100644 --- a/Rdmp.Core/Icons/IconProvision/RDMPConcept.cs +++ b/Rdmp.Core/Icons/IconProvision/RDMPConcept.cs @@ -204,5 +204,6 @@ public enum RDMPConcept Memento, TableInfoDatabaseNode, Dataset, - LoadMetadataCatalogueLinkage + LoadMetadataCatalogueLinkage, + Setting } \ No newline at end of file diff --git a/Rdmp.Core/MapsDirectlyToDatabaseTable/Versioning/MasterDatabaseScriptExecutor.cs b/Rdmp.Core/MapsDirectlyToDatabaseTable/Versioning/MasterDatabaseScriptExecutor.cs index b520d96dbc..112164607f 100644 --- a/Rdmp.Core/MapsDirectlyToDatabaseTable/Versioning/MasterDatabaseScriptExecutor.cs +++ b/Rdmp.Core/MapsDirectlyToDatabaseTable/Versioning/MasterDatabaseScriptExecutor.cs @@ -144,8 +144,7 @@ private void RunSQL(KeyValuePair kvp) DiscoveredServerHelper.CreateDatabaseTimeoutInSeconds); } - var now = DateTime.Now; - + var now = DateTime.UtcNow; Database.ExpectTable(RoundhouseScriptsRunTable, RoundhouseSchemaName) .Insert(new Dictionary { @@ -188,7 +187,7 @@ private void SetVersion(string name, string version) //repository_path version entry_date modified_date entered_by //Patching 2.6.0.1 2018-02-05 08:26:54.000 2018-02-05 08:26:54.000 DUNDEE\TZNind - var now = DateTime.Now; + var now = DateTime.UtcNow; versionTable.Insert(new Dictionary { diff --git a/Rdmp.Core/Providers/CatalogueChildProvider.cs b/Rdmp.Core/Providers/CatalogueChildProvider.cs index cf31a748d0..296d18fa1a 100644 --- a/Rdmp.Core/Providers/CatalogueChildProvider.cs +++ b/Rdmp.Core/Providers/CatalogueChildProvider.cs @@ -144,6 +144,7 @@ public class CatalogueChildProvider : ICoreChildProvider public FolderNode DatasetRootFolder { get; set; } public FolderNode CohortIdentificationConfigurationRootFolder { get; set; } + public FolderNode CohortIdentificationConfigurationRootFolderWithoutVersionedConfigurations { get; set; } public AllConnectionStringKeywordsNode AllConnectionStringKeywordsNode { get; set; } public ConnectionStringKeyword[] AllConnectionStringKeywords { get; set; } @@ -397,6 +398,10 @@ public CatalogueChildProvider(ICatalogueRepository repository, IChildProvider[] FolderHelper.BuildFolderTree(AllCohortIdentificationConfigurations); AddChildren(CohortIdentificationConfigurationRootFolder, new DescendancyList(CohortIdentificationConfigurationRootFolder)); + + CohortIdentificationConfigurationRootFolderWithoutVersionedConfigurations = FolderHelper.BuildFolderTree(AllCohortIdentificationConfigurations.Where(cic => cic.Version is null).ToArray()); + AddChildren(CohortIdentificationConfigurationRootFolderWithoutVersionedConfigurations, + new DescendancyList(CohortIdentificationConfigurationRootFolderWithoutVersionedConfigurations)); var templateAggregateConfigurationIds = new HashSet( repository.GetExtendedProperties(ExtendedProperty.IsTemplate) @@ -850,6 +855,7 @@ private void AddChildren(FolderNode folder, D //add subfolder children AddChildren(child, descendancy.Add(child)); + //add cics in folder foreach (var cic in folder.ChildObjects) AddChildren(cic, descendancy.Add(cic)); diff --git a/Rdmp.Core/Providers/ICoreChildProvider.cs b/Rdmp.Core/Providers/ICoreChildProvider.cs index a578400621..bdd068dbf0 100644 --- a/Rdmp.Core/Providers/ICoreChildProvider.cs +++ b/Rdmp.Core/Providers/ICoreChildProvider.cs @@ -47,7 +47,7 @@ public interface ICoreChildProvider : IChildProvider FolderNode DatasetRootFolder { get; } FolderNode LoadMetadataRootFolder { get; } FolderNode CohortIdentificationConfigurationRootFolder { get; } - + FolderNode CohortIdentificationConfigurationRootFolderWithoutVersionedConfigurations { get; } Catalogue[] AllCatalogues { get; } Curation.Data.Dataset[] AllDatasets { get; } Dictionary AllCataloguesDictionary { get; } diff --git a/Rdmp.Core/Providers/Nodes/LoadMetadataNodes/LoadDirectoryNode.cs b/Rdmp.Core/Providers/Nodes/LoadMetadataNodes/LoadDirectoryNode.cs index c9612b3e44..2319d2319e 100644 --- a/Rdmp.Core/Providers/Nodes/LoadMetadataNodes/LoadDirectoryNode.cs +++ b/Rdmp.Core/Providers/Nodes/LoadMetadataNodes/LoadDirectoryNode.cs @@ -19,16 +19,21 @@ public LoadDirectoryNode(LoadMetadata loadMetadata) LoadMetadata = loadMetadata; } - public bool IsEmpty => string.IsNullOrWhiteSpace(LoadMetadata.LocationOfFlatFiles); + public bool IsEmpty => string.IsNullOrWhiteSpace(LoadMetadata.LocationOfForLoadingDirectory); - public override string ToString() => string.IsNullOrWhiteSpace(LoadMetadata.LocationOfFlatFiles) - ? "???" - : LoadMetadata.LocationOfFlatFiles; + public override string ToString() + { + var directory = LoadMetadata.GetRootDirectory(); + if (directory == null) + { + if (LoadMetadata.LocationOfForLoadingDirectory == null) return "No Load Metadata Set"; + return "Custom"; + } + return directory.ToString(); + } - public DirectoryInfo GetDirectoryInfoIfAny() => string.IsNullOrWhiteSpace(LoadMetadata.LocationOfFlatFiles) - ? null - : new DirectoryInfo(LoadMetadata.LocationOfFlatFiles); + public DirectoryInfo GetDirectoryInfoIfAny() => LoadMetadata.GetRootDirectory(); protected bool Equals(LoadDirectoryNode other) => Equals(LoadMetadata, other.LoadMetadata); diff --git a/Rdmp.Core/QueryBuilding/AggregateBuilder.cs b/Rdmp.Core/QueryBuilding/AggregateBuilder.cs index 0c2a752b18..24ed8a5398 100644 --- a/Rdmp.Core/QueryBuilding/AggregateBuilder.cs +++ b/Rdmp.Core/QueryBuilding/AggregateBuilder.cs @@ -82,6 +82,10 @@ public string SQL /// public string HavingSQL { get; set; } + + public string AxisStartDateOverride { get; set; } + public string AxisEndDateOverride { get; set; } + /// /// Optional, Limit the results returned. /// @@ -167,6 +171,9 @@ public bool DoNotWriteOutParameters /// private readonly List _skipGroupByForThese = new(); + private readonly List _skipByUserRequest = new(); + + /// /// /// @@ -237,12 +244,16 @@ public void AddColumn(IColumn col) /// /// /// - public void AddColumn(IColumn col, bool includeAsGroupBy) + /// + public void AddColumn(IColumn col, bool includeAsGroupBy, bool useDefinedGroupIgnore=false) { SelectColumns.Add(new QueryTimeColumn(col)); - if (!includeAsGroupBy) _skipGroupByForThese.Add(col); + if (useDefinedGroupIgnore) + { + _skipByUserRequest.Add(col); + } } /// @@ -435,6 +446,10 @@ public void RegenerateSQL() GetGroupBySQL(queryLines, aggregateHelper); queryLines = queryLines.Where(l => !string.IsNullOrWhiteSpace(l.Text)).ToList(); + if (AxisStartDateOverride != null) + _axis.StartDate = AxisStartDateOverride; + if (AxisEndDateOverride != null) + _axis.EndDate = AxisEndDateOverride; _sql = aggregateHelper.BuildAggregate(queryLines, _axis); } @@ -468,7 +483,7 @@ private void GetGroupBySQL(List queryLines, IAggregateHelper aggrega //yes there are! better group by then! queryLines.Add(new CustomLine("group by ", QueryComponent.GroupBy)); - foreach (var col in SelectColumns) + foreach (var col in SelectColumns.Where(col => !_skipByUserRequest.Contains(col.IColumn))) { if (col.IColumn is AggregateCountColumn) continue; diff --git a/Rdmp.Core/QueryBuilding/AggregateCountColumn.cs b/Rdmp.Core/QueryBuilding/AggregateCountColumn.cs index 802bd232c4..a7d0cf4726 100644 --- a/Rdmp.Core/QueryBuilding/AggregateCountColumn.cs +++ b/Rdmp.Core/QueryBuilding/AggregateCountColumn.cs @@ -94,6 +94,9 @@ public string GetFullSelectLineStringForSavingIntoAnAggregate() => string.IsNull /// public bool IsPrimaryKey => false; + /// + public bool GroupBy => false; + /// public void Check(ICheckNotifier notifier) { diff --git a/Rdmp.Core/QueryBuilding/CohortQueryBuilderHelper.cs b/Rdmp.Core/QueryBuilding/CohortQueryBuilderHelper.cs index af3528dfaa..090d9114df 100644 --- a/Rdmp.Core/QueryBuilding/CohortQueryBuilderHelper.cs +++ b/Rdmp.Core/QueryBuilding/CohortQueryBuilderHelper.cs @@ -77,9 +77,12 @@ public static CohortQueryBuilderDependencySql GetSQLForAggregate(AggregateConfig //false makes it skip them in the SQL it generates (it uses them only in determining JOIN requirements etc but since we passed in the select SQL explicitly it should be the equivellent of telling the query builder to generate a regular select if (!isJoinAggregate) - builder.AddColumn(extractionIdentifier, false); + builder.AddColumn(extractionIdentifier, false, !extractionIdentifier.GroupBy); else - builder.AddColumnRange(aggregate.AggregateDimensions.ToArray(), false); + foreach (var agg in aggregate.AggregateDimensions) + { + builder.AddColumn(agg, false, !agg.GroupBy); + } } else { @@ -91,10 +94,14 @@ public static CohortQueryBuilderDependencySql GetSQLForAggregate(AggregateConfig //add the extraction information and do group by it if (!isJoinAggregate) - builder.AddColumn(extractionIdentifier, true); + builder.AddColumn(extractionIdentifier, true, !extractionIdentifier.GroupBy); else - builder.AddColumnRange(aggregate.AggregateDimensions.ToArray(), - true); //it's a joinable inception query (See JoinableCohortAggregateConfiguration) - these are allowed additional columns + { + foreach (var agg in aggregate.AggregateDimensions) + { + builder.AddColumn(agg, true, !agg.GroupBy); + } + } //it's a joinable inception query (See JoinableCohortAggregateConfiguration) - these are allowed additional columns builder.DoNotWriteOutOrderBy = true; } diff --git a/Rdmp.Core/Rdmp.Core.csproj b/Rdmp.Core/Rdmp.Core.csproj index 53659c989a..95a9950395 100644 --- a/Rdmp.Core/Rdmp.Core.csproj +++ b/Rdmp.Core/Rdmp.Core.csproj @@ -123,6 +123,10 @@ + + + + @@ -246,7 +250,11 @@ + + + + diff --git a/Rdmp.Core/ReusableLibraryCode/Progress/ForkDataLoadEventListener.cs b/Rdmp.Core/ReusableLibraryCode/Progress/ForkDataLoadEventListener.cs index b89dbd0173..36d394cb0f 100644 --- a/Rdmp.Core/ReusableLibraryCode/Progress/ForkDataLoadEventListener.cs +++ b/Rdmp.Core/ReusableLibraryCode/Progress/ForkDataLoadEventListener.cs @@ -1,9 +1,13 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with RDMP. If not, see . +using Rdmp.Core.Logging.Listeners; +using System.Collections.Generic; +using System.Linq; + namespace Rdmp.Core.ReusableLibraryCode.Progress; /// @@ -31,4 +35,10 @@ public void OnProgress(object sender, ProgressEventArgs e) foreach (var listener in _listeners) listener.OnProgress(sender, e); } + + + public List GetToLoggingDatabaseDataLoadEventListenersIfany() + { + return _listeners.Where(l => l.GetType() == typeof(ToLoggingDatabaseDataLoadEventListener)).Select(l => (ToLoggingDatabaseDataLoadEventListener)l).ToList(); + } } \ No newline at end of file diff --git a/Rdmp.Core/Setting/ISetting.cs b/Rdmp.Core/Setting/ISetting.cs new file mode 100644 index 0000000000..eec7864b6c --- /dev/null +++ b/Rdmp.Core/Setting/ISetting.cs @@ -0,0 +1,18 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see .using Amazon.Auth.AccessControlPolicy; + +using Rdmp.Core.MapsDirectlyToDatabaseTable; + +namespace Rdmp.Core.Setting; + +/// +/// Key Value pairs for arbitrary setting within RDMP instances +/// +public interface ISetting: IMapsDirectlyToDatabaseTable +{ + string Key { get; set; } + string Value { get; set; } +} \ No newline at end of file diff --git a/Rdmp.Core/Setting/Setting.cs b/Rdmp.Core/Setting/Setting.cs new file mode 100644 index 0000000000..efa3495533 --- /dev/null +++ b/Rdmp.Core/Setting/Setting.cs @@ -0,0 +1,60 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see .using Amazon.Auth.AccessControlPolicy; + +using Rdmp.Core.Curation.Data; +using Rdmp.Core.MapsDirectlyToDatabaseTable.Attributes; +using Rdmp.Core.Repositories; +using Rdmp.Core.ReusableLibraryCode.Annotations; +using System.Collections.Generic; +using System.Data.Common; + +namespace Rdmp.Core.Setting; + +/// + +public class Setting : DatabaseEntity, ISetting +{ + #region Database Properties + + private string _key; + private string _value; + + [NotNull] + [Unique] + public string Key + { + get => _key; + set => SetField(ref _key, value); + } + + [NotNull] + public string Value + { + get => _value; + set => SetField(ref _value, value); + } + + #endregion + + public Setting() { } + public Setting(ICatalogueRepository repository, string key, string value) + { + Key = key; + Value = value; + Repository = repository; + Repository.InsertAndHydrate(this, new Dictionary + { + { nameof(Key), key }, + { nameof(Value), value }, + }); + } + + public Setting(ICatalogueRepository repository, DbDataReader r): base(repository,r) + { + Key = r["Key"].ToString(); + Value = r["Value"].ToString(); + } +} \ No newline at end of file diff --git a/Rdmp.UI.Tests/ChildProviderTests.cs b/Rdmp.UI.Tests/ChildProviderTests.cs index e35e533058..4e7ac61cce 100644 --- a/Rdmp.UI.Tests/ChildProviderTests.cs +++ b/Rdmp.UI.Tests/ChildProviderTests.cs @@ -60,7 +60,7 @@ public void TestUpTo() string[] skip = { "AllAggregateContainers", "_dataExportFilterManager", "dataExportRepository", "WriteLock", - "_oProjectNumberToCohortsDictionary", "_errorsCheckNotifier", "ProgressStopwatch" + "_oProjectNumberToCohortsDictionary", "_errorsCheckNotifier", "ProgressStopwatch","CohortIdentificationConfigurationRootFolderWithoutVersionedConfigurations" }; // We have 2 providers and want to suck all the data out of one into the other diff --git a/Rdmp.UI.Tests/DesignPatternTests/ClassFileEvaluation/DocumentationCrossExaminationTest.cs b/Rdmp.UI.Tests/DesignPatternTests/ClassFileEvaluation/DocumentationCrossExaminationTest.cs index 0c116136cd..1407948d02 100644 --- a/Rdmp.UI.Tests/DesignPatternTests/ClassFileEvaluation/DocumentationCrossExaminationTest.cs +++ b/Rdmp.UI.Tests/DesignPatternTests/ClassFileEvaluation/DocumentationCrossExaminationTest.cs @@ -4,13 +4,13 @@ // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with RDMP. If not, see . +using NUnit.Framework; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; -using NUnit.Framework; namespace Rdmp.UI.Tests.DesignPatternTests.ClassFileEvaluation; @@ -351,8 +351,8 @@ public void FindProblems(List csFilesFound) var fileContents = File.ReadAllText(mdFile); foreach (Match m in MatchMdReferences.Matches(fileContents)) - foreach (Match word in Regex.Matches(m.Groups[1].Value, @"([A-Z]\w+){2,}")) - fileCommentTokens[mdFile].Add(word.Value); + foreach (Match word in Regex.Matches(m.Groups[1].Value, @"([A-Z]\w+){2,}")) + fileCommentTokens[mdFile].Add(word.Value); EnsureMaximumGlossaryUse(mdFile, problems); @@ -511,6 +511,7 @@ private void EnsureMaximumGlossaryUse(string mdFile, List problems) relPath = $"./{relPath}"; var suggestedLine = $"[{match.Value}]: {relPath}#{match.Value}"; + var markdownLink = $"[{match.Value}]({relPath}#{match.Value})"; //if it has spaces on either side if (line[Math.Max(0, match.Index - 1)] == ' ' && line[ @@ -524,7 +525,7 @@ private void EnsureMaximumGlossaryUse(string mdFile, List problems) allLinesRevised[lineNumber - 1] = line.Replace($"`{match.Value}`", $"[{match.Value}]"); //if it is a novel occurrence - if (!allLines.Contains(suggestedLine) && !suggestedLinks.ContainsValue(suggestedLine)) + if (!allLines.Contains(suggestedLine) && !suggestedLinks.ContainsValue(suggestedLine) && !allLines.Contains(markdownLink) && !suggestedLinks.ContainsValue(markdownLink)) { suggestedLinks.Add(match.Value, suggestedLine); problems.Add( diff --git a/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.Designer.cs b/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.Designer.cs new file mode 100644 index 0000000000..c480646f87 --- /dev/null +++ b/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.Designer.cs @@ -0,0 +1,128 @@ +namespace Rdmp.UI.AggregationUIs.Advanced +{ + partial class AggregateGraphDateSelector + { + /// + /// 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() + { + btnRefresh = new System.Windows.Forms.Button(); + btnCancel = new System.Windows.Forms.Button(); + tbStartDate = new System.Windows.Forms.TextBox(); + tbEndDate = new System.Windows.Forms.TextBox(); + label1 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + SuspendLayout(); + // + // btnRefresh + // + btnRefresh.Location = new System.Drawing.Point(63, 110); + btnRefresh.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnRefresh.Name = "btnRefresh"; + btnRefresh.Size = new System.Drawing.Size(88, 27); + btnRefresh.TabIndex = 3; + btnRefresh.Text = "Partial Refresh"; + btnRefresh.UseVisualStyleBackColor = true; + btnRefresh.Click += btnRefresh_Click; + // + // btnCancel + // + btnCancel.Location = new System.Drawing.Point(187, 110); + btnCancel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnCancel.Name = "btnCancel"; + btnCancel.Size = new System.Drawing.Size(88, 27); + btnCancel.TabIndex = 4; + btnCancel.Text = "Cancel"; + btnCancel.UseVisualStyleBackColor = true; + btnCancel.Click += btnCancel_Click; + // + // tbStartDate + // + tbStartDate.Location = new System.Drawing.Point(28, 57); + tbStartDate.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbStartDate.Name = "tbStartDate"; + tbStartDate.Size = new System.Drawing.Size(123, 23); + tbStartDate.TabIndex = 5; + tbStartDate.TextChanged += tbStartDate_TextChanged; + // + // tbEndDate + // + tbEndDate.Location = new System.Drawing.Point(186, 57); + tbEndDate.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbEndDate.Name = "tbEndDate"; + tbEndDate.Size = new System.Drawing.Size(123, 23); + tbEndDate.TabIndex = 6; + tbEndDate.TextChanged += tbEndDate_TextChanged; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(58, 28); + label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(58, 15); + label1.TabIndex = 7; + label1.Text = "Start Date"; + label1.Click += label1_Click; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(221, 28); + label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(54, 15); + label2.TabIndex = 8; + label2.Text = "End Date"; + label2.Click += label2_Click; + // + // AggregateGraphDateSelector + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(360, 174); + Controls.Add(label2); + Controls.Add(label1); + Controls.Add(tbEndDate); + Controls.Add(tbStartDate); + Controls.Add(btnCancel); + Controls.Add(btnRefresh); + Name = "AggregateGraphDateSelector"; + Text = "Select a start and end date to refresh"; + Load += AggregateGraphDateSelector_Load; + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private System.Windows.Forms.Button btnRefresh; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.TextBox tbStartDate; + private System.Windows.Forms.TextBox tbEndDate; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + } +} \ No newline at end of file diff --git a/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.cs b/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.cs new file mode 100644 index 0000000000..573ba3bc3a --- /dev/null +++ b/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Rdmp.UI.AggregationUIs.Advanced +{ + public partial class AggregateGraphDateSelector : Form + { + public AggregateGraphDateSelector(string startDate = "", string endDate = "") + { + + StartDate = startDate; + EndDate = endDate; + InitializeComponent(); + tbStartDate.Text = startDate; + tbEndDate.Text = endDate; + validateUpdate(); + } + + public string StartDate; + public string EndDate; + + private bool validateDate(string date) + { + return DateRegex().Match(date.Trim()).Success; + } + + + private void validateUpdate() + { + var startDateValid = validateDate(tbStartDate.Text); + var endDateValid = validateDate(tbEndDate.Text); + if (!startDateValid) + { + tbStartDate.ForeColor = Color.Red; + } + else + { + tbStartDate.ForeColor = Color.Black; + } + if (!endDateValid) + { + tbEndDate.ForeColor = Color.Red; + } + else + { + tbEndDate.ForeColor = Color.Black; + } + if (!startDateValid || !endDateValid) + { + btnRefresh.Enabled = false; + } + else + { + btnRefresh.Enabled = true; + } + + } + + private void AggregateGraphDateSelector_Load(object sender, EventArgs e) + { + + } + + private void label1_Click(object sender, EventArgs e) + { + + } + + private void label2_Click(object sender, EventArgs e) + { + + } + + private void tbStartDate_TextChanged(object sender, EventArgs e) + { + StartDate = tbStartDate.Text; + validateUpdate(); + + } + + [GeneratedRegex("^'\\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])'$")] + private static partial Regex DateRegex(); + + private void tbEndDate_TextChanged(object sender, EventArgs e) + { + EndDate = tbEndDate.Text; + validateUpdate(); + } + + private void btnRefresh_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + } + } +} diff --git a/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.resx b/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.resx new file mode 100644 index 0000000000..af32865ec1 --- /dev/null +++ b/Rdmp.UI/AggregationUIs/Advanced/AggregateGraphDateSelector.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.Designer.cs b/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.Designer.cs index 9d949658bd..114ede675a 100644 --- a/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.Designer.cs +++ b/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.Designer.cs @@ -37,6 +37,7 @@ private void InitializeComponent() this.olvColumnSQL = new BrightIdeasSoftware.OLVColumn(); this.olvEditInPopup = new BrightIdeasSoftware.OLVColumn(); this.olvAlias = new BrightIdeasSoftware.OLVColumn(); + this.olvGroupBy = new BrightIdeasSoftware.OLVColumn(); this.olvIncluded = new BrightIdeasSoftware.OLVColumn(); this.tbFilter = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); @@ -49,6 +50,7 @@ private void InitializeComponent() this.olvSelectColumns.AllColumns.Add(this.olvColumnSQL); this.olvSelectColumns.AllColumns.Add(this.olvEditInPopup); this.olvSelectColumns.AllColumns.Add(this.olvAlias); + this.olvSelectColumns.AllColumns.Add(this.olvGroupBy); this.olvSelectColumns.AllColumns.Add(this.olvIncluded); this.olvSelectColumns.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) @@ -59,7 +61,7 @@ private void InitializeComponent() this.olvAddRemove, this.olvColumnSQL, this.olvEditInPopup, - this.olvAlias}); + this.olvAlias,this.olvGroupBy}); this.olvSelectColumns.Cursor = System.Windows.Forms.Cursors.Default; this.olvSelectColumns.HideSelection = false; this.olvSelectColumns.Location = new System.Drawing.Point(0, 3); @@ -98,7 +100,15 @@ private void InitializeComponent() this.olvAlias.AspectName = "Alias"; this.olvAlias.Groupable = false; this.olvAlias.Text = "Alias"; - this.olvAlias.Width = 100; + this.olvAlias.Width = 100; // + // olvGroupBy + // + this.olvGroupBy.AspectName = "GroupBy"; + this.olvGroupBy.Groupable = true; + this.olvGroupBy.CheckBoxes = true; + this.olvGroupBy.Text = "Group By"; + this.olvGroupBy.Width = 100; + // // olvIncluded // @@ -149,6 +159,7 @@ private void InitializeComponent() private OLVColumn olvColumnSQL; private OLVColumn olvEditInPopup; private OLVColumn olvAlias; + private OLVColumn olvGroupBy; private System.Windows.Forms.TextBox tbFilter; private System.Windows.Forms.Label label1; private OLVColumn olvIncluded; diff --git a/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.cs b/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.cs index 0b49996920..7676e1c4a9 100644 --- a/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.cs +++ b/Rdmp.UI/AggregationUIs/Advanced/SelectColumnUI.cs @@ -9,6 +9,7 @@ using System.Collections.ObjectModel; using System.Drawing; using System.Linq; +using System.Runtime.InteropServices.Marshalling; using System.Windows.Forms; using BrightIdeasSoftware; using FAnsi.Discovery; @@ -72,12 +73,57 @@ public SelectColumnUI() olvEditInPopup.AspectGetter = rowObject => _includedColumns.Contains(rowObject) ? "Edit..." : null; olvIncluded.AspectGetter = rowObject => _includedColumns.Contains(rowObject) ? "Included" : "Not Included"; + + + olvGroupBy.AspectGetter = rowObject => + { + var ad = rowObject as AggregateDimension; + if(ad != null) + { + return ((AggregateDimension)rowObject).GroupBy; + } + var ei = rowObject as ExtractionInformation; + if (ei != null) + { + return ((ExtractionInformation)rowObject).GroupBy; + } + var acc = rowObject as AggregateCountColumn; + if (acc != null) + { + return ((AggregateCountColumn)rowObject).GroupBy; + } + return null; + }; + olvSelectColumns.AlwaysGroupByColumn = olvIncluded; olvSelectColumns.RowFormatter += RowFormatter; olvSelectColumns.CellEditStarting += CellEditStarting; olvSelectColumns.CellEditFinished += CellEditFinished; + + olvSelectColumns.SubItemChecking += (object sender, SubItemCheckingEventArgs args) => + { + if (args.Column == olvGroupBy) + { + var ad = args.RowObject as AggregateDimension; + if (ad != null) + { + ad.GroupBy = !ad.GroupBy; + ad.SaveToDatabase(); + return; + } + var ei = args.RowObject as ExtractionInformation; + if (ei != null ) + { + ei.GroupBy = !ei.GroupBy; + ei.SaveToDatabase(); + return; + } + + } + }; + olvAddRemove.ImageGetter += ImageGetter; olvSelectColumns.CellClick += olvSelectColumns_CellClick; diff --git a/Rdmp.UI/AggregationUIs/AggregateGraphUI.cs b/Rdmp.UI/AggregationUIs/AggregateGraphUI.cs index f6629eae84..e3760d2daf 100644 --- a/Rdmp.UI/AggregationUIs/AggregateGraphUI.cs +++ b/Rdmp.UI/AggregationUIs/AggregateGraphUI.cs @@ -11,6 +11,7 @@ using System.Data.Common; using System.Drawing; using System.Drawing.Imaging; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -31,6 +32,7 @@ using Rdmp.Core.ReusableLibraryCode.Checks; using Rdmp.Core.ReusableLibraryCode.DataAccess; using Rdmp.Core.ReusableLibraryCode.Extensions; +using Rdmp.UI.AggregationUIs.Advanced; using Rdmp.UI.ItemActivation; using Rdmp.UI.ScintillaHelper; using Rdmp.UI.SimpleDialogs; @@ -84,17 +86,9 @@ public int Timeout private readonly ToolStripMenuItem _miClipboardCsv = new("Comma Separated Format"); private readonly ToolStripMenuItem _btnCache = new("Cache", FamFamFamIcons.picture_save.ImageToBitmap()); private readonly ToolStripButton _btnResendQuery = new("Send Query", FamFamFamIcons.arrow_refresh.ImageToBitmap()); + private readonly ToolStripButton _btnRefreshData = new("Refresh Data", FamFamFamIcons.arrow_refresh.ImageToBitmap()); private readonly ToolStripTimeout _timeoutControls = new(); - private ToolStripMenuItem miSaveImages = new("Save Image", FamFamFamIcons.disk.ImageToBitmap()); - - private ToolStripMenuItem miCopyToClipboard = new("Copy to Clipboard", CatalogueIcons.Clipboard.ImageToBitmap()); - private ToolStripMenuItem miClipboardWord = new("Word Format"); - private ToolStripMenuItem miClipboardCsv = new("Comma Separated Format"); - private ToolStripMenuItem btnCache = new("Cache", FamFamFamIcons.picture_save.ImageToBitmap()); - - private ToolStripButton btnResendQuery = new("Send Query", FamFamFamIcons.arrow_refresh.ImageToBitmap()); - public AggregateGraphUI() { InitializeComponent(); @@ -132,6 +126,7 @@ public AggregateGraphUI() _miClipboardCsv.ToolTipText = "Copies data as CSV formatted"; _btnResendQuery.Click += btnResendQuery_Click; + _btnRefreshData.Click += btnRefreshData_Click; _miSaveImages.Click += MiSaveImagesClick; _btnCache.Click += btnCache_Click; @@ -151,6 +146,7 @@ private void SetToolbarButtonsEnabled(bool enabled) _miClipboardCsv.Enabled = enabled && _dt is { Rows.Count: > 0 }; _miClipboardWord.Enabled = enabled && _dt is { Rows.Count: > 0 }; _btnResendQuery.Enabled = enabled; + _btnRefreshData.Enabled = enabled; } public AggregateConfiguration AggregateConfiguration => _aggregateConfiguration; @@ -176,6 +172,12 @@ public void AbortLoadGraph() private Task _loadTask; + public void ReloadDataBetweenDates(string startDate, string endDate) + { + _loadTask = new Task(() => LoadGraph(true, startDate, endDate)); + _loadTask.Start(); + } + public void LoadGraphAsync() { //it is already executing @@ -208,7 +210,7 @@ public void LoadGraphAsync() label1.ForeColor = Color.Black; label1.Text = GetDescription(); - _loadTask = new Task(LoadGraph); + _loadTask = new Task(() => LoadGraph()); _loadTask.Start(); } @@ -230,7 +232,30 @@ protected virtual string GetDescription() => private DataTable _dt; - private void LoadGraph() + int GetQuarterName(DateTime myDate) + { + return (int)Math.Ceiling(myDate.Month / 3.0); + } + + private DateTime ConvertAxisOverridetoDate(string axisOverride, AxisIncrement increment) + { + var overrideDate = DateTime.Parse(axisOverride); + switch (increment) + { + case AxisIncrement.Day: + return overrideDate.Date; + case AxisIncrement.Month: + return new DateTime(overrideDate.Year, overrideDate.Month, 1); + case AxisIncrement.Quarter: + return new DateTime(overrideDate.Year, (3 * GetQuarterName(overrideDate)) - 2, 1); + case AxisIncrement.Year: + return new DateTime(overrideDate.Year, 1, 1); + default: + return DateTime.Parse(axisOverride); + } + } + + private void LoadGraph(bool isRefresh = false, string startDateOverride = null, string endDateOverride = null) { try { @@ -254,7 +279,54 @@ private void LoadGraph() var axis = AggregateConfiguration.GetAxisIfAny(); var builder = GetQueryBuilder(AggregateConfiguration); + if (startDateOverride != null) + builder.AxisStartDateOverride = startDateOverride; + if (endDateOverride != null) + builder.AxisEndDateOverride = endDateOverride; + + var dateColumnName = "joinDt"; + if (isRefresh) + { + //wipe out data from dt that is between these dates + + var columnIndex = _dt.Columns.IndexOf(dateColumnName); + + var incriment = axis.AxisIncrement; + + var startDate = ConvertAxisOverridetoDate(startDateOverride.Trim('\''), incriment); + var endDate = ConvertAxisOverridetoDate(endDateOverride.Trim('\''), incriment); + var rowsToDelete = _dt.Rows.Cast().Where(r => + { + //this could maybe be more efficient with some sort of lookup atfer the first time? + var currentDT = new DateTime(); + if (incriment == AxisIncrement.Day) + { + DateTime.TryParseExact(r.ItemArray[columnIndex].ToString(), "yyyy-mm-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out currentDT); + } + if (incriment == AxisIncrement.Month) + { + DateTime.TryParseExact(r.ItemArray[columnIndex].ToString(), "yyyy-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out currentDT); + } + if (incriment == AxisIncrement.Quarter) + { + var year = r.ItemArray[columnIndex].ToString()[0..4]; + var asString = r.ItemArray[columnIndex].ToString(); + var quarter = Int32.Parse(asString.Substring(asString.Length - 1)) * 3; + DateTime.TryParseExact($"{year}-{quarter}", "yyyy-mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out currentDT); + + } + if (incriment == AxisIncrement.Year) + { + DateTime.TryParseExact(r.ItemArray[columnIndex].ToString(), "yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out currentDT); + } + return currentDT >= startDate && currentDT <= endDate; + }).ToList(); + foreach (var row in rowsToDelete) + { + _dt.Rows.Remove(row); + } + } UpdateQueryViewerScreenWithQuery(builder.SQL); var countColumn = builder.SelectColumns.FirstOrDefault(c => c.IColumn is AggregateCountColumn); @@ -272,17 +344,25 @@ private void LoadGraph() _cmd = server.GetCommand(builder.SQL, con); _cmd.CommandTimeout = Timeout; - _dt = new DataTable(); + if (!isRefresh) + { + _dt = new DataTable(); + } Invoke(new MethodInvoker(() => { lblLoadStage.Text = "Executing Query..."; })); var adapter = server.GetDataAdapter(_cmd); adapter.Fill(_dt); _cmd = null; - + if (isRefresh) + { + _dt.DefaultView.Sort = dateColumnName; + _dt = _dt.DefaultView.ToTable(); + } //trim all leading/trailing whitespace from column - foreach (DataColumn c in _dt.Columns) - c.ColumnName = c.ColumnName.Trim(); + if (!isRefresh) + foreach (DataColumn c in _dt.Columns) + c.ColumnName = c.ColumnName.Trim(); if (_dt.Rows.Count == 0) throw new Exception("Query Returned No Rows"); @@ -504,6 +584,10 @@ private void PopulateGraphResults(QueryTimeColumn countColumn, AggregateContinuo if (index > chart1.Series.Count - 1) chart1.Series.Add(new Series()); + chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true; + chart1.ChartAreas[0].CursorX.AutoScroll = true; + chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true; + chart1.Series[index].XValueMember = _dt.Columns[0].ColumnName; chart1.Series[index].YValueMembers = _dt.Columns[index + 1].ColumnName; @@ -733,6 +817,9 @@ protected void BuildMenu(IActivateItems activator) foreach (var c in _timeoutControls.GetControls()) CommonFunctionality.Add(c); + + if (DatabaseObject.GetType() == typeof(AggregateConfiguration) && ((AggregateConfiguration)DatabaseObject).GetAxisIfAny() != null) + CommonFunctionality.Add(_btnRefreshData); } } @@ -837,6 +924,20 @@ private void btnResendQuery_Click(object sender, EventArgs e) LoadGraphAsync(); } + + private void btnRefreshData_Click(object sender, EventArgs e) + { + var axis = AggregateConfiguration.GetAxisIfAny(); + var getDateString = "GETDATE()"; + var startDate = axis.StartDate == getDateString ? "" : axis.StartDate; + var endDate = axis.EndDate == getDateString ? "" : axis.EndDate; + var dialog = new AggregateGraphDateSelector(startDate, endDate); + if (dialog.ShowDialog() == DialogResult.OK) + { + ReloadDataBetweenDates(dialog.StartDate, dialog.EndDate); + } + } + public override string GetTabName() => $"Graph:{base.GetTabName()}"; private void btnCache_Click(object sender, EventArgs e) diff --git a/Rdmp.UI/Collections/CohortIdentificationCollectionUI.cs b/Rdmp.UI/Collections/CohortIdentificationCollectionUI.cs index 29f2ee7d10..8fd9e3ca17 100644 --- a/Rdmp.UI/Collections/CohortIdentificationCollectionUI.cs +++ b/Rdmp.UI/Collections/CohortIdentificationCollectionUI.cs @@ -1,10 +1,11 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with RDMP. If not, see . using System; +using System.Collections.Generic; using Rdmp.Core; using Rdmp.Core.CommandExecution.AtomicCommands; using Rdmp.Core.Curation.Data; @@ -57,8 +58,10 @@ public override void SetItemActivator(IActivateItems activator) typeof(AllOrphanAggregateConfigurationsNode), typeof(AllTemplateAggregateConfigurationsNode) }; - tlvCohortIdentificationConfigurations.AddObject(Activator.CoreChildProvider - .CohortIdentificationConfigurationRootFolder); + var rootFolder = Activator.CoreChildProvider.CohortIdentificationConfigurationRootFolderWithoutVersionedConfigurations; + rootFolder.ChildFolders = new List>(); + rootFolder.ChildObjects = new List(); + tlvCohortIdentificationConfigurations.AddObject(rootFolder); tlvCohortIdentificationConfigurations.AddObject(Activator.CoreChildProvider.OrphanAggregateConfigurationsNode); tlvCohortIdentificationConfigurations.AddObject(Activator.CoreChildProvider .TemplateAggregateConfigurationsNode); @@ -84,8 +87,7 @@ public override void SetItemActivator(IActivateItems activator) CommonTreeFunctionality.SetupColumnTracking(olvName, new Guid("f8a42259-ce5a-4006-8ab8-e0305fce05aa")); CommonTreeFunctionality.SetupColumnTracking(olvFrozen, new Guid("d1e155ef-a28f-41b5-81e4-b763627ddb3c")); - tlvCohortIdentificationConfigurations.Expand(Activator.CoreChildProvider - .CohortIdentificationConfigurationRootFolder); + tlvCohortIdentificationConfigurations.Expand(rootFolder); _firstTime = false; } diff --git a/Rdmp.UI/Collections/RDMPCollectionCommonFunctionality.cs b/Rdmp.UI/Collections/RDMPCollectionCommonFunctionality.cs index 9f50429f72..a60232e790 100644 --- a/Rdmp.UI/Collections/RDMPCollectionCommonFunctionality.cs +++ b/Rdmp.UI/Collections/RDMPCollectionCommonFunctionality.cs @@ -353,11 +353,12 @@ private static string GetToolTipTitle(object model) => private static string GetToolTipBody(IActivateItems activator, ICanBeSummarised sum) { - if (DateTime.Now.CompareTo(nextInvalidateCache)>0) + if (DateTime.Now.CompareTo(nextInvalidateCache) > 0) { cache.Clear(); nextInvalidateCache = DateTime.Now.AddSeconds(10); - } else if (cache.TryGetValue(sum, out var body)) + } + else if (cache.TryGetValue(sum, out var body)) return body; var sb = new StringBuilder(); diff --git a/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandChooseHICProjectDirectory.cs b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandChooseHICProjectDirectory.cs index e42e965a70..e4e991b37a 100644 --- a/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandChooseHICProjectDirectory.cs +++ b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandChooseHICProjectDirectory.cs @@ -4,6 +4,7 @@ // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with RDMP. If not, see . +using System.IO; using System.Windows.Forms; using Rdmp.Core.CommandExecution.AtomicCommands; using Rdmp.Core.Curation.Data.DataLoad; @@ -35,7 +36,24 @@ public override void Execute() var dialog = new ChooseLoadDirectoryUI(Activator, _loadMetadata); if (dialog.ShowDialog() == DialogResult.OK) { - _loadMetadata.LocationOfFlatFiles = dialog.Result; + if (dialog.ResultDirectory.RootPath != null) + { + //todo check these are correct & make it work with linux + _loadMetadata.LocationOfForLoadingDirectory = Path.Combine(dialog.ResultDirectory.RootPath.FullName, _loadMetadata.DefaultForLoadingPath); + _loadMetadata.LocationOfForArchivingDirectory = Path.Combine(dialog.ResultDirectory.RootPath.FullName, _loadMetadata.DefaultForArchivingPath); + _loadMetadata.LocationOfExecutablesDirectory = Path.Combine(dialog.ResultDirectory.RootPath.FullName, _loadMetadata.DefaultExecutablesPath); + _loadMetadata.LocationOfCacheDirectory = Path.Combine(dialog.ResultDirectory.RootPath.FullName, _loadMetadata.DefaultCachePath); + } + else if (dialog.ResultDirectory.ForLoading != null && + dialog.ResultDirectory.ForArchiving != null && + dialog.ResultDirectory.ExecutablesPath != null && + dialog.ResultDirectory.Cache != null) + { + _loadMetadata.LocationOfForLoadingDirectory = dialog.ResultDirectory.ForLoading.FullName; + _loadMetadata.LocationOfForArchivingDirectory = dialog.ResultDirectory.ForArchiving.FullName; + _loadMetadata.LocationOfExecutablesDirectory = dialog.ResultDirectory.ExecutablesPath.FullName; + _loadMetadata.LocationOfCacheDirectory = dialog.ResultDirectory.Cache.FullName; + } _loadMetadata.SaveToDatabase(); Publish(_loadMetadata); } diff --git a/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandDeletePlugin.cs b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandDeletePlugin.cs index a4eae128d8..b1fd745b42 100644 --- a/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandDeletePlugin.cs +++ b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandDeletePlugin.cs @@ -21,7 +21,7 @@ public sealed class ExecuteCommandDeletePlugin : BasicUICommandExecution public ExecuteCommandDeletePlugin(IActivateItems activator, LoadModuleAssembly assembly) : base(activator) { _assembly = assembly; - } + } public override Image GetImage(IIconProvider iconProvider) => iconProvider.GetImage(RDMPConcept.Plugin, OverlayKind.Delete); diff --git a/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandUpdateCatalogueDataLocationUI.cs b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandUpdateCatalogueDataLocationUI.cs new file mode 100644 index 0000000000..977bf6e9e2 --- /dev/null +++ b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandUpdateCatalogueDataLocationUI.cs @@ -0,0 +1,46 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using Rdmp.Core.CommandExecution.AtomicCommands; +using Rdmp.Core.Curation.Data; +using Rdmp.Core.Icons.IconProvision; +using Rdmp.Core.ReusableLibraryCode.Icons.IconProvision; +using Rdmp.UI.ItemActivation; +using Rdmp.UI.SimpleDialogs; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; + +namespace Rdmp.UI.CommandExecution.AtomicCommands; + +internal class ExecuteCommandUpdateCatalogueDataLocationUI : BasicUICommandExecution, IAtomicCommand +{ + private readonly Catalogue _catalogue; + private readonly ColumnInfo _columnInfo; + private readonly IActivateItems _activator; + + public ExecuteCommandUpdateCatalogueDataLocationUI(IActivateItems activator, ColumnInfo columnInfo) : base(activator) + { + _activator = activator; + _columnInfo = columnInfo; + } + + public ExecuteCommandUpdateCatalogueDataLocationUI(IActivateItems activator, Catalogue catalogue) : base(activator) + { + _catalogue = catalogue; + _activator = activator; + } + + public override Image GetImage(IIconProvider iconProvider) => + iconProvider.GetImage(RDMPConcept.CatalogueItem, OverlayKind.Edit); + + public override void Execute() + { + UpdateCatalogueDataLocationUI ui; + ui = _columnInfo is not null ? ui = new UpdateCatalogueDataLocationUI(_activator, _columnInfo) : new UpdateCatalogueDataLocationUI(_activator, _catalogue); + + ui.Show(); + } +} \ No newline at end of file diff --git a/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.Designer.cs b/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.Designer.cs index 6a47dc3043..57822043d9 100644 --- a/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.Designer.cs +++ b/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.Designer.cs @@ -32,191 +32,435 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ChooseLoadDirectoryUI)); - this.rbCreateNew = new System.Windows.Forms.RadioButton(); - this.rbUseExisting = new System.Windows.Forms.RadioButton(); - this.tbCreateNew = new System.Windows.Forms.TextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.tbUseExisting = new System.Windows.Forms.TextBox(); - this.label2 = new System.Windows.Forms.Label(); - this.btnOk = new System.Windows.Forms.Button(); - this.btnCancel = new System.Windows.Forms.Button(); - this.ragSmiley1 = new RAGSmiley(); - this.btnBrowseForExisting = new System.Windows.Forms.Button(); - this.btnCreateNewBrowse = new System.Windows.Forms.Button(); - this.lblDataIsReservedWordExisting = new System.Windows.Forms.Label(); - this.lblDataIsReservedWordNew = new System.Windows.Forms.Label(); - this.helpIcon1 = new HelpIcon(); - this.SuspendLayout(); + rbCreateNew = new System.Windows.Forms.RadioButton(); + rbUseExisting = new System.Windows.Forms.RadioButton(); + tbCreateNew = new System.Windows.Forms.TextBox(); + label1 = new System.Windows.Forms.Label(); + tbUseExisting = new System.Windows.Forms.TextBox(); + label2 = new System.Windows.Forms.Label(); + btnOk = new System.Windows.Forms.Button(); + btnCancel = new System.Windows.Forms.Button(); + ragSmiley1 = new RAGSmiley(); + btnBrowseForExisting = new System.Windows.Forms.Button(); + btnCreateNewBrowse = new System.Windows.Forms.Button(); + lblDataIsReservedWordExisting = new System.Windows.Forms.Label(); + lblDataIsReservedWordNew = new System.Windows.Forms.Label(); + helpIcon1 = new HelpIcon(); + rbChooseYourOwn = new System.Windows.Forms.RadioButton(); + btnBrowseForLoading = new System.Windows.Forms.Button(); + label3 = new System.Windows.Forms.Label(); + tbForLoadingPath = new System.Windows.Forms.TextBox(); + btnBrowseForArchiving = new System.Windows.Forms.Button(); + label4 = new System.Windows.Forms.Label(); + tbForArchivingPath = new System.Windows.Forms.TextBox(); + btnBrowseExecutables = new System.Windows.Forms.Button(); + label5 = new System.Windows.Forms.Label(); + tbExecutablesPath = new System.Windows.Forms.TextBox(); + btnBrowseCache = new System.Windows.Forms.Button(); + label6 = new System.Windows.Forms.Label(); + tbCachePath = new System.Windows.Forms.TextBox(); + lblForLoadingError = new System.Windows.Forms.Label(); + lblExecutablesError = new System.Windows.Forms.Label(); + lblForArchivingError = new System.Windows.Forms.Label(); + lblCacheError = new System.Windows.Forms.Label(); + SuspendLayout(); // // rbCreateNew // - this.rbCreateNew.AutoSize = true; - this.rbCreateNew.Location = new System.Drawing.Point(12, 12); - this.rbCreateNew.Name = "rbCreateNew"; - this.rbCreateNew.Size = new System.Drawing.Size(323, 17); - this.rbCreateNew.TabIndex = 0; - this.rbCreateNew.TabStop = true; - this.rbCreateNew.Text = "Create New (new folder with all required folders created for you)"; - this.rbCreateNew.UseVisualStyleBackColor = true; - this.rbCreateNew.CheckedChanged += new System.EventHandler(this.rb_CheckedChanged); + rbCreateNew.AutoSize = true; + rbCreateNew.Location = new System.Drawing.Point(14, 14); + rbCreateNew.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbCreateNew.Name = "rbCreateNew"; + rbCreateNew.Size = new System.Drawing.Size(363, 19); + rbCreateNew.TabIndex = 0; + rbCreateNew.TabStop = true; + rbCreateNew.Text = "Create New (new folder with all required folders created for you)"; + rbCreateNew.UseVisualStyleBackColor = true; + rbCreateNew.CheckedChanged += rb_CheckedChanged; // // rbUseExisting // - this.rbUseExisting.AutoSize = true; - this.rbUseExisting.Location = new System.Drawing.Point(12, 78); - this.rbUseExisting.Name = "rbUseExisting"; - this.rbUseExisting.Size = new System.Drawing.Size(256, 17); - this.rbUseExisting.TabIndex = 0; - this.rbUseExisting.TabStop = true; - this.rbUseExisting.Text = "Use Existing Directory (must have correct folders)"; - this.rbUseExisting.UseVisualStyleBackColor = true; - this.rbUseExisting.CheckedChanged += new System.EventHandler(this.rb_CheckedChanged); + rbUseExisting.AutoSize = true; + rbUseExisting.Location = new System.Drawing.Point(14, 90); + rbUseExisting.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbUseExisting.Name = "rbUseExisting"; + rbUseExisting.Size = new System.Drawing.Size(284, 19); + rbUseExisting.TabIndex = 0; + rbUseExisting.TabStop = true; + rbUseExisting.Text = "Use Existing Directory (must have correct folders)"; + rbUseExisting.UseVisualStyleBackColor = true; + rbUseExisting.CheckedChanged += rb_CheckedChanged; // // tbCreateNew // - this.tbCreateNew.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; - this.tbCreateNew.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; - this.tbCreateNew.Location = new System.Drawing.Point(75, 35); - this.tbCreateNew.Name = "tbCreateNew"; - this.tbCreateNew.Size = new System.Drawing.Size(630, 20); - this.tbCreateNew.TabIndex = 1; - this.tbCreateNew.TextChanged += new System.EventHandler(this.tbCreateNew_TextChanged); + tbCreateNew.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + tbCreateNew.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; + tbCreateNew.Location = new System.Drawing.Point(88, 40); + tbCreateNew.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbCreateNew.Name = "tbCreateNew"; + tbCreateNew.Size = new System.Drawing.Size(734, 23); + tbCreateNew.TabIndex = 1; + tbCreateNew.TextChanged += tbCreateNew_TextChanged; // // label1 // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(37, 38); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(32, 13); - this.label1.TabIndex = 2; - this.label1.Text = "Path:"; + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(43, 44); + label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(34, 15); + label1.TabIndex = 2; + label1.Text = "Path:"; // // tbUseExisting // - this.tbUseExisting.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; - this.tbUseExisting.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; - this.tbUseExisting.Location = new System.Drawing.Point(75, 101); - this.tbUseExisting.Name = "tbUseExisting"; - this.tbUseExisting.Size = new System.Drawing.Size(630, 20); - this.tbUseExisting.TabIndex = 1; - this.tbUseExisting.TextChanged += new System.EventHandler(this.tbUseExisting_TextChanged); - this.tbUseExisting.Leave += new System.EventHandler(this.tbUseExisting_Leave); + tbUseExisting.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + tbUseExisting.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; + tbUseExisting.Location = new System.Drawing.Point(88, 117); + tbUseExisting.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbUseExisting.Name = "tbUseExisting"; + tbUseExisting.Size = new System.Drawing.Size(734, 23); + tbUseExisting.TabIndex = 1; + tbUseExisting.TextChanged += tbUseExisting_TextChanged; + tbUseExisting.Leave += tbUseExisting_Leave; // // label2 // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(37, 104); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(32, 13); - this.label2.TabIndex = 2; - this.label2.Text = "Path:"; + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(43, 120); + label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(34, 15); + label2.TabIndex = 2; + label2.Text = "Path:"; // // btnOk // - this.btnOk.Enabled = false; - this.btnOk.Location = new System.Drawing.Point(333, 139); - this.btnOk.Name = "btnOk"; - this.btnOk.Size = new System.Drawing.Size(75, 23); - this.btnOk.TabIndex = 3; - this.btnOk.Text = "Ok"; - this.btnOk.UseVisualStyleBackColor = true; - this.btnOk.Click += new System.EventHandler(this.btnOk_Click); + btnOk.Enabled = false; + btnOk.Location = new System.Drawing.Point(380, 369); + btnOk.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnOk.Name = "btnOk"; + btnOk.Size = new System.Drawing.Size(88, 27); + btnOk.TabIndex = 3; + btnOk.Text = "Ok"; + btnOk.UseVisualStyleBackColor = true; + btnOk.Click += btnOk_Click; // // btnCancel // - this.btnCancel.Location = new System.Drawing.Point(414, 139); - this.btnCancel.Name = "btnCancel"; - this.btnCancel.Size = new System.Drawing.Size(75, 23); - this.btnCancel.TabIndex = 3; - this.btnCancel.Text = "Cancel"; - this.btnCancel.UseVisualStyleBackColor = true; - this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + btnCancel.Location = new System.Drawing.Point(476, 369); + btnCancel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnCancel.Name = "btnCancel"; + btnCancel.Size = new System.Drawing.Size(88, 27); + btnCancel.TabIndex = 3; + btnCancel.Text = "Cancel"; + btnCancel.UseVisualStyleBackColor = true; + btnCancel.Click += btnCancel_Click; // // ragSmiley1 // - this.ragSmiley1.AlwaysShowHandCursor = false; - this.ragSmiley1.BackColor = System.Drawing.Color.Transparent; - this.ragSmiley1.Cursor = System.Windows.Forms.Cursors.Arrow; - this.ragSmiley1.Location = new System.Drawing.Point(792, 101); - this.ragSmiley1.Name = "ragSmiley1"; - this.ragSmiley1.Size = new System.Drawing.Size(20, 20); - this.ragSmiley1.TabIndex = 4; - this.ragSmiley1.Visible = false; + ragSmiley1.AlwaysShowHandCursor = false; + ragSmiley1.BackColor = System.Drawing.Color.Transparent; + ragSmiley1.Location = new System.Drawing.Point(924, 117); + ragSmiley1.Margin = new System.Windows.Forms.Padding(5, 3, 5, 3); + ragSmiley1.Name = "ragSmiley1"; + ragSmiley1.Size = new System.Drawing.Size(23, 23); + ragSmiley1.TabIndex = 4; + ragSmiley1.Visible = false; // // btnBrowseForExisting // - this.btnBrowseForExisting.Location = new System.Drawing.Point(711, 99); - this.btnBrowseForExisting.Name = "btnBrowseForExisting"; - this.btnBrowseForExisting.Size = new System.Drawing.Size(75, 23); - this.btnBrowseForExisting.TabIndex = 5; - this.btnBrowseForExisting.Text = "Browse..."; - this.btnBrowseForExisting.UseVisualStyleBackColor = true; - this.btnBrowseForExisting.Click += new System.EventHandler(this.btnBrowseForExisting_Click); + btnBrowseForExisting.Location = new System.Drawing.Point(830, 114); + btnBrowseForExisting.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnBrowseForExisting.Name = "btnBrowseForExisting"; + btnBrowseForExisting.Size = new System.Drawing.Size(88, 27); + btnBrowseForExisting.TabIndex = 5; + btnBrowseForExisting.Text = "Browse..."; + btnBrowseForExisting.UseVisualStyleBackColor = true; + btnBrowseForExisting.Click += btnBrowseForExisting_Click; // // btnCreateNewBrowse // - this.btnCreateNewBrowse.Location = new System.Drawing.Point(711, 33); - this.btnCreateNewBrowse.Name = "btnCreateNewBrowse"; - this.btnCreateNewBrowse.Size = new System.Drawing.Size(75, 23); - this.btnCreateNewBrowse.TabIndex = 5; - this.btnCreateNewBrowse.Text = "Browse..."; - this.btnCreateNewBrowse.UseVisualStyleBackColor = true; - this.btnCreateNewBrowse.Click += new System.EventHandler(this.btnCreateNewBrowse_Click); + btnCreateNewBrowse.Location = new System.Drawing.Point(830, 38); + btnCreateNewBrowse.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnCreateNewBrowse.Name = "btnCreateNewBrowse"; + btnCreateNewBrowse.Size = new System.Drawing.Size(88, 27); + btnCreateNewBrowse.TabIndex = 5; + btnCreateNewBrowse.Text = "Browse..."; + btnCreateNewBrowse.UseVisualStyleBackColor = true; + btnCreateNewBrowse.Click += btnCreateNewBrowse_Click; // // lblDataIsReservedWordExisting // - this.lblDataIsReservedWordExisting.AutoSize = true; - this.lblDataIsReservedWordExisting.ForeColor = System.Drawing.Color.Red; - this.lblDataIsReservedWordExisting.Location = new System.Drawing.Point(72, 124); - this.lblDataIsReservedWordExisting.Name = "lblDataIsReservedWordExisting"; - this.lblDataIsReservedWordExisting.Size = new System.Drawing.Size(206, 13); - this.lblDataIsReservedWordExisting.TabIndex = 6; - this.lblDataIsReservedWordExisting.Text = "Project directories cannot end with \"Data\""; - this.lblDataIsReservedWordExisting.Visible = false; + lblDataIsReservedWordExisting.AutoSize = true; + lblDataIsReservedWordExisting.ForeColor = System.Drawing.Color.Red; + lblDataIsReservedWordExisting.Location = new System.Drawing.Point(84, 143); + lblDataIsReservedWordExisting.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblDataIsReservedWordExisting.Name = "lblDataIsReservedWordExisting"; + lblDataIsReservedWordExisting.Size = new System.Drawing.Size(228, 15); + lblDataIsReservedWordExisting.TabIndex = 6; + lblDataIsReservedWordExisting.Text = "Project directories cannot end with \"Data\""; + lblDataIsReservedWordExisting.Visible = false; // // lblDataIsReservedWordNew // - this.lblDataIsReservedWordNew.AutoSize = true; - this.lblDataIsReservedWordNew.ForeColor = System.Drawing.Color.Red; - this.lblDataIsReservedWordNew.Location = new System.Drawing.Point(72, 58); - this.lblDataIsReservedWordNew.Name = "lblDataIsReservedWordNew"; - this.lblDataIsReservedWordNew.Size = new System.Drawing.Size(206, 13); - this.lblDataIsReservedWordNew.TabIndex = 6; - this.lblDataIsReservedWordNew.Text = "Project directories cannot end with \"Data\""; - this.lblDataIsReservedWordNew.Visible = false; + lblDataIsReservedWordNew.AutoSize = true; + lblDataIsReservedWordNew.ForeColor = System.Drawing.Color.Red; + lblDataIsReservedWordNew.Location = new System.Drawing.Point(84, 67); + lblDataIsReservedWordNew.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblDataIsReservedWordNew.Name = "lblDataIsReservedWordNew"; + lblDataIsReservedWordNew.Size = new System.Drawing.Size(228, 15); + lblDataIsReservedWordNew.TabIndex = 6; + lblDataIsReservedWordNew.Text = "Project directories cannot end with \"Data\""; + lblDataIsReservedWordNew.Visible = false; // // helpIcon1 // - this.helpIcon1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.helpIcon1.Location = new System.Drawing.Point(806, 2); - this.helpIcon1.Name = "helpIcon1"; - this.helpIcon1.Size = new System.Drawing.Size(19, 19); - this.helpIcon1.TabIndex = 7; + helpIcon1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; + helpIcon1.BackColor = System.Drawing.Color.Transparent; + helpIcon1.BackgroundImage = (System.Drawing.Image)resources.GetObject("helpIcon1.BackgroundImage"); + helpIcon1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + helpIcon1.Location = new System.Drawing.Point(950, 2); + helpIcon1.Margin = new System.Windows.Forms.Padding(0); + helpIcon1.MinimumSize = new System.Drawing.Size(26, 25); + helpIcon1.Name = "helpIcon1"; + helpIcon1.Size = new System.Drawing.Size(26, 25); + helpIcon1.SuppressClick = false; + helpIcon1.TabIndex = 7; + // + // rbChooseYourOwn + // + rbChooseYourOwn.AutoSize = true; + rbChooseYourOwn.Location = new System.Drawing.Point(13, 161); + rbChooseYourOwn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbChooseYourOwn.Name = "rbChooseYourOwn"; + rbChooseYourOwn.Size = new System.Drawing.Size(311, 19); + rbChooseYourOwn.TabIndex = 8; + rbChooseYourOwn.TabStop = true; + rbChooseYourOwn.Text = "Use Different Locations For Each Subfolder (advanced)"; + rbChooseYourOwn.UseVisualStyleBackColor = true; + // + // btnBrowseForLoading + // + btnBrowseForLoading.Location = new System.Drawing.Point(830, 184); + btnBrowseForLoading.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnBrowseForLoading.Name = "btnBrowseForLoading"; + btnBrowseForLoading.Size = new System.Drawing.Size(88, 27); + btnBrowseForLoading.TabIndex = 11; + btnBrowseForLoading.Text = "Browse..."; + btnBrowseForLoading.UseVisualStyleBackColor = true; + btnBrowseForLoading.Click += btnBrowseForLoading_Click; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(14, 190); + label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(97, 15); + label3.TabIndex = 10; + label3.Text = "ForLoading Path:"; + label3.Click += label3_Click; + // + // tbForLoadingPath + // + tbForLoadingPath.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + tbForLoadingPath.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; + tbForLoadingPath.Location = new System.Drawing.Point(113, 186); + tbForLoadingPath.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbForLoadingPath.Name = "tbForLoadingPath"; + tbForLoadingPath.Size = new System.Drawing.Size(709, 23); + tbForLoadingPath.TabIndex = 9; + tbForLoadingPath.TextChanged += tbForLoadingPath_TextChanged; + // + // btnBrowseForArchiving + // + btnBrowseForArchiving.Location = new System.Drawing.Point(830, 231); + btnBrowseForArchiving.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnBrowseForArchiving.Name = "btnBrowseForArchiving"; + btnBrowseForArchiving.Size = new System.Drawing.Size(88, 27); + btnBrowseForArchiving.TabIndex = 14; + btnBrowseForArchiving.Text = "Browse..."; + btnBrowseForArchiving.UseVisualStyleBackColor = true; + btnBrowseForArchiving.Click += btnBrowseForArchiving_Click; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(6, 237); + label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(105, 15); + label4.TabIndex = 13; + label4.Text = "ForArchiving Path:"; + // + // tbForArchivingPath + // + tbForArchivingPath.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + tbForArchivingPath.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; + tbForArchivingPath.Location = new System.Drawing.Point(113, 233); + tbForArchivingPath.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbForArchivingPath.Name = "tbForArchivingPath"; + tbForArchivingPath.Size = new System.Drawing.Size(709, 23); + tbForArchivingPath.TabIndex = 12; + tbForArchivingPath.TextChanged += tbForArchivingPath_TextChanged; + // + // btnBrowseExecutables + // + btnBrowseExecutables.Location = new System.Drawing.Point(830, 276); + btnBrowseExecutables.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnBrowseExecutables.Name = "btnBrowseExecutables"; + btnBrowseExecutables.Size = new System.Drawing.Size(88, 27); + btnBrowseExecutables.TabIndex = 17; + btnBrowseExecutables.Text = "Browse..."; + btnBrowseExecutables.UseVisualStyleBackColor = true; + btnBrowseExecutables.Click += btnBrowseForExecutables_Click; + // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(14, 282); + label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(99, 15); + label5.TabIndex = 16; + label5.Text = "Executables Path:"; + // + // tbExecutablesPath + // + tbExecutablesPath.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + tbExecutablesPath.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; + tbExecutablesPath.Location = new System.Drawing.Point(113, 278); + tbExecutablesPath.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbExecutablesPath.Name = "tbExecutablesPath"; + tbExecutablesPath.Size = new System.Drawing.Size(709, 23); + tbExecutablesPath.TabIndex = 15; + tbExecutablesPath.TextChanged += tbExecutablesPath_TextChanged; + // + // btnBrowseCache + // + btnBrowseCache.Location = new System.Drawing.Point(830, 322); + btnBrowseCache.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnBrowseCache.Name = "btnBrowseCache"; + btnBrowseCache.Size = new System.Drawing.Size(88, 27); + btnBrowseCache.TabIndex = 20; + btnBrowseCache.Text = "Browse..."; + btnBrowseCache.UseVisualStyleBackColor = true; + btnBrowseCache.Click += btnBrowseForCache_Click; + // + // label6 + // + label6.AutoSize = true; + label6.Location = new System.Drawing.Point(41, 328); + label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(70, 15); + label6.TabIndex = 19; + label6.Text = "Cache Path:"; + // + // tbCachePath + // + tbCachePath.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + tbCachePath.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; + tbCachePath.Location = new System.Drawing.Point(113, 324); + tbCachePath.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbCachePath.Name = "tbCachePath"; + tbCachePath.Size = new System.Drawing.Size(709, 23); + tbCachePath.TabIndex = 18; + tbCachePath.TextChanged += tbCachePath_TextChanged; + // + // lblForLoadingError + // + lblForLoadingError.AutoSize = true; + lblForLoadingError.ForeColor = System.Drawing.Color.Red; + lblForLoadingError.Location = new System.Drawing.Point(113, 212); + lblForLoadingError.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblForLoadingError.Name = "lblForLoadingError"; + lblForLoadingError.Size = new System.Drawing.Size(187, 15); + lblForLoadingError.TabIndex = 21; + lblForLoadingError.Text = "ForLoading Path cannot be empty"; + lblForLoadingError.Visible = false; + lblForLoadingError.Click += label7_Click; + // + // lblExecutablesError + // + lblExecutablesError.AutoSize = true; + lblExecutablesError.ForeColor = System.Drawing.Color.Red; + lblExecutablesError.Location = new System.Drawing.Point(113, 306); + lblExecutablesError.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblExecutablesError.Name = "lblExecutablesError"; + lblExecutablesError.Size = new System.Drawing.Size(189, 15); + lblExecutablesError.TabIndex = 22; + lblExecutablesError.Text = "Executables Path cannot be empty"; + lblExecutablesError.Visible = false; + // + // lblForArchivingError + // + lblForArchivingError.AutoSize = true; + lblForArchivingError.ForeColor = System.Drawing.Color.Red; + lblForArchivingError.Location = new System.Drawing.Point(113, 260); + lblForArchivingError.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblForArchivingError.Name = "lblForArchivingError"; + lblForArchivingError.Size = new System.Drawing.Size(195, 15); + lblForArchivingError.TabIndex = 23; + lblForArchivingError.Text = "ForArchiving Path cannot be empty"; + lblForArchivingError.Visible = false; + lblForArchivingError.Click += label8_Click; + // + // lblCacheError + // + lblCacheError.AutoSize = true; + lblCacheError.ForeColor = System.Drawing.Color.Red; + lblCacheError.Location = new System.Drawing.Point(113, 350); + lblCacheError.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblCacheError.Name = "lblCacheError"; + lblCacheError.Size = new System.Drawing.Size(160, 15); + lblCacheError.TabIndex = 24; + lblCacheError.Text = "Cache Path cannot be empty"; + lblCacheError.UseMnemonic = false; + lblCacheError.Visible = false; // // ChooseLoadDirectoryUI // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(831, 174); - this.Controls.Add(this.helpIcon1); - this.Controls.Add(this.lblDataIsReservedWordNew); - this.Controls.Add(this.lblDataIsReservedWordExisting); - this.Controls.Add(this.btnCreateNewBrowse); - this.Controls.Add(this.btnBrowseForExisting); - this.Controls.Add(this.ragSmiley1); - this.Controls.Add(this.btnCancel); - this.Controls.Add(this.btnOk); - this.Controls.Add(this.label2); - this.Controls.Add(this.label1); - this.Controls.Add(this.tbUseExisting); - this.Controls.Add(this.tbCreateNew); - this.Controls.Add(this.rbUseExisting); - this.Controls.Add(this.rbCreateNew); - this.Name = "ChooseLoadDirectoryUI"; - this.Text = "Load Directory"; - this.ResumeLayout(false); - this.PerformLayout(); - + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(979, 421); + Controls.Add(lblCacheError); + Controls.Add(lblForArchivingError); + Controls.Add(lblExecutablesError); + Controls.Add(lblForLoadingError); + Controls.Add(btnBrowseCache); + Controls.Add(label6); + Controls.Add(tbCachePath); + Controls.Add(btnBrowseExecutables); + Controls.Add(label5); + Controls.Add(tbExecutablesPath); + Controls.Add(btnBrowseForArchiving); + Controls.Add(label4); + Controls.Add(tbForArchivingPath); + Controls.Add(btnBrowseForLoading); + Controls.Add(label3); + Controls.Add(tbForLoadingPath); + Controls.Add(rbChooseYourOwn); + Controls.Add(helpIcon1); + Controls.Add(lblDataIsReservedWordNew); + Controls.Add(lblDataIsReservedWordExisting); + Controls.Add(btnCreateNewBrowse); + Controls.Add(btnBrowseForExisting); + Controls.Add(ragSmiley1); + Controls.Add(btnCancel); + Controls.Add(btnOk); + Controls.Add(label2); + Controls.Add(label1); + Controls.Add(tbUseExisting); + Controls.Add(tbCreateNew); + Controls.Add(rbUseExisting); + Controls.Add(rbCreateNew); + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + Name = "ChooseLoadDirectoryUI"; + Text = "Load Directory"; + ResumeLayout(false); + PerformLayout(); } #endregion @@ -235,6 +479,22 @@ private void InitializeComponent() private System.Windows.Forms.Label lblDataIsReservedWordExisting; private System.Windows.Forms.Label lblDataIsReservedWordNew; private HelpIcon helpIcon1; - + private System.Windows.Forms.RadioButton rbChooseYourOwn; + private System.Windows.Forms.Button btnBrowseForLoading; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox tbForLoadingPath; + private System.Windows.Forms.Button btnBrowseForArchiving; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox tbForArchivingPath; + private System.Windows.Forms.Button btnBrowseExecutables; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox tbExecutablesPath; + private System.Windows.Forms.Button btnBrowseCache; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox tbCachePath; + private System.Windows.Forms.Label lblForLoadingError; + private System.Windows.Forms.Label lblExecutablesError; + private System.Windows.Forms.Label lblForArchivingError; + private System.Windows.Forms.Label lblCacheError; } } \ No newline at end of file diff --git a/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.cs b/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.cs index 1f86e9b3f4..03f21011fa 100644 --- a/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.cs +++ b/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.cs @@ -33,7 +33,8 @@ public partial class ChooseLoadDirectoryUI : RDMPForm /// /// The users final choice of project directory, also check DialogResult for Ok / Cancel /// - public string Result { get; private set; } + //public string Result { get; private set; } + public LoadDirectory ResultDirectory { get; private set; } private Regex _endsWithDataFolder = new(@"[/\\]Data[/\\ ]*$", RegexOptions.IgnoreCase); @@ -47,24 +48,42 @@ public ChooseLoadDirectoryUI(IActivateItems activator, ILoadMetadata loadMetadat "ILoadMetadata.LocationOfFlatFiles", false, true); helpIcon1.SetHelpText("Location Of Flat Files", help); - - if (!string.IsNullOrWhiteSpace(loadMetadata.LocationOfFlatFiles)) + //if all the same root + var rootDirectory = ((LoadMetadata)loadMetadata).GetRootDirectory(); + if (rootDirectory != null) { - tbUseExisting.Text = loadMetadata.LocationOfFlatFiles; - CheckExistingProjectDirectory(); + tbUseExisting.Text = rootDirectory.FullName; + rbUseExisting.Checked = true; + } + else + { + //if different + tbForLoadingPath.Text = loadMetadata.LocationOfForLoadingDirectory; + tbForArchivingPath.Text = loadMetadata.LocationOfForArchivingDirectory; + tbExecutablesPath.Text = loadMetadata.LocationOfExecutablesDirectory; + tbCachePath.Text = loadMetadata.LocationOfCacheDirectory; + rbChooseYourOwn.Checked = true; + rbCreateNew.Checked = false; + rbUseExisting.Checked = false; } + rb_CheckedChanged(null, null); } private void rb_CheckedChanged(object sender, EventArgs e) { tbCreateNew.Enabled = rbCreateNew.Checked; tbUseExisting.Enabled = rbUseExisting.Checked; + tbForLoadingPath.Enabled = rbChooseYourOwn.Checked; + tbForArchivingPath.Enabled = rbChooseYourOwn.Checked; + tbExecutablesPath.Enabled = rbChooseYourOwn.Checked; + tbCachePath.Enabled = rbChooseYourOwn.Checked; btnOk.Enabled = true; } private void tbUseExisting_Leave(object sender, EventArgs e) { - CheckExistingProjectDirectory(); + if (rbUseExisting.Checked) + CheckExistingProjectDirectory(); } private void CheckExistingProjectDirectory() @@ -92,7 +111,7 @@ private void btnOk_Click(object sender, EventArgs e) if (!dir.Exists) dir.Create(); - Result = LoadDirectory.CreateDirectoryStructure(dir.Parent, dir.Name).RootPath.FullName; + ResultDirectory = LoadDirectory.CreateDirectoryStructure(dir.Parent, dir.Name); DialogResult = DialogResult.OK; Close(); @@ -106,7 +125,7 @@ private void btnOk_Click(object sender, EventArgs e) try { var dir = new LoadDirectory(tbUseExisting.Text); - Result = dir.RootPath.FullName; + ResultDirectory = dir; DialogResult = DialogResult.OK; Close(); } @@ -114,11 +133,47 @@ private void btnOk_Click(object sender, EventArgs e) { if (Activator.YesNo($"Path is invalid, use anyway? ({exception.Message})", "Invalid Path")) { - Result = tbUseExisting.Text; + ResultDirectory = new LoadDirectory(tbUseExisting.Text); DialogResult = DialogResult.OK; Close(); } } + if (rbChooseYourOwn.Checked) + { + var hasError = false; + lblForLoadingError.Visible = false; + lblForArchivingError.Visible = false; + lblExecutablesError.Visible = false; + lblCacheError.Visible = false; + + if (string.IsNullOrWhiteSpace(tbForLoadingPath.Text)) + { + lblForLoadingError.Visible = true; + hasError = true; + } + if (string.IsNullOrWhiteSpace(tbForArchivingPath.Text)) + { + lblForArchivingError.Visible = true; + hasError = true; + } + if (string.IsNullOrWhiteSpace(tbExecutablesPath.Text)) + { + lblExecutablesError.Visible = true; + hasError = true; + } + if (string.IsNullOrWhiteSpace(tbCachePath.Text)) + { + lblCacheError.Visible = true; + hasError = true; + } + if (hasError) return; + var dir = new LoadDirectory(tbForLoadingPath.Text, tbForArchivingPath.Text, tbExecutablesPath.Text, tbCachePath.Text); + ResultDirectory = dir; + DialogResult = DialogResult.OK; + Close(); + } + + } private void btnCancel_Click(object sender, EventArgs e) @@ -149,6 +204,63 @@ private void btnBrowseForExisting_Click(object sender, EventArgs e) } } + private void btnBrowseForLoading_Click(object sender, EventArgs e) + { + using var fbd = new FolderBrowserDialog + { + ShowNewFolderButton = false + }; + + if (fbd.ShowDialog() == DialogResult.OK) + { + tbForLoadingPath.Text = fbd.SelectedPath; + CheckExistingProjectDirectory(); + } + } + + private void btnBrowseForArchiving_Click(object sender, EventArgs e) + { + using var fbd = new FolderBrowserDialog + { + ShowNewFolderButton = false + }; + + if (fbd.ShowDialog() == DialogResult.OK) + { + tbForArchivingPath.Text = fbd.SelectedPath; + CheckExistingProjectDirectory(); + } + } + + private void btnBrowseForExecutables_Click(object sender, EventArgs e) + { + using var fbd = new FolderBrowserDialog + { + ShowNewFolderButton = false + }; + + if (fbd.ShowDialog() == DialogResult.OK) + { + tbExecutablesPath.Text = fbd.SelectedPath; + CheckExistingProjectDirectory(); + } + } + + private void btnBrowseForCache_Click(object sender, EventArgs e) + { + using var fbd = new FolderBrowserDialog + { + ShowNewFolderButton = false + }; + + if (fbd.ShowDialog() == DialogResult.OK) + { + tbCachePath.Text = fbd.SelectedPath; + CheckExistingProjectDirectory(); + } + } + + private void tbUseExisting_TextChanged(object sender, EventArgs e) { lblDataIsReservedWordExisting.Visible = _endsWithDataFolder.IsMatch(tbUseExisting.Text); @@ -158,4 +270,40 @@ private void tbCreateNew_TextChanged(object sender, EventArgs e) { lblDataIsReservedWordNew.Visible = _endsWithDataFolder.IsMatch(tbCreateNew.Text); } + + private void tbForLoadingPath_TextChanged(object sender, EventArgs e) + { + lblForLoadingError.Visible = string.IsNullOrWhiteSpace(tbForLoadingPath.Text); + } + + private void tbForArchivingPath_TextChanged(object sender, EventArgs e) + { + lblForArchivingError.Visible = string.IsNullOrWhiteSpace(tbForArchivingPath.Text); + } + + private void tbCachePath_TextChanged(object sender, EventArgs e) + { + lblCacheError.Visible = string.IsNullOrWhiteSpace(tbCachePath.Text); + } + + private void tbExecutablesPath_TextChanged(object sender, EventArgs e) + { + lblExecutablesError.Visible = string.IsNullOrWhiteSpace(tbExecutablesPath.Text); + } + + + private void label3_Click(object sender, EventArgs e) + { + + } + + private void label7_Click(object sender, EventArgs e) + { + + } + + private void label8_Click(object sender, EventArgs e) + { + + } } \ No newline at end of file diff --git a/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.resx b/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.resx index 1af7de150c..42b95e3b7e 100644 --- a/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.resx +++ b/Rdmp.UI/DataLoadUIs/LoadMetadataUIs/ChooseLoadFolderUI.resx @@ -1,17 +1,17 @@  - @@ -117,4 +117,33 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAABGdBTUEAAK/INwWK6QAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAB3RJTUUH4QURDyAuc4ezXQAAABl0RVh0 + U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAATYSURBVDhPbZPrT5NnGMbfpmm/wn8A/8E2N+eM + W3TObVnSnfzAh3VxRkAEBQGR6TZh8kFFPAxhQhWtbSlSaKHQUuDlbWlLaQstPR8pBctZFNpyKHbGXHsU + lizGK7nyfLp+933dyUP9X6ZImholHomkWcbJF2x9aJszHExxtYEt7pBvkzvo3eD0u5PsPleCpXIkKLUz + sZt8h8xT/1CW6EsOgWbrwy/4w6FtgS6YUjOBLZr2baoHPBuCPleST5ytcSc5xDvBt6UPb1PGye1MJpDi + 6UMpsX8h7fDPbye8s6lX7tgWiF+/iYmZTUePbVUsM6/wOq0rmcS7hF1p/VuULpDK6Pds5OiDm7QzlkqH + FrYwPhkH7VyGanwB/RNLMAWewzWThCkUT8sty3QLM5fzkJnNEGpnd0AEQA36NtlqZ5LH+Nfp8egmAnMb + GHIvQ6qPQaidwUPiB8w0RLoZAp6HI7oG2rUCiX6OrldN8W4rw+x6VYSieh1xVs9EPKvPFReNTW2kfbPr + UNsXcaUrBKFuGp4nCTxNvEBwfh1yyxxqOn3oMD2BPbIKtW0x3aCOiK51+rOuK4IsSj72jN1hWeGPTa07 + PLF1DDiWcVnmxyWpB6H5JDZSL5Ek3tx+iamldVxTeFH92IVu6yyp/QxKy5yjutXFr2nzsKlW4xJHbFgU + WCfjCXNwFc39UZx/5MQFsRPeJ3G4ptdg8D3F5EISz5Pb6B2P4VfROOq6vWDci1CNzSUqH9gFF4UTHKqF + meXep2c1owQ04FgkUz0of2hH6YOdwGNDFNbwCpbWUlhc3YJ8dBrlLWZiC3rGYuixxFB816wpbbZwqca+ + KPeOaoox+J6RqfOoeDSB4vtWnG4excnGEfSSwNNECuH5OCRMGDVSG4oajTjVoIfcFIVidAa5twzMyXoj + l6pTBLm1nQGN1rWMPvsCLkqcb0BFd0049fcIpMOTMHgXcE/jR94tLXJvMMi9yaC4UU9AUciMU/jpypDm + WK2WS5HDcf5sdQs0tvmE1r2E28oAzjSZUUimFzQYUEkqVYvHUXHPhBPXaeJBnKjtx2WJldxrBqKhYOJo + tUaQU9PPoS4K7ezzLTZ+my7qGCawLnMMFaRmQb0eebd1GLDFsLy2BbV1GseuaXCcgApuDkAyFICSbHZD + NuH45oKS/93vvWyqrNnKKm22ZpU1mUUt/aH0qH8ZbcMRnG0ykjpDuNFhh1QbRJ3M9gaWX9eHpl4X6IkY + rkjH09/+1iM6UtGV9VVlN4squmOiCIz9S62OV9w4Qv/V5YbRu4h2QwQ1rVaU3dWh5M4QzjYwuCQ0QTzo + w6A9hiqhBT9eUtH7T7fzDpcr2IfK5Dtf6uerDHX8ujbjhypNTu5NHV3X4UgrzdPoJdVkhjDZLID24SC6 + TBF06MOofmQmGynpPfmtOZ+WtGccONO+A/pP3/+hpo5W9WV+XankHa1Wi6+22RxNKk/insb7ihgCtedV + Q7czUSUcdRwqlYnfzxPzPilqy/y4ULpLeEuHy+XUkQo552BZZ/aBEhn/QHG7YP/px+p9hW303oJW9Z58 + ieC9PBGfgLL35Is5H+SLd5Pv0JEKBfXleQX1xTkF6/NyOftgaQfnsxIZl9Tg7i9q4+4rlHL2nmplf1Qg + YREw9eFJyW7ytSjqX0I4f9SuCRVeAAAAAElFTkSuQmCC + + \ No newline at end of file diff --git a/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.Designer.cs b/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.Designer.cs new file mode 100644 index 0000000000..faad99184a --- /dev/null +++ b/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.Designer.cs @@ -0,0 +1,156 @@ +using Rdmp.Core.CommandExecution.AtomicCommands; +using Rdmp.Core.Curation.Data.Cohort; +using Rdmp.UI.ChecksUI; +using Rdmp.UI.ItemActivation; +using Rdmp.UI.SimpleDialogs; +using Rdmp.UI.SubComponents; +using System; +using System.Linq; +using System.Windows.Forms; +using static Azure.Core.HttpHeader; + +namespace Rdmp.UI.LocationsMenu.Versioning +{ + partial class VersioningControlUI + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + private CohortIdentificationConfiguration _cic; + private IActivateItems _activator; + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + gbTicketing = new System.Windows.Forms.GroupBox(); + btnShowTicket = new System.Windows.Forms.Button(); + tbTicket = new System.Windows.Forms.ComboBox(); + label6 = new System.Windows.Forms.Label(); + gbTicketing.SuspendLayout(); + SuspendLayout(); + // + // gbTicketing + // + gbTicketing.Controls.Add(btnShowTicket); + gbTicketing.Controls.Add(tbTicket); + gbTicketing.Controls.Add(label6); + gbTicketing.Location = new System.Drawing.Point(4, 3); + gbTicketing.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + gbTicketing.Name = "gbTicketing"; + gbTicketing.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); + gbTicketing.Size = new System.Drawing.Size(343, 53); + gbTicketing.TabIndex = 37; + gbTicketing.TabStop = false; + gbTicketing.Text = "Versioning"; + // + // btnShowTicket + // + btnShowTicket.Location = new System.Drawing.Point(241, 19); + btnShowTicket.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnShowTicket.Name = "btnShowTicket"; + btnShowTicket.Size = new System.Drawing.Size(94, 25); + btnShowTicket.TabIndex = 32; + btnShowTicket.Text = "Save Version"; + btnShowTicket.UseVisualStyleBackColor = true; + btnShowTicket.Click += CommitNewVersion; + // + // tbTicket + // + tbTicket.Location = new System.Drawing.Point(50, 20); + tbTicket.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbTicket.Name = "tbTicket"; + tbTicket.Size = new System.Drawing.Size(187, 23); + tbTicket.TabIndex = 30; + tbTicket.SelectionChangeCommitted += VersionChange; + //tbTicket.TextChanged += tbTicket_TextChanged; + // + // label6 + // + label6.AutoSize = true; + label6.Location = new System.Drawing.Point(6, 23); + label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(48, 15); + label6.TabIndex = 31; + label6.Text = "Version:"; + // + // VersioningControlUI + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + Controls.Add(gbTicketing); + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + Name = "VersioningControlUI"; + Size = new System.Drawing.Size(354, 62); + gbTicketing.ResumeLayout(false); + gbTicketing.PerformLayout(); + ResumeLayout(false); + } + + private void VersionChange(object sender, EventArgs e) + { + if (tbTicket.SelectedItem is CohortIdentificationConfiguration ei && ei.ID != _cic.ID) + { + _activator.Activate(ei); + //reset current dropdown + tbTicket.SelectedIndex = 0; + } + } + + private void CommitNewVersion(object sender, EventArgs e) + { + var versions = _cic.GetVersions(); + var cmd = new ExecuteCommandCreateVersionOfCohortConfiguration(_activator, _cic, $"{_cic.Name}-v{versions.Count + 1}-{DateTime.Now.ToString("yyyy-MM-dd")}"); + cmd.Execute(); + versions = _cic.GetVersions(); + versions.Insert(0, _cic); + tbTicket.DataSource = versions; + tbTicket.Enabled = true; + } + + + public void Setup(CohortIdentificationConfiguration databaseObject, IActivateItems activator) + { + _cic = databaseObject; + _activator = activator; + tbTicket.DropDownStyle = ComboBoxStyle.DropDownList; + var versions = databaseObject.GetVersions(); + if (!versions.Any() || databaseObject.Version is not null) + { + tbTicket.Enabled = false; + label6.Enabled = false; + } + if (databaseObject.Version is not null) + { + btnShowTicket.Enabled = false; + } + versions.Insert(0, databaseObject); + tbTicket.DataSource = versions; + } + #endregion + + private System.Windows.Forms.GroupBox gbTicketing; + private System.Windows.Forms.Button btnShowTicket; + private System.Windows.Forms.ComboBox tbTicket; + private System.Windows.Forms.Label label6; + + } +} diff --git a/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.cs b/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.cs new file mode 100644 index 0000000000..3db70fb3aa --- /dev/null +++ b/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.cs @@ -0,0 +1,20 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using Rdmp.UI.TestsAndSetup.ServicePropogation; + +namespace Rdmp.UI.LocationsMenu.Versioning; + +/// +/// This control lets you list and select a version of a cohort identification configuration +/// +public partial class VersioningControlUI : RDMPUserControl +{ + public VersioningControlUI() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.resx b/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.resx new file mode 100644 index 0000000000..af32865ec1 --- /dev/null +++ b/Rdmp.UI/LocationsMenu/Versioning/VersioningControlUI.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Rdmp.UI/Menus/CatalogueMenu.cs b/Rdmp.UI/Menus/CatalogueMenu.cs index 24b80b75ae..207620f4e0 100644 --- a/Rdmp.UI/Menus/CatalogueMenu.cs +++ b/Rdmp.UI/Menus/CatalogueMenu.cs @@ -68,8 +68,10 @@ public CatalogueMenu(RDMPContextMenuStripArgs args, Catalogue catalogue) : base( ////////////////// UI Commands for the CatalogueItems submenu of the Catalogue context menu /////////////////// Add(new ExecuteCommandBulkProcessCatalogueItems(_activator, catalogue) { SuggestedCategory = CatalogueItems, Weight = -99.049f }); + Add(new ExecuteCommandUpdateCatalogueDataLocationUI(_activator, catalogue) + { SuggestedCategory = CatalogueItems, Weight = -99.049f, OverrideCommandName = "Update Catalogue Data Location" }); Add(new ExecuteCommandPasteClipboardAsNewCatalogueItems(_activator, catalogue, Clipboard.GetText) - { SuggestedCategory = CatalogueItems, Weight = -99.047f }); + { SuggestedCategory = CatalogueItems, Weight = -99.047f }); Add(new ExecuteCommandReOrderColumns(_activator, catalogue) { SuggestedCategory = CatalogueItems, Weight = -99.046f }); Add(new ExecuteCommandGuessAssociatedColumns(_activator, catalogue, null) @@ -84,10 +86,10 @@ public CatalogueMenu(RDMPContextMenuStripArgs args, Catalogue catalogue) : base( { foreach (var lmd in catalogue.LoadMetadatas()) { - if (lmd.LocationOfFlatFiles == null) return; + if (lmd.GetRootDirectory() == null) return; try { - var dirReal = new DirectoryInfo(lmd.LocationOfFlatFiles); + var dirReal = lmd.GetRootDirectory(); Add(new ExecuteCommandOpenInExplorer(_activator, dirReal) { OverrideCommandName = "Open Load Directory" }); } diff --git a/Rdmp.UI/Menus/ColumnInfoMenu.cs b/Rdmp.UI/Menus/ColumnInfoMenu.cs index 3031589617..f50e204668 100644 --- a/Rdmp.UI/Menus/ColumnInfoMenu.cs +++ b/Rdmp.UI/Menus/ColumnInfoMenu.cs @@ -22,5 +22,6 @@ public ColumnInfoMenu(RDMPContextMenuStripArgs args, ColumnInfo columnInfo) : ba Add(new ExecuteCommandAddJoinInfo(_activator, columnInfo.TableInfo)); Add(new ExecuteCommandAnonymiseColumnInfo(_activator, columnInfo)); + Add(new ExecuteCommandUpdateCatalogueDataLocationUI(_activator, columnInfo)); } } \ No newline at end of file diff --git a/Rdmp.UI/Menus/LoadModuleAssemblyMenu.cs b/Rdmp.UI/Menus/LoadModuleAssemblyMenu.cs index b3f7a6d831..c948c041ed 100644 --- a/Rdmp.UI/Menus/LoadModuleAssemblyMenu.cs +++ b/Rdmp.UI/Menus/LoadModuleAssemblyMenu.cs @@ -13,6 +13,6 @@ internal class LoadModuleAssemblyMenu : RDMPContextMenuStrip { public LoadModuleAssemblyMenu(RDMPContextMenuStripArgs args, LoadModuleAssembly assembly) : base(args, assembly) { - Add(new ExecuteCommandDeletePlugin(_activator, assembly)); + Add(new ExecuteCommandDeletePlugin(_activator, assembly),System.Windows.Forms.Keys.Delete); } } \ No newline at end of file diff --git a/Rdmp.UI/Rdmp.UI.csproj b/Rdmp.UI/Rdmp.UI.csproj index 851c801cb4..ad5df71f36 100644 --- a/Rdmp.UI/Rdmp.UI.csproj +++ b/Rdmp.UI/Rdmp.UI.csproj @@ -116,6 +116,9 @@ AtomicCommandLinkLabel.cs + + UserControl + UserControl diff --git a/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.Designer.cs b/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.Designer.cs index 77169809a4..8db4332d2f 100644 --- a/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.Designer.cs +++ b/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.Designer.cs @@ -30,430 +30,425 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.label1 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.splitContainer1 = new System.Windows.Forms.SplitContainer(); - this.panel2 = new System.Windows.Forms.Panel(); - this.cbExactMatch = new System.Windows.Forms.CheckBox(); - this.btnClear = new System.Windows.Forms.Button(); - this.checkBox1 = new System.Windows.Forms.CheckBox(); - this.lbPastedColumns = new System.Windows.Forms.ListBox(); - this.splitContainer2 = new System.Windows.Forms.SplitContainer(); - this.panel1 = new System.Windows.Forms.Panel(); - this.tbFilter = new System.Windows.Forms.TextBox(); - this.label3 = new System.Windows.Forms.Label(); - this.olvCatalogueItems = new BrightIdeasSoftware.ObjectListView(); - this.olvName = new BrightIdeasSoftware.OLVColumn(); - this.groupBox2 = new System.Windows.Forms.GroupBox(); - this.btnApplyTransform = new System.Windows.Forms.Button(); - this.rbDeleteExtrctionInformation = new System.Windows.Forms.RadioButton(); - this.rbDeleteAssociatedColumnInfos = new System.Windows.Forms.RadioButton(); - this.rbDelete = new System.Windows.Forms.RadioButton(); - this.label4 = new System.Windows.Forms.Label(); - this.cbTableInfos = new System.Windows.Forms.ComboBox(); - this.rbGuessNewAssociatedColumns = new System.Windows.Forms.RadioButton(); - this.cbRecategorise = new System.Windows.Forms.CheckBox(); - this.panel3 = new System.Windows.Forms.Panel(); - this.ddExtractionCategory = new System.Windows.Forms.ComboBox(); - this.label5 = new System.Windows.Forms.Label(); - this.rbMarkExtractable = new System.Windows.Forms.RadioButton(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); - this.splitContainer1.Panel1.SuspendLayout(); - this.splitContainer1.Panel2.SuspendLayout(); - this.splitContainer1.SuspendLayout(); - this.panel2.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); - this.splitContainer2.Panel1.SuspendLayout(); - this.splitContainer2.Panel2.SuspendLayout(); - this.splitContainer2.SuspendLayout(); - this.panel1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.olvCatalogueItems)).BeginInit(); - this.groupBox2.SuspendLayout(); - this.panel3.SuspendLayout(); - this.SuspendLayout(); + label1 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + splitContainer1 = new System.Windows.Forms.SplitContainer(); + panel2 = new System.Windows.Forms.Panel(); + cbExactMatch = new System.Windows.Forms.CheckBox(); + btnClear = new System.Windows.Forms.Button(); + checkBox1 = new System.Windows.Forms.CheckBox(); + lbPastedColumns = new System.Windows.Forms.ListBox(); + splitContainer2 = new System.Windows.Forms.SplitContainer(); + panel1 = new System.Windows.Forms.Panel(); + tbFilter = new System.Windows.Forms.TextBox(); + label3 = new System.Windows.Forms.Label(); + olvCatalogueItems = new ObjectListView(); + olvName = new OLVColumn(); + groupBox2 = new System.Windows.Forms.GroupBox(); + btnApplyTransform = new System.Windows.Forms.Button(); + rbDeleteExtrctionInformation = new System.Windows.Forms.RadioButton(); + rbDeleteAssociatedColumnInfos = new System.Windows.Forms.RadioButton(); + rbDelete = new System.Windows.Forms.RadioButton(); + label4 = new System.Windows.Forms.Label(); + cbTableInfos = new System.Windows.Forms.ComboBox(); + rbGuessNewAssociatedColumns = new System.Windows.Forms.RadioButton(); + cbRecategorise = new System.Windows.Forms.CheckBox(); + panel3 = new System.Windows.Forms.Panel(); + ddExtractionCategory = new System.Windows.Forms.ComboBox(); + label5 = new System.Windows.Forms.Label(); + rbMarkExtractable = new System.Windows.Forms.RadioButton(); + ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit(); + splitContainer1.Panel1.SuspendLayout(); + splitContainer1.Panel2.SuspendLayout(); + splitContainer1.SuspendLayout(); + panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)splitContainer2).BeginInit(); + splitContainer2.Panel1.SuspendLayout(); + splitContainer2.Panel2.SuspendLayout(); + splitContainer2.SuspendLayout(); + panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)olvCatalogueItems).BeginInit(); + groupBox2.SuspendLayout(); + panel3.SuspendLayout(); + SuspendLayout(); // // label1 // - this.label1.AutoSize = true; - this.label1.Dock = System.Windows.Forms.DockStyle.Top; - this.label1.Location = new System.Drawing.Point(0, 0); - this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(117, 15); - this.label1.TabIndex = 0; - this.label1.Text = "Paste Columns Here:"; + label1.AutoSize = true; + label1.Dock = System.Windows.Forms.DockStyle.Top; + label1.Location = new System.Drawing.Point(0, 0); + label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(117, 15); + label1.TabIndex = 0; + label1.Text = "Paste Columns Here:"; // // label2 // - this.label2.AutoSize = true; - this.label2.Dock = System.Windows.Forms.DockStyle.Top; - this.label2.Location = new System.Drawing.Point(0, 0); - this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(135, 15); - this.label2.TabIndex = 1; - this.label2.Text = "CatalogueItemsAffected"; + label2.AutoSize = true; + label2.Dock = System.Windows.Forms.DockStyle.Top; + label2.Location = new System.Drawing.Point(0, 0); + label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(135, 15); + label2.TabIndex = 1; + label2.Text = "CatalogueItemsAffected"; // // splitContainer1 // - this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer1.Location = new System.Drawing.Point(0, 0); - this.splitContainer1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.splitContainer1.Name = "splitContainer1"; + splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + splitContainer1.Location = new System.Drawing.Point(0, 0); + splitContainer1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + splitContainer1.Name = "splitContainer1"; // // splitContainer1.Panel1 // - this.splitContainer1.Panel1.Controls.Add(this.panel2); - this.splitContainer1.Panel1.Controls.Add(this.checkBox1); - this.splitContainer1.Panel1.Controls.Add(this.lbPastedColumns); - this.splitContainer1.Panel1.Controls.Add(this.label1); + splitContainer1.Panel1.Controls.Add(panel2); + splitContainer1.Panel1.Controls.Add(checkBox1); + splitContainer1.Panel1.Controls.Add(lbPastedColumns); + splitContainer1.Panel1.Controls.Add(label1); // // splitContainer1.Panel2 // - this.splitContainer1.Panel2.Controls.Add(this.splitContainer2); - this.splitContainer1.Size = new System.Drawing.Size(1097, 693); - this.splitContainer1.SplitterDistance = 294; - this.splitContainer1.SplitterWidth = 5; - this.splitContainer1.TabIndex = 2; + splitContainer1.Panel2.Controls.Add(splitContainer2); + splitContainer1.Size = new System.Drawing.Size(1097, 693); + splitContainer1.SplitterDistance = 294; + splitContainer1.SplitterWidth = 5; + splitContainer1.TabIndex = 2; // // panel2 // - this.panel2.Controls.Add(this.cbExactMatch); - this.panel2.Controls.Add(this.btnClear); - this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; - this.panel2.Location = new System.Drawing.Point(0, 661); - this.panel2.Name = "panel2"; - this.panel2.Size = new System.Drawing.Size(294, 32); - this.panel2.TabIndex = 8; + panel2.Controls.Add(cbExactMatch); + panel2.Controls.Add(btnClear); + panel2.Dock = System.Windows.Forms.DockStyle.Bottom; + panel2.Location = new System.Drawing.Point(0, 661); + panel2.Name = "panel2"; + panel2.Size = new System.Drawing.Size(294, 32); + panel2.TabIndex = 8; // // cbExactMatch // - this.cbExactMatch.AutoSize = true; - this.cbExactMatch.Checked = true; - this.cbExactMatch.CheckState = System.Windows.Forms.CheckState.Checked; - this.cbExactMatch.Dock = System.Windows.Forms.DockStyle.Fill; - this.cbExactMatch.Location = new System.Drawing.Point(88, 0); - this.cbExactMatch.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.cbExactMatch.Name = "cbExactMatch"; - this.cbExactMatch.Size = new System.Drawing.Size(206, 32); - this.cbExactMatch.TabIndex = 4; - this.cbExactMatch.Text = "Exact Matches Only"; - this.cbExactMatch.UseVisualStyleBackColor = true; - this.cbExactMatch.CheckedChanged += new System.EventHandler(this.cbExactMatch_CheckedChanged); + cbExactMatch.AutoSize = true; + cbExactMatch.Checked = true; + cbExactMatch.CheckState = System.Windows.Forms.CheckState.Checked; + cbExactMatch.Dock = System.Windows.Forms.DockStyle.Fill; + cbExactMatch.Location = new System.Drawing.Point(88, 0); + cbExactMatch.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + cbExactMatch.Name = "cbExactMatch"; + cbExactMatch.Size = new System.Drawing.Size(206, 32); + cbExactMatch.TabIndex = 4; + cbExactMatch.Text = "Exact Matches Only"; + cbExactMatch.UseVisualStyleBackColor = true; + cbExactMatch.CheckedChanged += cbExactMatch_CheckedChanged; // // btnClear // - this.btnClear.Dock = System.Windows.Forms.DockStyle.Left; - this.btnClear.Location = new System.Drawing.Point(0, 0); - this.btnClear.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.btnClear.Name = "btnClear"; - this.btnClear.Size = new System.Drawing.Size(88, 32); - this.btnClear.TabIndex = 2; - this.btnClear.Text = "Clear"; - this.btnClear.UseVisualStyleBackColor = true; - this.btnClear.Click += new System.EventHandler(this.btnClear_Click); + btnClear.Dock = System.Windows.Forms.DockStyle.Left; + btnClear.Location = new System.Drawing.Point(0, 0); + btnClear.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnClear.Name = "btnClear"; + btnClear.Size = new System.Drawing.Size(88, 32); + btnClear.TabIndex = 2; + btnClear.Text = "Clear"; + btnClear.UseVisualStyleBackColor = true; + btnClear.Click += btnClear_Click; // // checkBox1 // - this.checkBox1.AutoSize = true; - this.checkBox1.Location = new System.Drawing.Point(-18, -17); - this.checkBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(83, 19); - this.checkBox1.TabIndex = 3; - this.checkBox1.Text = "checkBox1"; - this.checkBox1.UseVisualStyleBackColor = true; + checkBox1.AutoSize = true; + checkBox1.Location = new System.Drawing.Point(-18, -17); + checkBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + checkBox1.Name = "checkBox1"; + checkBox1.Size = new System.Drawing.Size(83, 19); + checkBox1.TabIndex = 3; + checkBox1.Text = "checkBox1"; + checkBox1.UseVisualStyleBackColor = true; // // lbPastedColumns // - this.lbPastedColumns.Dock = System.Windows.Forms.DockStyle.Fill; - this.lbPastedColumns.FormattingEnabled = true; - this.lbPastedColumns.ItemHeight = 15; - this.lbPastedColumns.Location = new System.Drawing.Point(0, 15); - this.lbPastedColumns.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.lbPastedColumns.Name = "lbPastedColumns"; - this.lbPastedColumns.Size = new System.Drawing.Size(294, 678); - this.lbPastedColumns.TabIndex = 1; - this.lbPastedColumns.KeyUp += new System.Windows.Forms.KeyEventHandler(this.lbPastedColumns_KeyUp); + lbPastedColumns.Dock = System.Windows.Forms.DockStyle.Fill; + lbPastedColumns.FormattingEnabled = true; + lbPastedColumns.ItemHeight = 15; + lbPastedColumns.Location = new System.Drawing.Point(0, 15); + lbPastedColumns.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + lbPastedColumns.Name = "lbPastedColumns"; + lbPastedColumns.Size = new System.Drawing.Size(294, 678); + lbPastedColumns.TabIndex = 1; + lbPastedColumns.KeyUp += lbPastedColumns_KeyUp; // // splitContainer2 // - this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer2.Location = new System.Drawing.Point(0, 0); - this.splitContainer2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.splitContainer2.Name = "splitContainer2"; + splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; + splitContainer2.Location = new System.Drawing.Point(0, 0); + splitContainer2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + splitContainer2.Name = "splitContainer2"; // // splitContainer2.Panel1 // - this.splitContainer2.Panel1.Controls.Add(this.panel1); - this.splitContainer2.Panel1.Controls.Add(this.olvCatalogueItems); - this.splitContainer2.Panel1.Controls.Add(this.label2); + splitContainer2.Panel1.Controls.Add(panel1); + splitContainer2.Panel1.Controls.Add(olvCatalogueItems); + splitContainer2.Panel1.Controls.Add(label2); // // splitContainer2.Panel2 // - this.splitContainer2.Panel2.Controls.Add(this.groupBox2); - this.splitContainer2.Size = new System.Drawing.Size(798, 693); - this.splitContainer2.SplitterDistance = 325; - this.splitContainer2.SplitterWidth = 5; - this.splitContainer2.TabIndex = 3; + splitContainer2.Panel2.Controls.Add(groupBox2); + splitContainer2.Size = new System.Drawing.Size(798, 693); + splitContainer2.SplitterDistance = 325; + splitContainer2.SplitterWidth = 5; + splitContainer2.TabIndex = 3; // // panel1 // - this.panel1.Controls.Add(this.tbFilter); - this.panel1.Controls.Add(this.label3); - this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; - this.panel1.Location = new System.Drawing.Point(0, 661); - this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(325, 32); - this.panel1.TabIndex = 8; + panel1.Controls.Add(tbFilter); + panel1.Controls.Add(label3); + panel1.Dock = System.Windows.Forms.DockStyle.Bottom; + panel1.Location = new System.Drawing.Point(0, 661); + panel1.Name = "panel1"; + panel1.Size = new System.Drawing.Size(325, 32); + panel1.TabIndex = 8; // // tbFilter // - this.tbFilter.Dock = System.Windows.Forms.DockStyle.Fill; - this.tbFilter.Location = new System.Drawing.Point(36, 0); - this.tbFilter.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.tbFilter.Name = "tbFilter"; - this.tbFilter.Size = new System.Drawing.Size(289, 23); - this.tbFilter.TabIndex = 3; - this.tbFilter.TextChanged += new System.EventHandler(this.tbFilter_TextChanged); + tbFilter.Dock = System.Windows.Forms.DockStyle.Fill; + tbFilter.Location = new System.Drawing.Point(36, 0); + tbFilter.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tbFilter.Name = "tbFilter"; + tbFilter.Size = new System.Drawing.Size(289, 23); + tbFilter.TabIndex = 3; + tbFilter.TextChanged += tbFilter_TextChanged; // // label3 // - this.label3.AutoSize = true; - this.label3.Dock = System.Windows.Forms.DockStyle.Left; - this.label3.Location = new System.Drawing.Point(0, 0); - this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(36, 15); - this.label3.TabIndex = 2; - this.label3.Text = "Filter:"; + label3.AutoSize = true; + label3.Dock = System.Windows.Forms.DockStyle.Left; + label3.Location = new System.Drawing.Point(0, 0); + label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(36, 15); + label3.TabIndex = 2; + label3.Text = "Filter:"; // // olvCatalogueItems // - this.olvCatalogueItems.AllColumns.Add(this.olvName); - this.olvCatalogueItems.CellEditUseWholeCell = false; - this.olvCatalogueItems.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.olvName}); - this.olvCatalogueItems.Cursor = System.Windows.Forms.Cursors.Default; - this.olvCatalogueItems.Dock = System.Windows.Forms.DockStyle.Fill; - this.olvCatalogueItems.HideSelection = false; - this.olvCatalogueItems.Location = new System.Drawing.Point(0, 15); - this.olvCatalogueItems.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.olvCatalogueItems.Name = "olvCatalogueItems"; - this.olvCatalogueItems.RowHeight = 19; - this.olvCatalogueItems.ShowGroups = false; - this.olvCatalogueItems.Size = new System.Drawing.Size(325, 678); - this.olvCatalogueItems.TabIndex = 1; - this.olvCatalogueItems.UseCompatibleStateImageBehavior = false; - this.olvCatalogueItems.View = System.Windows.Forms.View.Details; + olvCatalogueItems.AllColumns.Add(olvName); + olvCatalogueItems.CellEditUseWholeCell = false; + olvCatalogueItems.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { olvName }); + olvCatalogueItems.Dock = System.Windows.Forms.DockStyle.Fill; + olvCatalogueItems.Location = new System.Drawing.Point(0, 15); + olvCatalogueItems.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + olvCatalogueItems.Name = "olvCatalogueItems"; + olvCatalogueItems.RowHeight = 19; + olvCatalogueItems.ShowGroups = false; + olvCatalogueItems.Size = new System.Drawing.Size(325, 678); + olvCatalogueItems.TabIndex = 1; + olvCatalogueItems.UseCompatibleStateImageBehavior = false; + olvCatalogueItems.View = System.Windows.Forms.View.Details; // // olvName // - this.olvName.AspectName = "ToString"; - this.olvName.FillsFreeSpace = true; - this.olvName.MinimumWidth = 100; - this.olvName.Width = 100; + olvName.AspectName = "ToString"; + olvName.FillsFreeSpace = true; + olvName.MinimumWidth = 100; + olvName.Width = 100; // // groupBox2 // - this.groupBox2.Controls.Add(this.btnApplyTransform); - this.groupBox2.Controls.Add(this.rbDeleteExtrctionInformation); - this.groupBox2.Controls.Add(this.rbDeleteAssociatedColumnInfos); - this.groupBox2.Controls.Add(this.rbDelete); - this.groupBox2.Controls.Add(this.label4); - this.groupBox2.Controls.Add(this.cbTableInfos); - this.groupBox2.Controls.Add(this.rbGuessNewAssociatedColumns); - this.groupBox2.Controls.Add(this.cbRecategorise); - this.groupBox2.Controls.Add(this.panel3); - this.groupBox2.Controls.Add(this.rbMarkExtractable); - this.groupBox2.Dock = System.Windows.Forms.DockStyle.Fill; - this.groupBox2.Location = new System.Drawing.Point(0, 0); - this.groupBox2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.groupBox2.Name = "groupBox2"; - this.groupBox2.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.groupBox2.Size = new System.Drawing.Size(468, 693); - this.groupBox2.TabIndex = 4; - this.groupBox2.TabStop = false; - this.groupBox2.Text = "Transform:"; - this.groupBox2.Enter += new System.EventHandler(this.groupBox2_Enter); + groupBox2.Controls.Add(btnApplyTransform); + groupBox2.Controls.Add(rbDeleteExtrctionInformation); + groupBox2.Controls.Add(rbDeleteAssociatedColumnInfos); + groupBox2.Controls.Add(rbDelete); + groupBox2.Controls.Add(label4); + groupBox2.Controls.Add(cbTableInfos); + groupBox2.Controls.Add(rbGuessNewAssociatedColumns); + groupBox2.Controls.Add(cbRecategorise); + groupBox2.Controls.Add(panel3); + groupBox2.Controls.Add(rbMarkExtractable); + groupBox2.Dock = System.Windows.Forms.DockStyle.Fill; + groupBox2.Location = new System.Drawing.Point(0, 0); + groupBox2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + groupBox2.Name = "groupBox2"; + groupBox2.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); + groupBox2.Size = new System.Drawing.Size(468, 693); + groupBox2.TabIndex = 4; + groupBox2.TabStop = false; + groupBox2.Text = "Transform:"; + groupBox2.Enter += groupBox2_Enter; // // btnApplyTransform // - this.btnApplyTransform.Dock = System.Windows.Forms.DockStyle.Top; - this.btnApplyTransform.Location = new System.Drawing.Point(4, 223); - this.btnApplyTransform.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.btnApplyTransform.Name = "btnApplyTransform"; - this.btnApplyTransform.Size = new System.Drawing.Size(460, 27); - this.btnApplyTransform.TabIndex = 4; - this.btnApplyTransform.Text = "Apply Transform"; - this.btnApplyTransform.UseVisualStyleBackColor = true; - this.btnApplyTransform.Click += new System.EventHandler(this.btnApplyTransform_Click); + btnApplyTransform.Dock = System.Windows.Forms.DockStyle.Top; + btnApplyTransform.Location = new System.Drawing.Point(4, 223); + btnApplyTransform.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnApplyTransform.Name = "btnApplyTransform"; + btnApplyTransform.Size = new System.Drawing.Size(460, 27); + btnApplyTransform.TabIndex = 4; + btnApplyTransform.Text = "Apply Transform"; + btnApplyTransform.UseVisualStyleBackColor = true; + btnApplyTransform.Click += btnApplyTransform_Click; // // rbDeleteExtrctionInformation // - this.rbDeleteExtrctionInformation.AutoSize = true; - this.rbDeleteExtrctionInformation.Dock = System.Windows.Forms.DockStyle.Top; - this.rbDeleteExtrctionInformation.Location = new System.Drawing.Point(4, 204); - this.rbDeleteExtrctionInformation.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.rbDeleteExtrctionInformation.Name = "rbDeleteExtrctionInformation"; - this.rbDeleteExtrctionInformation.Size = new System.Drawing.Size(460, 19); - this.rbDeleteExtrctionInformation.TabIndex = 3; - this.rbDeleteExtrctionInformation.TabStop = true; - this.rbDeleteExtrctionInformation.Text = "Delete Extraction Information"; - this.rbDeleteExtrctionInformation.UseVisualStyleBackColor = true; + rbDeleteExtrctionInformation.AutoSize = true; + rbDeleteExtrctionInformation.Dock = System.Windows.Forms.DockStyle.Top; + rbDeleteExtrctionInformation.Location = new System.Drawing.Point(4, 204); + rbDeleteExtrctionInformation.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbDeleteExtrctionInformation.Name = "rbDeleteExtrctionInformation"; + rbDeleteExtrctionInformation.Size = new System.Drawing.Size(460, 19); + rbDeleteExtrctionInformation.TabIndex = 3; + rbDeleteExtrctionInformation.TabStop = true; + rbDeleteExtrctionInformation.Text = "Delete Extraction Information"; + rbDeleteExtrctionInformation.UseVisualStyleBackColor = true; // // rbDeleteAssociatedColumnInfos // - this.rbDeleteAssociatedColumnInfos.AutoSize = true; - this.rbDeleteAssociatedColumnInfos.Dock = System.Windows.Forms.DockStyle.Top; - this.rbDeleteAssociatedColumnInfos.Location = new System.Drawing.Point(4, 185); - this.rbDeleteAssociatedColumnInfos.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.rbDeleteAssociatedColumnInfos.Name = "rbDeleteAssociatedColumnInfos"; - this.rbDeleteAssociatedColumnInfos.Size = new System.Drawing.Size(460, 19); - this.rbDeleteAssociatedColumnInfos.TabIndex = 3; - this.rbDeleteAssociatedColumnInfos.TabStop = true; - this.rbDeleteAssociatedColumnInfos.Text = "Delete Associated ColumnInfos (will also delete extraction information)"; - this.rbDeleteAssociatedColumnInfos.UseVisualStyleBackColor = true; + rbDeleteAssociatedColumnInfos.AutoSize = true; + rbDeleteAssociatedColumnInfos.Dock = System.Windows.Forms.DockStyle.Top; + rbDeleteAssociatedColumnInfos.Location = new System.Drawing.Point(4, 185); + rbDeleteAssociatedColumnInfos.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbDeleteAssociatedColumnInfos.Name = "rbDeleteAssociatedColumnInfos"; + rbDeleteAssociatedColumnInfos.Size = new System.Drawing.Size(460, 19); + rbDeleteAssociatedColumnInfos.TabIndex = 3; + rbDeleteAssociatedColumnInfos.TabStop = true; + rbDeleteAssociatedColumnInfos.Text = "Delete Associated ColumnInfos (will also delete extraction information)"; + rbDeleteAssociatedColumnInfos.UseVisualStyleBackColor = true; // // rbDelete // - this.rbDelete.AutoSize = true; - this.rbDelete.Dock = System.Windows.Forms.DockStyle.Top; - this.rbDelete.Location = new System.Drawing.Point(4, 166); - this.rbDelete.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.rbDelete.Name = "rbDelete"; - this.rbDelete.Size = new System.Drawing.Size(460, 19); - this.rbDelete.TabIndex = 3; - this.rbDelete.TabStop = true; - this.rbDelete.Text = "Delete Catalogue Items"; - this.rbDelete.UseVisualStyleBackColor = true; + rbDelete.AutoSize = true; + rbDelete.Dock = System.Windows.Forms.DockStyle.Top; + rbDelete.Location = new System.Drawing.Point(4, 166); + rbDelete.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbDelete.Name = "rbDelete"; + rbDelete.Size = new System.Drawing.Size(460, 19); + rbDelete.TabIndex = 3; + rbDelete.TabStop = true; + rbDelete.Text = "Delete Catalogue Items"; + rbDelete.UseVisualStyleBackColor = true; // // label4 // - this.label4.Dock = System.Windows.Forms.DockStyle.Top; - this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point); - this.label4.Location = new System.Drawing.Point(4, 127); - this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(460, 39); - this.label4.TabIndex = 1; - this.label4.Text = "(every matching CatalogueItem will have an associated ColumnInfo guessed from the" + - " TableInfo you select in the box above)"; + label4.Dock = System.Windows.Forms.DockStyle.Top; + label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic); + label4.Location = new System.Drawing.Point(4, 127); + label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(460, 39); + label4.TabIndex = 1; + label4.Text = "(every matching CatalogueItem will have an associated ColumnInfo guessed from the TableInfo you select in the box above)"; // // cbTableInfos // - this.cbTableInfos.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest; - this.cbTableInfos.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; - this.cbTableInfos.Dock = System.Windows.Forms.DockStyle.Top; - this.cbTableInfos.FormattingEnabled = true; - this.cbTableInfos.Location = new System.Drawing.Point(4, 104); - this.cbTableInfos.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.cbTableInfos.Name = "cbTableInfos"; - this.cbTableInfos.Size = new System.Drawing.Size(460, 23); - this.cbTableInfos.TabIndex = 2; - this.cbTableInfos.SelectedIndexChanged += new System.EventHandler(this.cbTableInfos_SelectedIndexChanged); + cbTableInfos.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest; + cbTableInfos.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + cbTableInfos.Dock = System.Windows.Forms.DockStyle.Top; + cbTableInfos.FormattingEnabled = true; + cbTableInfos.Location = new System.Drawing.Point(4, 104); + cbTableInfos.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + cbTableInfos.Name = "cbTableInfos"; + cbTableInfos.Size = new System.Drawing.Size(460, 23); + cbTableInfos.TabIndex = 2; + cbTableInfos.SelectedIndexChanged += cbTableInfos_SelectedIndexChanged; // // rbGuessNewAssociatedColumns // - this.rbGuessNewAssociatedColumns.AutoSize = true; - this.rbGuessNewAssociatedColumns.Dock = System.Windows.Forms.DockStyle.Top; - this.rbGuessNewAssociatedColumns.Location = new System.Drawing.Point(4, 85); - this.rbGuessNewAssociatedColumns.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.rbGuessNewAssociatedColumns.Name = "rbGuessNewAssociatedColumns"; - this.rbGuessNewAssociatedColumns.Size = new System.Drawing.Size(460, 19); - this.rbGuessNewAssociatedColumns.TabIndex = 0; - this.rbGuessNewAssociatedColumns.TabStop = true; - this.rbGuessNewAssociatedColumns.Text = "Guess New Associated Columns"; - this.rbGuessNewAssociatedColumns.UseVisualStyleBackColor = true; + rbGuessNewAssociatedColumns.AutoSize = true; + rbGuessNewAssociatedColumns.Dock = System.Windows.Forms.DockStyle.Top; + rbGuessNewAssociatedColumns.Location = new System.Drawing.Point(4, 85); + rbGuessNewAssociatedColumns.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbGuessNewAssociatedColumns.Name = "rbGuessNewAssociatedColumns"; + rbGuessNewAssociatedColumns.Size = new System.Drawing.Size(460, 19); + rbGuessNewAssociatedColumns.TabIndex = 0; + rbGuessNewAssociatedColumns.TabStop = true; + rbGuessNewAssociatedColumns.Text = "Guess New Associated Columns"; + rbGuessNewAssociatedColumns.UseVisualStyleBackColor = true; // // cbRecategorise // - this.cbRecategorise.AutoSize = true; - this.cbRecategorise.Dock = System.Windows.Forms.DockStyle.Top; - this.cbRecategorise.Location = new System.Drawing.Point(4, 66); - this.cbRecategorise.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.cbRecategorise.Name = "cbRecategorise"; - this.cbRecategorise.Size = new System.Drawing.Size(460, 19); - this.cbRecategorise.TabIndex = 7; - this.cbRecategorise.Text = "Recategorise Matching CatalogueItems (that are already extractable)"; - this.cbRecategorise.UseVisualStyleBackColor = true; + cbRecategorise.AutoSize = true; + cbRecategorise.Dock = System.Windows.Forms.DockStyle.Top; + cbRecategorise.Location = new System.Drawing.Point(4, 66); + cbRecategorise.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + cbRecategorise.Name = "cbRecategorise"; + cbRecategorise.Size = new System.Drawing.Size(460, 19); + cbRecategorise.TabIndex = 7; + cbRecategorise.Text = "Recategorise Matching CatalogueItems (that are already extractable)"; + cbRecategorise.UseVisualStyleBackColor = true; // // panel3 // - this.panel3.Controls.Add(this.ddExtractionCategory); - this.panel3.Controls.Add(this.label5); - this.panel3.Dock = System.Windows.Forms.DockStyle.Top; - this.panel3.Location = new System.Drawing.Point(4, 38); - this.panel3.Name = "panel3"; - this.panel3.Size = new System.Drawing.Size(460, 28); - this.panel3.TabIndex = 8; + panel3.Controls.Add(ddExtractionCategory); + panel3.Controls.Add(label5); + panel3.Dock = System.Windows.Forms.DockStyle.Top; + panel3.Location = new System.Drawing.Point(4, 38); + panel3.Name = "panel3"; + panel3.Size = new System.Drawing.Size(460, 28); + panel3.TabIndex = 8; // // ddExtractionCategory // - this.ddExtractionCategory.Dock = System.Windows.Forms.DockStyle.Fill; - this.ddExtractionCategory.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.ddExtractionCategory.FormattingEnabled = true; - this.ddExtractionCategory.Location = new System.Drawing.Point(58, 0); - this.ddExtractionCategory.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.ddExtractionCategory.Name = "ddExtractionCategory"; - this.ddExtractionCategory.Size = new System.Drawing.Size(402, 23); - this.ddExtractionCategory.TabIndex = 5; - this.ddExtractionCategory.SelectedIndexChanged += new System.EventHandler(this.ddExtractionCategory_SelectedIndexChanged); + ddExtractionCategory.Dock = System.Windows.Forms.DockStyle.Fill; + ddExtractionCategory.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + ddExtractionCategory.FormattingEnabled = true; + ddExtractionCategory.Location = new System.Drawing.Point(58, 0); + ddExtractionCategory.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + ddExtractionCategory.Name = "ddExtractionCategory"; + ddExtractionCategory.Size = new System.Drawing.Size(402, 23); + ddExtractionCategory.TabIndex = 5; + ddExtractionCategory.SelectedIndexChanged += ddExtractionCategory_SelectedIndexChanged; // // label5 // - this.label5.AutoSize = true; - this.label5.Dock = System.Windows.Forms.DockStyle.Left; - this.label5.Location = new System.Drawing.Point(0, 0); - this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(58, 15); - this.label5.TabIndex = 6; - this.label5.Text = "Category:"; + label5.AutoSize = true; + label5.Dock = System.Windows.Forms.DockStyle.Left; + label5.Location = new System.Drawing.Point(0, 0); + label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(58, 15); + label5.TabIndex = 6; + label5.Text = "Category:"; // // rbMarkExtractable // - this.rbMarkExtractable.AutoSize = true; - this.rbMarkExtractable.Dock = System.Windows.Forms.DockStyle.Top; - this.rbMarkExtractable.Location = new System.Drawing.Point(4, 19); - this.rbMarkExtractable.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.rbMarkExtractable.Name = "rbMarkExtractable"; - this.rbMarkExtractable.Size = new System.Drawing.Size(460, 19); - this.rbMarkExtractable.TabIndex = 0; - this.rbMarkExtractable.TabStop = true; - this.rbMarkExtractable.Text = "Mark Associated Columns Extractable"; - this.rbMarkExtractable.UseVisualStyleBackColor = true; - this.rbMarkExtractable.CheckedChanged += new System.EventHandler(this.rbMarkExtractable_CheckedChanged); + rbMarkExtractable.AutoSize = true; + rbMarkExtractable.Dock = System.Windows.Forms.DockStyle.Top; + rbMarkExtractable.Location = new System.Drawing.Point(4, 19); + rbMarkExtractable.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + rbMarkExtractable.Name = "rbMarkExtractable"; + rbMarkExtractable.Size = new System.Drawing.Size(460, 19); + rbMarkExtractable.TabIndex = 0; + rbMarkExtractable.TabStop = true; + rbMarkExtractable.Text = "Mark Associated Columns Extractable"; + rbMarkExtractable.UseVisualStyleBackColor = true; + rbMarkExtractable.CheckedChanged += rbMarkExtractable_CheckedChanged; // // BulkProcessCatalogueItemsUI // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.splitContainer1); - this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.Name = "BulkProcessCatalogueItemsUI"; - this.Size = new System.Drawing.Size(1097, 693); - this.splitContainer1.Panel1.ResumeLayout(false); - this.splitContainer1.Panel1.PerformLayout(); - this.splitContainer1.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); - this.splitContainer1.ResumeLayout(false); - this.panel2.ResumeLayout(false); - this.panel2.PerformLayout(); - this.splitContainer2.Panel1.ResumeLayout(false); - this.splitContainer2.Panel1.PerformLayout(); - this.splitContainer2.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); - this.splitContainer2.ResumeLayout(false); - this.panel1.ResumeLayout(false); - this.panel1.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.olvCatalogueItems)).EndInit(); - this.groupBox2.ResumeLayout(false); - this.groupBox2.PerformLayout(); - this.panel3.ResumeLayout(false); - this.panel3.PerformLayout(); - this.ResumeLayout(false); - + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + Controls.Add(splitContainer1); + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + Name = "BulkProcessCatalogueItemsUI"; + Size = new System.Drawing.Size(1097, 693); + splitContainer1.Panel1.ResumeLayout(false); + splitContainer1.Panel1.PerformLayout(); + splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)splitContainer1).EndInit(); + splitContainer1.ResumeLayout(false); + panel2.ResumeLayout(false); + panel2.PerformLayout(); + splitContainer2.Panel1.ResumeLayout(false); + splitContainer2.Panel1.PerformLayout(); + splitContainer2.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)splitContainer2).EndInit(); + splitContainer2.ResumeLayout(false); + panel1.ResumeLayout(false); + panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)olvCatalogueItems).EndInit(); + groupBox2.ResumeLayout(false); + groupBox2.PerformLayout(); + panel3.ResumeLayout(false); + panel3.PerformLayout(); + ResumeLayout(false); } #endregion diff --git a/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.resx b/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.resx index f298a7be80..af32865ec1 100644 --- a/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.resx +++ b/Rdmp.UI/SimpleDialogs/BulkProcessCatalogueItemsUI.resx @@ -1,4 +1,64 @@ - + + + diff --git a/Rdmp.UI/SimpleDialogs/InstanceSettings.Designer.cs b/Rdmp.UI/SimpleDialogs/InstanceSettings.Designer.cs new file mode 100644 index 0000000000..8a7b4da3c8 --- /dev/null +++ b/Rdmp.UI/SimpleDialogs/InstanceSettings.Designer.cs @@ -0,0 +1,76 @@ +namespace Rdmp.UI.SimpleDialogs +{ + partial class InstanceSettings + { + /// + /// 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() + { + components = new System.ComponentModel.Container(); + cbAutoSuggestProjectNumbers = new System.Windows.Forms.CheckBox(); + instanceSettingsToolTips = new System.Windows.Forms.ToolTip(components); + label1 = new System.Windows.Forms.Label(); + SuspendLayout(); + // + // cbAutoSuggestProjectNumbers + // + cbAutoSuggestProjectNumbers.AutoSize = true; + cbAutoSuggestProjectNumbers.Location = new System.Drawing.Point(13, 40); + cbAutoSuggestProjectNumbers.Name = "cbAutoSuggestProjectNumbers"; + cbAutoSuggestProjectNumbers.Size = new System.Drawing.Size(364, 19); + cbAutoSuggestProjectNumbers.TabIndex = 0; + cbAutoSuggestProjectNumbers.Text = "Automatically Suggest Project Numbers During Project Creation"; + cbAutoSuggestProjectNumbers.UseVisualStyleBackColor = true; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(13, 9); + label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(308, 15); + label1.TabIndex = 2; + label1.Text = "Settings will automatically be Saved as you change them "; + // + // InstanceSettings + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(800, 450); + Controls.Add(label1); + Controls.Add(cbAutoSuggestProjectNumbers); + Name = "InstanceSettings"; + Text = "InstanceSettings"; + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private System.Windows.Forms.CheckBox cbAutoSuggestProjectNumbers; + private System.Windows.Forms.ToolTip instanceSettingsToolTips; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/Rdmp.UI/SimpleDialogs/InstanceSettings.cs b/Rdmp.UI/SimpleDialogs/InstanceSettings.cs new file mode 100644 index 0000000000..5713fc9712 --- /dev/null +++ b/Rdmp.UI/SimpleDialogs/InstanceSettings.cs @@ -0,0 +1,79 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . +using Rdmp.Core.ReusableLibraryCode; +using Rdmp.Core.Setting; +using Rdmp.UI.ItemActivation; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Windows.Forms; + +namespace Rdmp.UI.SimpleDialogs +{ + public partial class InstanceSettings : Form + { + private readonly IActivateItems _activator; + private bool _loaded; + private Setting[] _settings; + + public InstanceSettings(IActivateItems activator) + { + InitializeComponent(); + _activator = activator; + _settings = _activator.RepositoryLocator.CatalogueRepository.GetAllObjects(); + + RegisterCheckbox(cbAutoSuggestProjectNumbers, "AutoSuggestProjectNumbers"); + _loaded = true; + } + + private void CheckboxCheckedChanged(object sender, EventArgs e) + { + if (!_loaded) + return; + + var cb = (CheckBox)sender; + var mappedSetting = checkboxDictionary.GetValueOrDefault(cb); + if (mappedSetting != null) + { + mappedSetting.Value = Convert.ToString(cb.Checked); + mappedSetting.SaveToDatabase(); + } + } + private Dictionary checkboxDictionary = new(); + + + private void AddTooltip(Control c, string propertyName) + { + var helpText = + _activator.CommentStore.GetDocumentationIfExists($"{nameof(InstanceSettings)}.{propertyName}", false); + if (string.IsNullOrWhiteSpace(helpText)) return; + + instanceSettingsToolTips.SetToolTip(c, UsefulStuff.SplitByLength(helpText, 100)); + } + + private void RegisterCheckbox(CheckBox cb, string propertyName) + { + var prop = _settings.Where(s => s.Key == propertyName).FirstOrDefault(); + var value = false; + if (prop is null) + { + prop = new Setting(_activator.RepositoryLocator.CatalogueRepository,propertyName, Convert.ToString(false)); + prop.SaveToDatabase(); + } + value = Convert.ToBoolean(prop.Value); + checkboxDictionary.Add(cb, prop); + + cb.Checked = value; + + // register callback + cb.CheckedChanged += CheckboxCheckedChanged; + + // add help + AddTooltip(cb, propertyName); + } + } +} diff --git a/Rdmp.UI/SimpleDialogs/InstanceSettings.resx b/Rdmp.UI/SimpleDialogs/InstanceSettings.resx new file mode 100644 index 0000000000..cf87c79ddd --- /dev/null +++ b/Rdmp.UI/SimpleDialogs/InstanceSettings.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.Designer.cs b/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.Designer.cs new file mode 100644 index 0000000000..4fba305a1e --- /dev/null +++ b/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.Designer.cs @@ -0,0 +1,307 @@ +namespace Rdmp.UI.SimpleDialogs +{ + partial class UpdateCatalogueDataLocationUI + { + /// + /// 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() + { + components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdateCatalogueDataLocationUI)); + helpIcon1 = new SimpleControls.HelpIcon(); + tbCurrentLocation = new System.Windows.Forms.TextBox(); + lbl1 = new System.Windows.Forms.Label(); + serverDatabaseTableSelector1 = new SimpleControls.ServerDatabaseTableSelector(); + label1 = new System.Windows.Forms.Label(); + btnConfirm = new System.Windows.Forms.Button(); + tbMapping = new System.Windows.Forms.TextBox(); + label2 = new System.Windows.Forms.Label(); + tlvDatasets = new BrightIdeasSoftware.TreeListView(); + olvName = new BrightIdeasSoftware.OLVColumn(); + olvState = new BrightIdeasSoftware.OLVColumn(); + panel1 = new System.Windows.Forms.Panel(); + lblFilter = new System.Windows.Forms.Label(); + tbFilter = new System.Windows.Forms.TextBox(); + splitContainer1 = new System.Windows.Forms.SplitContainer(); + label3 = new System.Windows.Forms.Label(); + toolTip1 = new System.Windows.Forms.ToolTip(components); + toolTip2 = new System.Windows.Forms.ToolTip(components); + helpIcon2 = new SimpleControls.HelpIcon(); + ((System.ComponentModel.ISupportInitialize)tlvDatasets).BeginInit(); + panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit(); + splitContainer1.Panel1.SuspendLayout(); + splitContainer1.Panel2.SuspendLayout(); + splitContainer1.SuspendLayout(); + SuspendLayout(); + // + // helpIcon1 + // + helpIcon1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; + helpIcon1.BackColor = System.Drawing.Color.Transparent; + helpIcon1.BackgroundImage = (System.Drawing.Image)resources.GetObject("helpIcon1.BackgroundImage"); + helpIcon1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + helpIcon1.Location = new System.Drawing.Point(1438, 9); + helpIcon1.Margin = new System.Windows.Forms.Padding(5, 3, 5, 3); + helpIcon1.MaximumSize = new System.Drawing.Size(26, 25); + helpIcon1.MinimumSize = new System.Drawing.Size(26, 25); + helpIcon1.Name = "helpIcon1"; + helpIcon1.Size = new System.Drawing.Size(26, 25); + helpIcon1.SuppressClick = false; + helpIcon1.TabIndex = 32; + // + // tbCurrentLocation + // + tbCurrentLocation.Enabled = false; + tbCurrentLocation.Location = new System.Drawing.Point(159, 30); + tbCurrentLocation.Name = "tbCurrentLocation"; + tbCurrentLocation.Size = new System.Drawing.Size(460, 23); + tbCurrentLocation.TabIndex = 33; + // + // lbl1 + // + lbl1.AutoSize = true; + lbl1.Location = new System.Drawing.Point(30, 33); + lbl1.Name = "lbl1"; + lbl1.Size = new System.Drawing.Size(126, 15); + lbl1.TabIndex = 34; + lbl1.Text = "Current Data Location:"; + lbl1.Click += label1_Click; + // + // serverDatabaseTableSelector1 + // + serverDatabaseTableSelector1.AllowTableValuedFunctionSelection = false; + serverDatabaseTableSelector1.AutoSize = true; + serverDatabaseTableSelector1.Database = ""; + serverDatabaseTableSelector1.DatabaseType = FAnsi.DatabaseType.MicrosoftSQLServer; + serverDatabaseTableSelector1.Location = new System.Drawing.Point(21, 98); + serverDatabaseTableSelector1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + serverDatabaseTableSelector1.Name = "serverDatabaseTableSelector1"; + serverDatabaseTableSelector1.Password = ""; + serverDatabaseTableSelector1.Server = ""; + serverDatabaseTableSelector1.Size = new System.Drawing.Size(727, 218); + serverDatabaseTableSelector1.TabIndex = 35; + serverDatabaseTableSelector1.Timeout = ""; + serverDatabaseTableSelector1.Username = ""; + serverDatabaseTableSelector1.Load += serverDatabaseTableSelector1_Load; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(30, 80); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(110, 15); + label1.TabIndex = 36; + label1.Text = "New Data Location:"; + label1.Click += label1_Click_1; + // + // btnConfirm + // + btnConfirm.Location = new System.Drawing.Point(635, 379); + btnConfirm.Name = "btnConfirm"; + btnConfirm.Size = new System.Drawing.Size(75, 23); + btnConfirm.TabIndex = 37; + btnConfirm.Text = "Confirm"; + btnConfirm.UseVisualStyleBackColor = true; + btnConfirm.Click += btnConfirm_Click; + // + // tbMapping + // + tbMapping.Location = new System.Drawing.Point(181, 339); + tbMapping.Name = "tbMapping"; + tbMapping.Size = new System.Drawing.Size(538, 23); + tbMapping.TabIndex = 38; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(3, 342); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(172, 15); + label2.TabIndex = 39; + label2.Text = "(Optional) Map Column Name:"; + label2.Click += label2_Click; + // + // tlvDatasets + // + tlvDatasets.AllColumns.Add(olvName); + tlvDatasets.AllColumns.Add(olvState); + tlvDatasets.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + tlvDatasets.BorderStyle = System.Windows.Forms.BorderStyle.None; + tlvDatasets.CellEditUseWholeCell = false; + tlvDatasets.CheckBoxes = true; + tlvDatasets.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { olvName, olvState }); + tlvDatasets.Location = new System.Drawing.Point(5, 3); + tlvDatasets.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tlvDatasets.Name = "tlvDatasets"; + tlvDatasets.RowHeight = 19; + tlvDatasets.ShowGroups = false; + tlvDatasets.ShowImagesOnSubItems = true; + tlvDatasets.Size = new System.Drawing.Size(333, 955); + tlvDatasets.TabIndex = 28; + tlvDatasets.UseCompatibleStateImageBehavior = false; + tlvDatasets.View = System.Windows.Forms.View.Details; + tlvDatasets.VirtualMode = true; + // + // olvName + // + olvName.AspectName = "ToString"; + olvName.Groupable = false; + olvName.MinimumWidth = 100; + olvName.Text = "Name"; + olvName.Width = 118; + // + // olvState + // + olvState.Groupable = false; + olvState.Text = "Location"; + olvState.Width = 160; + // + // panel1 + // + panel1.Controls.Add(lblFilter); + panel1.Controls.Add(tbFilter); + panel1.Dock = System.Windows.Forms.DockStyle.Bottom; + panel1.Location = new System.Drawing.Point(0, 563); + panel1.Name = "panel1"; + panel1.Size = new System.Drawing.Size(342, 34); + panel1.TabIndex = 32; + // + // lblFilter + // + lblFilter.AutoSize = true; + lblFilter.Location = new System.Drawing.Point(3, 8); + lblFilter.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + lblFilter.Name = "lblFilter"; + lblFilter.Size = new System.Drawing.Size(36, 15); + lblFilter.TabIndex = 30; + lblFilter.Text = "Filter:"; + // + // tbFilter + // + tbFilter.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + tbFilter.Location = new System.Drawing.Point(39, 5); + tbFilter.Margin = new System.Windows.Forms.Padding(0, 3, 4, 3); + tbFilter.Name = "tbFilter"; + tbFilter.Size = new System.Drawing.Size(292, 23); + tbFilter.TabIndex = 29; + tbFilter.TextChanged += tbFilter_TextChanged; + // + // splitContainer1 + // + splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + splitContainer1.Location = new System.Drawing.Point(0, 0); + splitContainer1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + splitContainer1.Panel1.Controls.Add(panel1); + splitContainer1.Panel1.Controls.Add(tlvDatasets); + // + // splitContainer1.Panel2 + // + splitContainer1.Panel2.Controls.Add(helpIcon2); + splitContainer1.Panel2.Controls.Add(label3); + splitContainer1.Panel2.Controls.Add(label2); + splitContainer1.Panel2.Controls.Add(tbMapping); + splitContainer1.Panel2.Controls.Add(btnConfirm); + splitContainer1.Panel2.Controls.Add(label1); + splitContainer1.Panel2.Controls.Add(serverDatabaseTableSelector1); + splitContainer1.Panel2.Controls.Add(lbl1); + splitContainer1.Panel2.Controls.Add(tbCurrentLocation); + splitContainer1.Panel2.Controls.Add(helpIcon1); + splitContainer1.Size = new System.Drawing.Size(1114, 601); + splitContainer1.SplitterDistance = 346; + splitContainer1.SplitterWidth = 5; + splitContainer1.TabIndex = 30; + // + // label3 + // + label3.AutoSize = true; + label3.ForeColor = System.Drawing.Color.Red; + label3.Location = new System.Drawing.Point(12, 379); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(0, 15); + label3.TabIndex = 40; + label3.Click += label3_Click; + // + // helpIcon2 + // + helpIcon2.BackColor = System.Drawing.Color.Transparent; + helpIcon2.BackgroundImage = (System.Drawing.Image)resources.GetObject("helpIcon2.BackgroundImage"); + helpIcon2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + helpIcon2.Location = new System.Drawing.Point(728, 340); + helpIcon2.Margin = new System.Windows.Forms.Padding(0); + helpIcon2.MinimumSize = new System.Drawing.Size(22, 22); + helpIcon2.Name = "helpIcon2"; + helpIcon2.Size = new System.Drawing.Size(22, 22); + helpIcon2.SuppressClick = false; + helpIcon2.TabIndex = 41; + // + // UpdateCatalogueDataLocationUI + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(1114, 601); + Controls.Add(splitContainer1); + Name = "UpdateCatalogueDataLocationUI"; + Text = "Update Catalogue Data Location"; + Load += UpdateCatalogueDataLocationUI_Load; + ((System.ComponentModel.ISupportInitialize)tlvDatasets).EndInit(); + panel1.ResumeLayout(false); + panel1.PerformLayout(); + splitContainer1.Panel1.ResumeLayout(false); + splitContainer1.Panel2.ResumeLayout(false); + splitContainer1.Panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)splitContainer1).EndInit(); + splitContainer1.ResumeLayout(false); + ResumeLayout(false); + } + + #endregion + + private BrightIdeasSoftware.TreeListView tlvDatasets; + private SimpleControls.HelpIcon helpIcon1; + private BrightIdeasSoftware.OLVColumn olvName; + private BrightIdeasSoftware.OLVColumn olvState; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Label lblFilter; + private System.Windows.Forms.TextBox tbFilter; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.Label lbl1; + private System.Windows.Forms.TextBox tbCurrentLocation; + private System.Windows.Forms.Label label1; + private SimpleControls.ServerDatabaseTableSelector serverDatabaseTableSelector1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button btnConfirm; + private System.Windows.Forms.TextBox tbMapping; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.ToolTip toolTip2; + private SimpleControls.HelpIcon helpIcon2; + } +} \ No newline at end of file diff --git a/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.cs b/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.cs new file mode 100644 index 0000000000..147c300ec5 --- /dev/null +++ b/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.cs @@ -0,0 +1,173 @@ +// Copyright (c) The University of Dundee 2024-2024 +// This file is part of the Research Data Management Platform (RDMP). +// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with RDMP. If not, see . + +using System; +using System.Linq; +using System.Windows.Forms; +using BrightIdeasSoftware; +using Rdmp.Core.CommandExecution.AtomicCommands; +using Rdmp.Core.Curation.Data; +using Rdmp.UI.ItemActivation; +using Rdmp.UI.Refreshing; + +namespace Rdmp.UI.SimpleDialogs; + +public partial class UpdateCatalogueDataLocationUI : Form +{ + private readonly Catalogue _catalogue; + private readonly ColumnInfo _columnInfo; + private bool _firstTime = true; + private readonly IActivateItems _activator; + + public UpdateCatalogueDataLocationUI(IActivateItems activator, Catalogue catalogue) + { + InitializeComponent(); + _catalogue = catalogue; + olvState.AspectGetter = State_AspectGetter; + GetCurrentDataLocation(); + RefreshData(); + _activator = activator; + } + + public UpdateCatalogueDataLocationUI(IActivateItems activator, ColumnInfo columnInfo) + { + InitializeComponent(); + _columnInfo = columnInfo; + olvState.AspectGetter = State_AspectGetter; + GetCurrentDataLocation(); + RefreshData(); + _activator = activator; + } + + + private void RefreshData() + { + helpIcon2.SetHelpText("Column Mapping", """ + Optionally add a mapping for your columns. + '$column' is the current column value. + e.g. "$column_old" would turn "myColumn" into "myColumn_old" + """); + if (_catalogue is not null) + { + tlvDatasets.AddObjects(_catalogue.CatalogueItems); + tlvDatasets.EnableObjects(tlvDatasets.Objects); + } + else + { + splitContainer1.SplitterDistance = 0; + + panel1.Width = 0; + panel1.Visible = false; + tlvDatasets.Visible = false; + } + + if (_firstTime) + { + tlvDatasets.CheckAll(); + _firstTime = false; + } + } + + private void GetCurrentDataLocation() + { + if (_columnInfo is not null) + { + tbCurrentLocation.Text = _columnInfo.Name; + } + else + { + var location = _catalogue.CatalogueItems.Where(ci => ci.ColumnInfo is not null).Select(ci => DropColumnIdentifierFromName(ci.ColumnInfo.Name)) + .ToList(); + + tbCurrentLocation.Text = + location.Distinct().Skip(1).Any() ? "Multiple Locations Found" : location.First(); + } + } + + private string DropColumnIdentifierFromName(string name) + { + return string.Join('.', name.Split('.')[..^1]); + } + + private void tbFilter_TextChanged(object sender, EventArgs e) + { + tlvDatasets.ModelFilter = new TextMatchFilter(tlvDatasets, tbFilter.Text); + tlvDatasets.UseFiltering = true; + } + + private object State_AspectGetter(object rowobject) + { + var item = (CatalogueItem)rowobject; + return item.ColumnInfo.Name; + } + + private void label1_Click(object sender, EventArgs e) + { + } + + private void label1_Click_1(object sender, EventArgs e) + { + } + + private void label2_Click(object sender, EventArgs e) + { + } + + private void Run() + { + //should be disabled until things are set + var catalogueItems = _catalogue is not null + ? tlvDatasets.CheckedObjects.Cast().ToArray() + : _columnInfo.CatalogueItems.ToArray(); + var cmd = new ExecuteCommandUpdateCatalogueDataLocation(_activator, catalogueItems, + serverDatabaseTableSelector1.GetDiscoveredTable(), tbMapping.Text); + var check = cmd.Check(); + if (check is null) + { + label3.Text = null; + cmd.Execute(); + if (_catalogue is not null) + _activator.RefreshBus.Publish(_catalogue, new RefreshObjectEventArgs(_catalogue)); + if (_columnInfo is not null) + _activator.RefreshBus.Publish(_columnInfo, new RefreshObjectEventArgs(_columnInfo)); + this.Close(); + } + else + { + label3.Text = check; + } + } + + private void btnConfirm_Click(object sender, EventArgs e) + { + var catalogueItems = _catalogue is not null + ? tlvDatasets.CheckedObjects.Cast() + : _columnInfo.CatalogueItems; + var location = catalogueItems.Select(ci => DropColumnIdentifierFromName(ci.ColumnInfo.Name)).ToList(); + if (location.Distinct().Skip(1).Any()) + { + if (_activator.YesNo("Catalogue uses multiple tables. Are you sure you want to proceed?", + "Update all Tables")) Run(); + Run(); + } + else + { + Run(); + } + } + + private void serverDatabaseTableSelector1_Load(object sender, EventArgs e) + { + } + + private void label3_Click(object sender, EventArgs e) + { + } + + private void UpdateCatalogueDataLocationUI_Load(object sender, EventArgs e) + { + } +} \ No newline at end of file diff --git a/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.resx b/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.resx new file mode 100644 index 0000000000..f651235c27 --- /dev/null +++ b/Rdmp.UI/SimpleDialogs/UpdateCatalogueDataLocationUI.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAABGdBTUEAAK/INwWK6QAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAB3RJTUUH4QURDyAuc4ezXQAAABl0RVh0 + U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAATYSURBVDhPbZPrT5NnGMbfpmm/wn8A/8E2N+eM + W3TObVnSnfzAh3VxRkAEBQGR6TZh8kFFPAxhQhWtbSlSaKHQUuDlbWlLaQstPR8pBctZFNpyKHbGXHsU + lizGK7nyfLp+933dyUP9X6ZImholHomkWcbJF2x9aJszHExxtYEt7pBvkzvo3eD0u5PsPleCpXIkKLUz + sZt8h8xT/1CW6EsOgWbrwy/4w6FtgS6YUjOBLZr2baoHPBuCPleST5ytcSc5xDvBt6UPb1PGye1MJpDi + 6UMpsX8h7fDPbye8s6lX7tgWiF+/iYmZTUePbVUsM6/wOq0rmcS7hF1p/VuULpDK6Pds5OiDm7QzlkqH + FrYwPhkH7VyGanwB/RNLMAWewzWThCkUT8sty3QLM5fzkJnNEGpnd0AEQA36NtlqZ5LH+Nfp8egmAnMb + GHIvQ6qPQaidwUPiB8w0RLoZAp6HI7oG2rUCiX6OrldN8W4rw+x6VYSieh1xVs9EPKvPFReNTW2kfbPr + UNsXcaUrBKFuGp4nCTxNvEBwfh1yyxxqOn3oMD2BPbIKtW0x3aCOiK51+rOuK4IsSj72jN1hWeGPTa07 + PLF1DDiWcVnmxyWpB6H5JDZSL5Ek3tx+iamldVxTeFH92IVu6yyp/QxKy5yjutXFr2nzsKlW4xJHbFgU + WCfjCXNwFc39UZx/5MQFsRPeJ3G4ptdg8D3F5EISz5Pb6B2P4VfROOq6vWDci1CNzSUqH9gFF4UTHKqF + meXep2c1owQ04FgkUz0of2hH6YOdwGNDFNbwCpbWUlhc3YJ8dBrlLWZiC3rGYuixxFB816wpbbZwqca+ + KPeOaoox+J6RqfOoeDSB4vtWnG4excnGEfSSwNNECuH5OCRMGDVSG4oajTjVoIfcFIVidAa5twzMyXoj + l6pTBLm1nQGN1rWMPvsCLkqcb0BFd0049fcIpMOTMHgXcE/jR94tLXJvMMi9yaC4UU9AUciMU/jpypDm + WK2WS5HDcf5sdQs0tvmE1r2E28oAzjSZUUimFzQYUEkqVYvHUXHPhBPXaeJBnKjtx2WJldxrBqKhYOJo + tUaQU9PPoS4K7ezzLTZ+my7qGCawLnMMFaRmQb0eebd1GLDFsLy2BbV1GseuaXCcgApuDkAyFICSbHZD + NuH45oKS/93vvWyqrNnKKm22ZpU1mUUt/aH0qH8ZbcMRnG0ykjpDuNFhh1QbRJ3M9gaWX9eHpl4X6IkY + rkjH09/+1iM6UtGV9VVlN4squmOiCIz9S62OV9w4Qv/V5YbRu4h2QwQ1rVaU3dWh5M4QzjYwuCQ0QTzo + w6A9hiqhBT9eUtH7T7fzDpcr2IfK5Dtf6uerDHX8ujbjhypNTu5NHV3X4UgrzdPoJdVkhjDZLID24SC6 + TBF06MOofmQmGynpPfmtOZ+WtGccONO+A/pP3/+hpo5W9WV+XankHa1Wi6+22RxNKk/insb7ihgCtedV + Q7czUSUcdRwqlYnfzxPzPilqy/y4ULpLeEuHy+XUkQo552BZZ/aBEhn/QHG7YP/px+p9hW303oJW9Z58 + ieC9PBGfgLL35Is5H+SLd5Pv0JEKBfXleQX1xTkF6/NyOftgaQfnsxIZl9Tg7i9q4+4rlHL2nmplf1Qg + YREw9eFJyW7ytSjqX0I4f9SuCRVeAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAABGdBTUEAAK/INwWK6QAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAB3RJTUUH4QURDyAuc4ezXQAAABl0RVh0 + U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAATYSURBVDhPbZPrT5NnGMbfpmm/wn8A/8E2N+eM + W3TObVnSnfzAh3VxRkAEBQGR6TZh8kFFPAxhQhWtbSlSaKHQUuDlbWlLaQstPR8pBctZFNpyKHbGXHsU + lizGK7nyfLp+933dyUP9X6ZImholHomkWcbJF2x9aJszHExxtYEt7pBvkzvo3eD0u5PsPleCpXIkKLUz + sZt8h8xT/1CW6EsOgWbrwy/4w6FtgS6YUjOBLZr2baoHPBuCPleST5ytcSc5xDvBt6UPb1PGye1MJpDi + 6UMpsX8h7fDPbye8s6lX7tgWiF+/iYmZTUePbVUsM6/wOq0rmcS7hF1p/VuULpDK6Pds5OiDm7QzlkqH + FrYwPhkH7VyGanwB/RNLMAWewzWThCkUT8sty3QLM5fzkJnNEGpnd0AEQA36NtlqZ5LH+Nfp8egmAnMb + GHIvQ6qPQaidwUPiB8w0RLoZAp6HI7oG2rUCiX6OrldN8W4rw+x6VYSieh1xVs9EPKvPFReNTW2kfbPr + UNsXcaUrBKFuGp4nCTxNvEBwfh1yyxxqOn3oMD2BPbIKtW0x3aCOiK51+rOuK4IsSj72jN1hWeGPTa07 + PLF1DDiWcVnmxyWpB6H5JDZSL5Ek3tx+iamldVxTeFH92IVu6yyp/QxKy5yjutXFr2nzsKlW4xJHbFgU + WCfjCXNwFc39UZx/5MQFsRPeJ3G4ptdg8D3F5EISz5Pb6B2P4VfROOq6vWDci1CNzSUqH9gFF4UTHKqF + meXep2c1owQ04FgkUz0of2hH6YOdwGNDFNbwCpbWUlhc3YJ8dBrlLWZiC3rGYuixxFB816wpbbZwqca+ + KPeOaoox+J6RqfOoeDSB4vtWnG4excnGEfSSwNNECuH5OCRMGDVSG4oajTjVoIfcFIVidAa5twzMyXoj + l6pTBLm1nQGN1rWMPvsCLkqcb0BFd0049fcIpMOTMHgXcE/jR94tLXJvMMi9yaC4UU9AUciMU/jpypDm + WK2WS5HDcf5sdQs0tvmE1r2E28oAzjSZUUimFzQYUEkqVYvHUXHPhBPXaeJBnKjtx2WJldxrBqKhYOJo + tUaQU9PPoS4K7ezzLTZ+my7qGCawLnMMFaRmQb0eebd1GLDFsLy2BbV1GseuaXCcgApuDkAyFICSbHZD + NuH45oKS/93vvWyqrNnKKm22ZpU1mUUt/aH0qH8ZbcMRnG0ykjpDuNFhh1QbRJ3M9gaWX9eHpl4X6IkY + rkjH09/+1iM6UtGV9VVlN4squmOiCIz9S62OV9w4Qv/V5YbRu4h2QwQ1rVaU3dWh5M4QzjYwuCQ0QTzo + w6A9hiqhBT9eUtH7T7fzDpcr2IfK5Dtf6uerDHX8ujbjhypNTu5NHV3X4UgrzdPoJdVkhjDZLID24SC6 + TBF06MOofmQmGynpPfmtOZ+WtGccONO+A/pP3/+hpo5W9WV+XankHa1Wi6+22RxNKk/insb7ihgCtedV + Q7czUSUcdRwqlYnfzxPzPilqy/y4ULpLeEuHy+XUkQo552BZZ/aBEhn/QHG7YP/px+p9hW303oJW9Z58 + ieC9PBGfgLL35Is5H+SLd5Pv0JEKBfXleQX1xTkF6/NyOftgaQfnsxIZl9Tg7i9q4+4rlHL2nmplf1Qg + YREw9eFJyW7ytSjqX0I4f9SuCRVeAAAAAElFTkSuQmCC + + + + 17, 17 + + + 114, 17 + + \ No newline at end of file diff --git a/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.Designer.cs b/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.Designer.cs index 42bf4eb488..52942b8e22 100644 --- a/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.Designer.cs +++ b/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.Designer.cs @@ -1,5 +1,6 @@ using BrightIdeasSoftware; using Rdmp.UI.LocationsMenu.Ticketing; +using Rdmp.UI.LocationsMenu.Versioning; namespace Rdmp.UI.SubComponents { @@ -34,290 +35,286 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); + components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CohortIdentificationConfigurationUI)); - this.tlvCic = new BrightIdeasSoftware.TreeListView(); - this.olvNameCol = new BrightIdeasSoftware.OLVColumn(); - this.olvExecute = new BrightIdeasSoftware.OLVColumn(); - this.olvOrder = new BrightIdeasSoftware.OLVColumn(); - this.olvCached = new BrightIdeasSoftware.OLVColumn(); - this.olvCount = new BrightIdeasSoftware.OLVColumn(); - this.olvCumulativeTotal = new BrightIdeasSoftware.OLVColumn(); - this.olvWorking = new BrightIdeasSoftware.OLVColumn(); - this.olvTime = new BrightIdeasSoftware.OLVColumn(); - this.olvCatalogue = new BrightIdeasSoftware.OLVColumn(); - this.timer1 = new System.Windows.Forms.Timer(this.components); - this.ticket = new Rdmp.UI.LocationsMenu.Ticketing.TicketingControlUI(); - this.btnAbortLoad = new System.Windows.Forms.Button(); - this.btnExecute = new System.Windows.Forms.Button(); - this.splitContainer2 = new System.Windows.Forms.SplitContainer(); - this.gbCicInfo = new System.Windows.Forms.GroupBox(); - this.tbDescription = new System.Windows.Forms.TextBox(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.panel1 = new System.Windows.Forms.Panel(); - this.lblExecuteAllPhase = new System.Windows.Forms.Label(); - this.btnClearCache = new System.Windows.Forms.Button(); - ((System.ComponentModel.ISupportInitialize)(this.tlvCic)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); - this.splitContainer2.Panel1.SuspendLayout(); - this.splitContainer2.Panel2.SuspendLayout(); - this.splitContainer2.SuspendLayout(); - this.gbCicInfo.SuspendLayout(); - this.groupBox1.SuspendLayout(); - this.panel1.SuspendLayout(); - this.SuspendLayout(); + tlvCic = new TreeListView(); + olvNameCol = new OLVColumn(); + olvExecute = new OLVColumn(); + olvOrder = new OLVColumn(); + olvCached = new OLVColumn(); + olvCount = new OLVColumn(); + olvCumulativeTotal = new OLVColumn(); + olvWorking = new OLVColumn(); + olvTime = new OLVColumn(); + olvCatalogue = new OLVColumn(); + timer1 = new System.Windows.Forms.Timer(components); + ticket = new TicketingControlUI(); + version = new VersioningControlUI(); + btnAbortLoad = new System.Windows.Forms.Button(); + btnExecute = new System.Windows.Forms.Button(); + splitContainer2 = new System.Windows.Forms.SplitContainer(); + gbCicInfo = new System.Windows.Forms.GroupBox(); + tbDescription = new System.Windows.Forms.TextBox(); + groupBox1 = new System.Windows.Forms.GroupBox(); + panel1 = new System.Windows.Forms.Panel(); + btnClearCache = new System.Windows.Forms.Button(); + lblExecuteAllPhase = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)tlvCic).BeginInit(); + ((System.ComponentModel.ISupportInitialize)splitContainer2).BeginInit(); + splitContainer2.Panel1.SuspendLayout(); + splitContainer2.Panel2.SuspendLayout(); + splitContainer2.SuspendLayout(); + gbCicInfo.SuspendLayout(); + groupBox1.SuspendLayout(); + panel1.SuspendLayout(); + SuspendLayout(); // // tlvCic // - this.tlvCic.AllColumns.Add(this.olvNameCol); - this.tlvCic.AllColumns.Add(this.olvExecute); - this.tlvCic.AllColumns.Add(this.olvOrder); - this.tlvCic.AllColumns.Add(this.olvCached); - this.tlvCic.AllColumns.Add(this.olvCount); - this.tlvCic.AllColumns.Add(this.olvCumulativeTotal); - this.tlvCic.AllColumns.Add(this.olvWorking); - this.tlvCic.AllColumns.Add(this.olvTime); - this.tlvCic.AllColumns.Add(this.olvCatalogue); - this.tlvCic.CellEditUseWholeCell = false; - this.tlvCic.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.olvNameCol, - this.olvExecute, - this.olvCached, - this.olvCount, - this.olvCumulativeTotal, - this.olvWorking, - this.olvTime, - this.olvCatalogue}); - this.tlvCic.Cursor = System.Windows.Forms.Cursors.Default; - this.tlvCic.Dock = System.Windows.Forms.DockStyle.Fill; - this.tlvCic.HideSelection = false; - this.tlvCic.Location = new System.Drawing.Point(0, 0); - this.tlvCic.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.tlvCic.Name = "tlvCic"; - this.tlvCic.ShowGroups = false; - this.tlvCic.Size = new System.Drawing.Size(1450, 714); - this.tlvCic.TabIndex = 60; - this.tlvCic.UseCompatibleStateImageBehavior = false; - this.tlvCic.View = System.Windows.Forms.View.Details; - this.tlvCic.VirtualMode = true; + tlvCic.AllColumns.Add(olvNameCol); + tlvCic.AllColumns.Add(olvExecute); + tlvCic.AllColumns.Add(olvOrder); + tlvCic.AllColumns.Add(olvCached); + tlvCic.AllColumns.Add(olvCount); + tlvCic.AllColumns.Add(olvCumulativeTotal); + tlvCic.AllColumns.Add(olvWorking); + tlvCic.AllColumns.Add(olvTime); + tlvCic.AllColumns.Add(olvCatalogue); + tlvCic.CellEditUseWholeCell = false; + tlvCic.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { olvNameCol, olvExecute, olvCached, olvCount, olvCumulativeTotal, olvWorking, olvTime, olvCatalogue }); + tlvCic.Dock = System.Windows.Forms.DockStyle.Fill; + tlvCic.Location = new System.Drawing.Point(0, 0); + tlvCic.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tlvCic.Name = "tlvCic"; + tlvCic.ShowGroups = false; + tlvCic.Size = new System.Drawing.Size(1450, 691); + tlvCic.TabIndex = 60; + tlvCic.UseCompatibleStateImageBehavior = false; + tlvCic.View = System.Windows.Forms.View.Details; + tlvCic.VirtualMode = true; // // olvNameCol // - this.olvNameCol.AspectName = "ToString"; - this.olvNameCol.MinimumWidth = 100; - this.olvNameCol.Text = "Name"; - this.olvNameCol.Width = 100; + olvNameCol.AspectName = "ToString"; + olvNameCol.MinimumWidth = 100; + olvNameCol.Text = "Name"; + olvNameCol.Width = 100; // // olvExecute // - this.olvExecute.Sortable = false; - this.olvExecute.Text = "Execute"; - this.olvExecute.IsEditable = false; + olvExecute.IsEditable = false; + olvExecute.Sortable = false; + olvExecute.Text = "Execute"; // // olvOrder // - this.olvOrder.DisplayIndex = 2; - this.olvOrder.IsVisible = false; - this.olvOrder.Sortable = false; - this.olvOrder.Text = "Order"; + olvOrder.DisplayIndex = 2; + olvOrder.IsVisible = false; + olvOrder.Sortable = false; + olvOrder.Text = "Order"; // // olvCached // - this.olvCached.IsEditable = false; - this.olvCached.Sortable = false; - this.olvCached.Text = "Cached"; + olvCached.IsEditable = false; + olvCached.Sortable = false; + olvCached.Text = "Cached"; // // olvCount // - this.olvCount.IsEditable = false; - this.olvCount.Sortable = false; - this.olvCount.Text = "Count"; + olvCount.IsEditable = false; + olvCount.Sortable = false; + olvCount.Text = "Count"; // // olvCumulativeTotal // - this.olvCumulativeTotal.IsEditable = false; - this.olvCumulativeTotal.Sortable = false; - this.olvCumulativeTotal.Text = "Cumulative Total"; + olvCumulativeTotal.IsEditable = false; + olvCumulativeTotal.Sortable = false; + olvCumulativeTotal.Text = "Cumulative Total"; // // olvWorking // - this.olvWorking.IsEditable = false; - this.olvWorking.Sortable = false; - this.olvWorking.Text = "Working"; + olvWorking.IsEditable = false; + olvWorking.Sortable = false; + olvWorking.Text = "Working"; // // olvTime // - this.olvTime.IsEditable = false; - this.olvTime.Sortable = false; - this.olvTime.Text = "Time"; + olvTime.IsEditable = false; + olvTime.Sortable = false; + olvTime.Text = "Time"; // // olvCatalogue // - this.olvCatalogue.IsEditable = false; - this.olvCatalogue.Sortable = false; - this.olvCatalogue.Text = "Catalogue"; - this.olvCatalogue.Width = 150; + olvCatalogue.IsEditable = false; + olvCatalogue.Sortable = false; + olvCatalogue.Text = "Catalogue"; + olvCatalogue.Width = 150; // // timer1 // - this.timer1.Enabled = true; - this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + timer1.Enabled = true; + timer1.Tick += timer1_Tick; // // ticket // - this.ticket.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Right))); - this.ticket.Location = new System.Drawing.Point(1097, 0); - this.ticket.Margin = new System.Windows.Forms.Padding(0); - this.ticket.Name = "ticket"; - this.ticket.Size = new System.Drawing.Size(348, 79); - this.ticket.TabIndex = 55; - this.ticket.TicketText = ""; - this.ticket.TicketTextChanged += new System.EventHandler(this.ticket_TicketTextChanged); + ticket.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + ticket.Location = new System.Drawing.Point(1097, 0); + ticket.Margin = new System.Windows.Forms.Padding(0); + ticket.Name = "ticket"; + ticket.Size = new System.Drawing.Size(348, 81); + ticket.TabIndex = 55; + ticket.TicketText = ""; + ticket.TicketTextChanged += ticket_TicketTextChanged; + // + // version + // + version.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + version.Location = new System.Drawing.Point(1097, 50); + version.Margin = new System.Windows.Forms.Padding(0); + version.Name = "version"; + version.Size = new System.Drawing.Size(348, 81); + version.TabIndex = 55; // // btnAbortLoad // - this.btnAbortLoad.Image = ((System.Drawing.Image)(resources.GetObject("btnAbortLoad.Image"))); - this.btnAbortLoad.Location = new System.Drawing.Point(178, 0); - this.btnAbortLoad.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.btnAbortLoad.Name = "btnAbortLoad"; - this.btnAbortLoad.Size = new System.Drawing.Size(34, 36); - this.btnAbortLoad.TabIndex = 65; - this.btnAbortLoad.UseVisualStyleBackColor = true; - this.btnAbortLoad.Click += new System.EventHandler(this.btnAbortLoad_Click); + btnAbortLoad.Image = (System.Drawing.Image)resources.GetObject("btnAbortLoad.Image"); + btnAbortLoad.Location = new System.Drawing.Point(178, 0); + btnAbortLoad.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnAbortLoad.Name = "btnAbortLoad"; + btnAbortLoad.Size = new System.Drawing.Size(34, 36); + btnAbortLoad.TabIndex = 65; + btnAbortLoad.UseVisualStyleBackColor = true; + btnAbortLoad.Click += btnAbortLoad_Click; // // btnExecute // - this.btnExecute.Image = ((System.Drawing.Image)(resources.GetObject("btnExecute.Image"))); - this.btnExecute.Location = new System.Drawing.Point(0, 0); - this.btnExecute.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.btnExecute.Name = "btnExecute"; - this.btnExecute.Size = new System.Drawing.Size(178, 36); - this.btnExecute.TabIndex = 66; - this.btnExecute.UseVisualStyleBackColor = true; - this.btnExecute.Click += new System.EventHandler(this.btnExecute_Click); + btnExecute.Image = (System.Drawing.Image)resources.GetObject("btnExecute.Image"); + btnExecute.Location = new System.Drawing.Point(0, 0); + btnExecute.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnExecute.Name = "btnExecute"; + btnExecute.Size = new System.Drawing.Size(178, 36); + btnExecute.TabIndex = 66; + btnExecute.UseVisualStyleBackColor = true; + btnExecute.Click += btnExecute_Click; // // splitContainer2 // - this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; - this.splitContainer2.Location = new System.Drawing.Point(0, 0); - this.splitContainer2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.splitContainer2.Name = "splitContainer2"; - this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal; + splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; + splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; + splitContainer2.Location = new System.Drawing.Point(0, 0); + splitContainer2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + splitContainer2.Name = "splitContainer2"; + splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal; // // splitContainer2.Panel1 // - this.splitContainer2.Panel1.Controls.Add(this.gbCicInfo); - this.splitContainer2.Panel1.Controls.Add(this.groupBox1); - this.splitContainer2.Panel1.Controls.Add(this.ticket); - this.splitContainer2.Panel1MinSize = 82; + splitContainer2.Panel1.Controls.Add(version); + splitContainer2.Panel1.Controls.Add(gbCicInfo); + splitContainer2.Panel1.Controls.Add(groupBox1); + splitContainer2.Panel1.Controls.Add(ticket); + splitContainer2.Panel1MinSize = 82; // // splitContainer2.Panel2 // - this.splitContainer2.Panel2.Controls.Add(this.tlvCic); - this.splitContainer2.Size = new System.Drawing.Size(1450, 801); - this.splitContainer2.SplitterDistance = 82; - this.splitContainer2.SplitterWidth = 5; - this.splitContainer2.TabIndex = 67; + splitContainer2.Panel2.Controls.Add(tlvCic); + splitContainer2.Size = new System.Drawing.Size(1450, 801); + splitContainer2.SplitterDistance = 105; + splitContainer2.SplitterWidth = 5; + splitContainer2.TabIndex = 67; // // gbCicInfo // - this.gbCicInfo.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.gbCicInfo.Controls.Add(this.tbDescription); - this.gbCicInfo.Location = new System.Drawing.Point(261, 3); - this.gbCicInfo.Name = "gbCicInfo"; - this.gbCicInfo.Size = new System.Drawing.Size(833, 76); - this.gbCicInfo.TabIndex = 71; - this.gbCicInfo.TabStop = false; - this.gbCicInfo.Text = "Name:"; + gbCicInfo.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + gbCicInfo.Controls.Add(tbDescription); + gbCicInfo.Location = new System.Drawing.Point(261, 3); + gbCicInfo.Name = "gbCicInfo"; + gbCicInfo.Size = new System.Drawing.Size(833, 99); + gbCicInfo.TabIndex = 71; + gbCicInfo.TabStop = false; + gbCicInfo.Text = "Name:"; // // tbDescription // - this.tbDescription.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.tbDescription.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.tbDescription.Location = new System.Drawing.Point(6, 16); - this.tbDescription.Margin = new System.Windows.Forms.Padding(3, 3, 1, 0); - this.tbDescription.Multiline = true; - this.tbDescription.Name = "tbDescription"; - this.tbDescription.ReadOnly = true; - this.tbDescription.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.tbDescription.Size = new System.Drawing.Size(824, 57); - this.tbDescription.TabIndex = 54; + tbDescription.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + tbDescription.BorderStyle = System.Windows.Forms.BorderStyle.None; + tbDescription.Location = new System.Drawing.Point(6, 16); + tbDescription.Margin = new System.Windows.Forms.Padding(3, 3, 1, 0); + tbDescription.Multiline = true; + tbDescription.Name = "tbDescription"; + tbDescription.ReadOnly = true; + tbDescription.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + tbDescription.Size = new System.Drawing.Size(824, 80); + tbDescription.TabIndex = 54; // // groupBox1 // - this.groupBox1.Controls.Add(this.panel1); - this.groupBox1.Controls.Add(this.lblExecuteAllPhase); - this.groupBox1.Location = new System.Drawing.Point(4, 3); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.groupBox1.Size = new System.Drawing.Size(255, 76); - this.groupBox1.TabIndex = 67; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Controls"; + groupBox1.Controls.Add(panel1); + groupBox1.Controls.Add(lblExecuteAllPhase); + groupBox1.Location = new System.Drawing.Point(4, 3); + groupBox1.Name = "groupBox1"; + groupBox1.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); + groupBox1.Size = new System.Drawing.Size(255, 76); + groupBox1.TabIndex = 67; + groupBox1.TabStop = false; + groupBox1.Text = "Controls"; // // panel1 // - this.panel1.Controls.Add(this.btnClearCache); - this.panel1.Controls.Add(this.btnExecute); - this.panel1.Controls.Add(this.btnAbortLoad); - this.panel1.Dock = System.Windows.Forms.DockStyle.Top; - this.panel1.Location = new System.Drawing.Point(4, 19); - this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(247, 36); - this.panel1.TabIndex = 70; + panel1.Controls.Add(btnClearCache); + panel1.Controls.Add(btnExecute); + panel1.Controls.Add(btnAbortLoad); + panel1.Dock = System.Windows.Forms.DockStyle.Top; + panel1.Location = new System.Drawing.Point(4, 19); + panel1.Name = "panel1"; + panel1.Size = new System.Drawing.Size(247, 36); + panel1.TabIndex = 70; // - // lblExecuteAllPhase + // btnClearCache // - this.lblExecuteAllPhase.Enabled = false; - this.lblExecuteAllPhase.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.lblExecuteAllPhase.Location = new System.Drawing.Point(4, 58); - this.lblExecuteAllPhase.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.lblExecuteAllPhase.Name = "lblExecuteAllPhase"; - this.lblExecuteAllPhase.Size = new System.Drawing.Size(178, 13); - this.lblExecuteAllPhase.TabIndex = 70; - this.lblExecuteAllPhase.Text = "Execution status..."; - this.lblExecuteAllPhase.TextAlign = System.Drawing.ContentAlignment.TopCenter; + btnClearCache.Image = (System.Drawing.Image)resources.GetObject("btnClearCache.Image"); + btnClearCache.Location = new System.Drawing.Point(212, 0); + btnClearCache.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + btnClearCache.Name = "btnClearCache"; + btnClearCache.Size = new System.Drawing.Size(34, 36); + btnClearCache.TabIndex = 67; + btnClearCache.UseVisualStyleBackColor = true; + btnClearCache.Click += btnClearCache_Click; // - // btnClearCache + // lblExecuteAllPhase // - this.btnClearCache.Image = ((System.Drawing.Image)(resources.GetObject("btnClearCache.Image"))); - this.btnClearCache.Location = new System.Drawing.Point(212, 0); - this.btnClearCache.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.btnClearCache.Name = "btnClearCache"; - this.btnClearCache.Size = new System.Drawing.Size(34, 36); - this.btnClearCache.TabIndex = 67; - this.btnClearCache.UseVisualStyleBackColor = true; - this.btnClearCache.Click += new System.EventHandler(this.btnClearCache_Click); + lblExecuteAllPhase.Enabled = false; + lblExecuteAllPhase.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + lblExecuteAllPhase.Location = new System.Drawing.Point(4, 58); + lblExecuteAllPhase.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblExecuteAllPhase.Name = "lblExecuteAllPhase"; + lblExecuteAllPhase.Size = new System.Drawing.Size(178, 13); + lblExecuteAllPhase.TabIndex = 70; + lblExecuteAllPhase.Text = "Execution status..."; + lblExecuteAllPhase.TextAlign = System.Drawing.ContentAlignment.TopCenter; // // CohortIdentificationConfigurationUI // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.splitContainer2); - this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.Name = "CohortIdentificationConfigurationUI"; - this.Size = new System.Drawing.Size(1450, 801); - ((System.ComponentModel.ISupportInitialize)(this.tlvCic)).EndInit(); - this.splitContainer2.Panel1.ResumeLayout(false); - this.splitContainer2.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); - this.splitContainer2.ResumeLayout(false); - this.gbCicInfo.ResumeLayout(false); - this.gbCicInfo.PerformLayout(); - this.groupBox1.ResumeLayout(false); - this.panel1.ResumeLayout(false); - this.ResumeLayout(false); - + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + Controls.Add(splitContainer2); + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + Name = "CohortIdentificationConfigurationUI"; + Size = new System.Drawing.Size(1450, 801); + ((System.ComponentModel.ISupportInitialize)tlvCic).EndInit(); + splitContainer2.Panel1.ResumeLayout(false); + splitContainer2.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)splitContainer2).EndInit(); + splitContainer2.ResumeLayout(false); + gbCicInfo.ResumeLayout(false); + gbCicInfo.PerformLayout(); + groupBox1.ResumeLayout(false); + panel1.ResumeLayout(false); + ResumeLayout(false); } #endregion private TicketingControlUI ticket; + private VersioningControlUI version; private TreeListView tlvCic; private OLVColumn olvNameCol; private OLVColumn olvExecute; diff --git a/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.cs b/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.cs index 975fae2fd1..3783bd4c02 100644 --- a/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.cs +++ b/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.cs @@ -1,4 +1,4 @@ -// Copyright (c) The University of Dundee 2018-2019 +// Copyright (c) The University of Dundee 2018-2024 // This file is part of the Research Data Management Platform (RDMP). // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -73,7 +73,7 @@ public partial class CohortIdentificationConfigurationUI : CohortIdentificationC private ExecuteCommandClearQueryCache _clearCacheCommand; - private CohortIdentificationConfigurationUICommon Common = new (); + private CohortIdentificationConfigurationUICommon Common = new(); public CohortIdentificationConfigurationUI() { @@ -134,6 +134,7 @@ public CohortIdentificationConfigurationUI() tt.SetToolTip(btnExecute, "Starts running and caches all cohort sets and containers"); tt.SetToolTip(btnAbortLoad, "Cancels execution of any running cohort sets"); + } @@ -142,7 +143,6 @@ public void RefreshBus_RefreshObject(object sender, RefreshObjectEventArgs e) Common.Activator = Activator; var descendancy = Activator.CoreChildProvider.GetDescendancyListIfAnyFor(e.Object); - //if publish event was for a child of the cic (_cic is in the objects descendancy i.e. it sits below our cic) if (descendancy != null && descendancy.Parents.Contains(Common.Configuration)) { @@ -168,6 +168,7 @@ private void refreshColumnValues(object sender, EventArgs e) public override void SetDatabaseObject(IActivateItems activator, CohortIdentificationConfiguration databaseObject) { base.SetDatabaseObject(activator, databaseObject); + version.Setup(databaseObject, activator); Common.Configuration = databaseObject; Common.Compiler.CohortIdentificationConfiguration = databaseObject; @@ -183,19 +184,21 @@ public override void SetDatabaseObject(IActivateItems activator, CohortIdentific _commonFunctionality = new RDMPCollectionCommonFunctionality(); _commonFunctionality.SetUp(RDMPCollection.Cohort, tlvCic, activator, olvNameCol, olvNameCol, - new RDMPCollectionCommonFunctionalitySettings - { - SuppressActivate = true, - AddFavouriteColumn = false, - AddCheckColumn = false, - AllowSorting = - true //important, we need sorting on so that we can override sort order with our OrderableComparer - }); + new RDMPCollectionCommonFunctionalitySettings + { + SuppressActivate = true, + AddFavouriteColumn = false, + AddCheckColumn = false, + AllowSorting = + true //important, we need sorting on so that we can override sort order with our OrderableComparer + }); _commonFunctionality.MenuBuilt += MenuBuilt; + tlvCic.Objects = null; tlvCic.AddObject(databaseObject); if (UserSettings.ExpandAllInCohortBuilder) tlvCic.ExpandAll(); + tlvCic.SelectedIndex = 0; } CommonFunctionality.AddToMenu(cbIncludeCumulative); @@ -406,6 +409,11 @@ private static void ViewCrashMessage(ICompileable compileable) { ExceptionViewer.Show(compileable.CrashMessage); } + + private void cbKnownVersions_SelectedIndexChanged(object sender, EventArgs e) + { + + } } [TypeDescriptionProvider( diff --git a/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.resx b/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.resx index f8c7b52d45..71846e1320 100644 --- a/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.resx +++ b/Rdmp.UI/SubComponents/CohortIdentificationConfigurationUI.resx @@ -1,4 +1,64 @@ - + + + @@ -76,7 +136,7 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - vQAADr0BR/uQrQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xMzQDW3oAAAEaSURBVDhPnY/t + vAAADrwBlbxySQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xMzQDW3oAAAEaSURBVDhPnY/t KoVBFEbf/+IupMQ1iIiU3yIppaSkEAk5KR8pKUmJkgiRlJKUFIlEkgtQiBK5huU5pzPTNGO8OrvWNLP3 flZNkmRKyKJKCsEKChUFAoO/GMP9Qq5cSb7/a9AQCEy5kvz8b8EX33zwybvOV53PvPHES6rICu545IYH rrnnklvO9TrjilO9TriIiqxgn2N2OWKbQzY5YF2dNfZYZYdltlhig0V1XUkOc5ljhVmtTmt1UqsTLDDO diff --git a/Rdmp.UI/TestsAndSetup/ServicePropogation/RDMPSingleDatabaseObjectControl.cs b/Rdmp.UI/TestsAndSetup/ServicePropogation/RDMPSingleDatabaseObjectControl.cs index ebb4de7600..8f16a3a378 100644 --- a/Rdmp.UI/TestsAndSetup/ServicePropogation/RDMPSingleDatabaseObjectControl.cs +++ b/Rdmp.UI/TestsAndSetup/ServicePropogation/RDMPSingleDatabaseObjectControl.cs @@ -13,9 +13,7 @@ using Rdmp.Core.CommandExecution.AtomicCommands; using Rdmp.Core.Curation.Data; using Rdmp.Core.Curation.Data.Aggregation; -using Rdmp.Core.Curation.Data.Cohort; using Rdmp.Core.MapsDirectlyToDatabaseTable; -using Rdmp.Core.Repositories; using Rdmp.Core.ReusableLibraryCode.Settings; using Rdmp.UI.ExtractionUIs.FilterUIs; using Rdmp.UI.ItemActivation; diff --git a/Rdmp.UI/Wizard/CreateNewDataExtractionProjectUI.cs b/Rdmp.UI/Wizard/CreateNewDataExtractionProjectUI.cs index 6bc385d83c..2d1f047c74 100644 --- a/Rdmp.UI/Wizard/CreateNewDataExtractionProjectUI.cs +++ b/Rdmp.UI/Wizard/CreateNewDataExtractionProjectUI.cs @@ -21,6 +21,7 @@ using Rdmp.Core.DataLoad.Modules.DataFlowSources; using Rdmp.Core.Icons.IconProvision; using Rdmp.Core.ReusableLibraryCode.Progress; +using Rdmp.Core.Setting; using Rdmp.UI.CohortUI.CohortSourceManagement; using Rdmp.UI.ItemActivation; using Rdmp.UI.SingleControlForms; @@ -52,15 +53,26 @@ public partial class CreateNewDataExtractionProjectUI : RDMPForm public ExtractionConfiguration ExtractionConfigurationCreatedIfAny { get; private set; } public Project ProjectCreatedIfAny { get; private set; } - public CreateNewDataExtractionProjectUI(IActivateItems activator) : base(activator) + private void GetNextProjectNumber(IActivateItems activator) { - InitializeComponent(); - _existingProjects = activator.RepositoryLocator.DataExportRepository.GetAllObjects(); - var highestNumber = _existingProjects.Max(p => p.ProjectNumber); + var AutoSuggestProjectNumbers = false; + var AutoSuggestProjectNumbersSetting = activator.RepositoryLocator.CatalogueRepository.GetAllObjects().Where(s => s.Key == "AutoSuggestProjectNumbers").FirstOrDefault(); + if (AutoSuggestProjectNumbersSetting is not null) AutoSuggestProjectNumbers = Convert.ToBoolean(AutoSuggestProjectNumbersSetting.Value); + if (AutoSuggestProjectNumbers) + { + _existingProjects = activator.RepositoryLocator.DataExportRepository.GetAllObjects(); + var highestNumber = _existingProjects.Max(p => p.ProjectNumber); + tbProjectNumber.Text = highestNumber == null ? "1" : (highestNumber.Value + 1).ToString(); + } - tbProjectNumber.Text = highestNumber == null ? "1" : (highestNumber.Value + 1).ToString(); + } + + public CreateNewDataExtractionProjectUI(IActivateItems activator) : base(activator) + { + InitializeComponent(); + GetNextProjectNumber(activator); pbCohort.Image = activator.CoreIconProvider.GetImage(RDMPConcept.CohortIdentificationConfiguration) .ImageToBitmap(); pbCohortFile.Image = activator.CoreIconProvider.GetImage(RDMPConcept.File).ImageToBitmap(); diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs index cac26791a7..aa2132ac5d 100644 --- a/SharedAssemblyInfo.cs +++ b/SharedAssemblyInfo.cs @@ -10,6 +10,6 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("8.1.7")] -[assembly: AssemblyFileVersion("8.1.7")] -[assembly: AssemblyInformationalVersion("8.1.7")] +[assembly: AssemblyVersion("8.2.0")] +[assembly: AssemblyFileVersion("8.2.0")] +[assembly: AssemblyInformationalVersion("8.2.0")] diff --git a/Tests.Common/Scenarios/TestsRequiringADle.cs b/Tests.Common/Scenarios/TestsRequiringADle.cs index a11bdc1f74..42522915bd 100644 --- a/Tests.Common/Scenarios/TestsRequiringADle.cs +++ b/Tests.Common/Scenarios/TestsRequiringADle.cs @@ -63,10 +63,12 @@ protected override void SetUp() TestCatalogue = Import(LiveTable); RowsBefore = 5000; - TestLoadMetadata = new LoadMetadata(CatalogueRepository, "Loading Test Catalogue") - { - LocationOfFlatFiles = LoadDirectory.RootPath.FullName - }; + TestLoadMetadata = new LoadMetadata(CatalogueRepository, "Loading Test Catalogue"); + TestLoadMetadata.LocationOfForLoadingDirectory = Path.Combine(LoadDirectory.RootPath.FullName, TestLoadMetadata.DefaultForLoadingPath); + TestLoadMetadata.LocationOfForArchivingDirectory = Path.Combine(LoadDirectory.RootPath.FullName, TestLoadMetadata.DefaultForArchivingPath); + TestLoadMetadata.LocationOfExecutablesDirectory = Path.Combine(LoadDirectory.RootPath.FullName, TestLoadMetadata.DefaultExecutablesPath); + TestLoadMetadata.LocationOfCacheDirectory = Path.Combine(LoadDirectory.RootPath.FullName, TestLoadMetadata.DefaultCachePath); + TestLoadMetadata.SaveToDatabase(); @@ -77,7 +79,7 @@ protected override void SetUp() //Get DleRunner to run pre load checks (includes trigger creation etc) var runner = new DleRunner(new DleOptions - { LoadMetadata = TestLoadMetadata.ID.ToString(), Command = CommandLineActivity.check }); + { LoadMetadata = TestLoadMetadata.ID.ToString(), Command = CommandLineActivity.check }); runner.Run(RepositoryLocator, ThrowImmediatelyDataLoadEventListener.Quiet, new AcceptAllCheckNotifier(), new GracefulCancellationToken()); } @@ -171,13 +173,13 @@ public void RunDLE(LoadMetadata lmd, int timeoutInMilliseconds, bool checks) { //Get DleRunner to run pre load checks (includes trigger creation etc) var checker = new DleRunner(new DleOptions - { LoadMetadata = lmd.ID.ToString(), Command = CommandLineActivity.check }); + { LoadMetadata = lmd.ID.ToString(), Command = CommandLineActivity.check }); checker.Run(RepositoryLocator, ThrowImmediatelyDataLoadEventListener.Quiet, new AcceptAllCheckNotifier(), new GracefulCancellationToken(timeout, timeout)); } var runner = new DleRunner(new DleOptions - { LoadMetadata = lmd.ID.ToString(), Command = CommandLineActivity.run }); + { LoadMetadata = lmd.ID.ToString(), Command = CommandLineActivity.run }); runner.Run(RepositoryLocator, ThrowImmediatelyDataLoadEventListener.Quiet, ThrowImmediatelyCheckNotifier.Quiet, new GracefulCancellationToken(timeout, timeout)); } diff --git a/Tests.Common/UnitTests.cs b/Tests.Common/UnitTests.cs index 88dcc109a4..447976c0e7 100644 --- a/Tests.Common/UnitTests.cs +++ b/Tests.Common/UnitTests.cs @@ -40,6 +40,7 @@ using Rdmp.Core.MapsDirectlyToDatabaseTable; using Rdmp.Core.Repositories; using Rdmp.Core.ReusableLibraryCode.Checks; +using Rdmp.Core.Setting; namespace Tests.Common; @@ -579,6 +580,10 @@ public static T WhenIHaveA(MemoryDataExportRepository repository) where T : D return (T)(object)new LoadMetadataCatalogueLinkage(repository, lmd,cata); } + if (typeof(T) == typeof(Setting)) { + return (T)(object)new Setting(repository.CatalogueRepository, "", ""); + } + throw new TestCaseNotWrittenYetException(typeof(T)); }