diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..350da421cc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**RDMP Version** +Displayed in the task bar of the software e.g. v4.1.5 + +**Error with Stack Trace** +``` +If applicable, paste the entire stack trace here, leave the triple quotes (```) +``` + +**Database Engine** +Sql Server, Oracle, MySql or Postgres. + +**Additional context** +Add any other context about the problem here. diff --git a/.travis.yml b/.travis.yml index 4771951394..10c6fb78c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,35 @@ -language: csharp -mono: none -dotnet: 2.2.100 +language: generic +dist: bionic +os: linux addons: postgresql: "10" + apt: + packages: + sources: + - sourceline: 'deb [arch=amd64] https://packages.microsoft.com/ubuntu/18.04/prod bionic main' + key_url: 'https://packages.microsoft.com/keys/microsoft.asc' + - sourceline: 'deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/ubuntu/18.04/mssql-server-2019 bionic main' services: - postgresql - mysql +env: + global: + - MSSQL_SA_PASSWORD="YourStrong!Passw0rd" + - ACCEPT_EULA=Y + - MSSQL_PID='developer' + +cache: + directories: + - $HOME/.local/share/NuGet/ + - $HOME/.nuget + before_script: -- sudo docker run --name=mssql-server-linux-latest -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=YourStrong!Passw0rd' -p 1433:1433 -d microsoft/mssql-server-linux:2017-latest -- sudo apt install libc6-dev -- sudo apt install libgdiplus +- sudo apt-get install -y --no-install-recommends libc6-dev libgdiplus dotnet-sdk-2.2 dotnet-sdk-3.1 +- sudo apt-get install -y --no-install-recommends mssql-tools mssql-server +- sudo /opt/mssql/bin/mssql-conf -n setup accept-eula script: - dotnet publish "./Tools/rdmp" -r linux-x64 diff --git a/Application/ResearchDataManagementPlatform/WindowManagement/WindowFactory.cs b/Application/ResearchDataManagementPlatform/WindowManagement/WindowFactory.cs index dce76fa658..f5849b6500 100644 --- a/Application/ResearchDataManagementPlatform/WindowManagement/WindowFactory.cs +++ b/Application/ResearchDataManagementPlatform/WindowManagement/WindowFactory.cs @@ -18,6 +18,7 @@ using Rdmp.UI.Icons; using Rdmp.UI.ItemActivation; using Rdmp.UI.Refreshing; +using Rdmp.UI.SimpleControls; using Rdmp.UI.SingleControlForms; using Rdmp.UI.TestsAndSetup.ServicePropogation; using ResearchDataManagementPlatform.WindowManagement.ContentWindowTracking.Persistence; @@ -123,6 +124,9 @@ private void AddControlToDockContent(IActivateItems activator, Control control,D if (control is IConsultableBeforeClosing consult) content.FormClosing += consult.ConsultAboutClosing; + if(control is ISaveableUI saveable) + content.FormClosing += (s,e)=>saveable.GetObjectSaverButton()?.CheckForUnsavedChangesAnOfferToSave(); + content.KeyPreview = true; if (content is RDMPSingleControlTab tab) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc7d9558ab..1d90bc1052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ... +## [4.1.6] - 2020-08-04 + +### Added + +- Added 'Save Changes' prompt when closing tabs +- Added Import command for bringing in one or more [CohortIdentificationConfiguration] into an existing container (like Merge / UnMerge but for existing configurations) +- Added checks for LoadProgress dates being in sensible ranges during DLE + +### Fixed + +- Fixed [bug when parsing lists of ints in CLI](https://github.com/HicServices/RDMP/issues/84) + ## [4.1.5] - 2020-07-14 ### Added @@ -486,7 +498,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed Culture (e.g. en-us) not being passed correctly in DelimitedFlatFileAttacher - Fixed bug where Updater would show older versions of RDMP as installable 'updates' -[Unreleased]: https://github.com/HicServices/RDMP/compare/v4.1.5...develop +[Unreleased]: https://github.com/HicServices/RDMP/compare/v4.1.6...develop +[4.1.6]: https://github.com/HicServices/RDMP/compare/v4.1.5...v4.1.6 [4.1.5]: https://github.com/HicServices/RDMP/compare/v4.1.4...v4.1.5 [4.1.4]: https://github.com/HicServices/RDMP/compare/v4.1.3...v4.1.4 [4.1.3]: https://github.com/HicServices/RDMP/compare/v4.1.2...v4.1.3 @@ -535,3 +548,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Pipeline]: ./Documentation/CodeTutorials/Glossary.md#Pipeline [Lookup]: ./Documentation/CodeTutorials/Glossary.md#Lookup +[CohortIdentificationConfiguration]: ./Documentation/CodeTutorials/Glossary.md#CohortIdentificationConfiguration \ No newline at end of file diff --git a/Documentation/CodeTutorials/Packages.md b/Documentation/CodeTutorials/Packages.md index 1a143afea9..e04e98af28 100644 --- a/Documentation/CodeTutorials/Packages.md +++ b/Documentation/CodeTutorials/Packages.md @@ -20,7 +20,7 @@ | CsvHelper | [GitHub](https://github.com/JoshClose/CsvHelper) | [15.0.5](https://www.nuget.org/packages/CsvHelper/15.0.5) | MS-PL / Apache 2.0 | Enables reading/writing CSV files | | NPOI | [GitHub](https://github.com/tonyqus/npoi) | [2.4.1](https://www.nuget.org/packages/NPOI/2.4.1) | Apache 2.0 | Enables reading/writing Microsoft Excel files | | ExcelNumberFormat | [GitHub](https://github.com/andersnm/ExcelNumberFormat) | [1.0.10](https://www.nuget.org/packages/ExcelNumberFormat/1.0.10) |[MIT](https://opensource.org/licenses/MIT) | Handles translating number formats from Excel formats into usable values | | -| [NLog](https://nlog-project.org/) | [GitHub](https://github.com/NLog/NLog) | [4.7.2](https://www.nuget.org/packages/NLog/4.7.2) | [BSD 3-Clause](https://github.com/NLog/NLog/blob/dev/LICENSE.txt) | Flexible user configurable logging | | +| [NLog](https://nlog-project.org/) | [GitHub](https://github.com/NLog/NLog) | [4.7.3](https://www.nuget.org/packages/NLog/4.7.3) | [BSD 3-Clause](https://github.com/NLog/NLog/blob/dev/LICENSE.txt) | Flexible user configurable logging | | | HIC.FAnsiSql |[GitHub](https://github.com/HicServices/FAnsiSql) | [1.0.2](https://www.nuget.org/packages/HIC.FansiSql/1.0.2) | [GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.html) | [DBMS] abstraction layer | | HIC.BadMedicine | [GitHub](https://github.com/HicServices/BadMedicine) | [0.1.6](https://www.nuget.org/packages/HIC.BadMedicine/0.1.6) | [GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.html) | Generate Test Datasets for tests/exericses | | SSH.NET | [GitHub](https://github.com/sshnet/SSH.NET) | [2016.1.0](https://www.nuget.org/packages/SSH.NET/2016.1.0) | [MIT](https://github.com/sshnet/SSH.NET/blob/develop/LICENSE) | Enables fetching files from SFTP servers | diff --git a/Plugins/Plugin.Test/Plugin.Test.nuspec b/Plugins/Plugin.Test/Plugin.Test.nuspec index 09094cfb9a..c9529fab4c 100644 --- a/Plugins/Plugin.Test/Plugin.Test.nuspec +++ b/Plugins/Plugin.Test/Plugin.Test.nuspec @@ -21,7 +21,7 @@ - + diff --git a/Plugins/Plugin.UI/Plugin.UI.nuspec b/Plugins/Plugin.UI/Plugin.UI.nuspec index 9c1c4f9954..4e6c2fe0ce 100644 --- a/Plugins/Plugin.UI/Plugin.UI.nuspec +++ b/Plugins/Plugin.UI/Plugin.UI.nuspec @@ -24,7 +24,7 @@ - + diff --git a/Plugins/Plugin/Plugin.nuspec b/Plugins/Plugin/Plugin.nuspec index 6901432fd7..52e9e1d093 100644 --- a/Plugins/Plugin/Plugin.nuspec +++ b/Plugins/Plugin/Plugin.nuspec @@ -18,7 +18,7 @@ - + diff --git a/Rdmp.Core.Tests/CohortCreation/CohortIdentificationConfigurationMergerTests.cs b/Rdmp.Core.Tests/CohortCreation/CohortIdentificationConfigurationMergerTests.cs index eef8df6a4d..62a03e0e76 100644 --- a/Rdmp.Core.Tests/CohortCreation/CohortIdentificationConfigurationMergerTests.cs +++ b/Rdmp.Core.Tests/CohortCreation/CohortIdentificationConfigurationMergerTests.cs @@ -119,5 +119,44 @@ public void TestSimpleUnMerge() Assert.IsFalse(results[1].RootCohortAggregateContainer.GetAllAggregateConfigurationsRecursively().Intersect(new []{ aggregate1,aggregate2,aggregate3}).Any(),"Expected new aggregates to be new!"); } + + [Test] + public void TestSimpleImportCic() + { + var merger = new CohortIdentificationConfigurationMerger(CatalogueRepository); + + var cic1 = new CohortIdentificationConfiguration(CatalogueRepository,"cic1"); + var cic2 = new CohortIdentificationConfiguration(CatalogueRepository,"cic2"); + + cic1.CreateRootContainerIfNotExists(); + var root1 = cic1.RootCohortAggregateContainer; + root1.Name = "Root1"; + root1.SaveToDatabase(); + root1.AddChild(aggregate1,1); + + cic2.CreateRootContainerIfNotExists(); + var root2 = cic2.RootCohortAggregateContainer; + root2.Name = "Root2"; + root2.SaveToDatabase(); + root2.AddChild(aggregate2,2); + + Assert.AreEqual(1,cic1.RootCohortAggregateContainer.GetAllAggregateConfigurationsRecursively().Count); + Assert.AreEqual(1,cic2.RootCohortAggregateContainer.GetAllAggregateConfigurationsRecursively().Count); + + int numberOfCicsBefore = CatalogueRepository.GetAllObjects().Count(); + + //import 2 into 1 + merger.Import(new []{cic2 },cic1.RootCohortAggregateContainer); + + //no new cics + Assert.AreEqual(numberOfCicsBefore,CatalogueRepository.GetAllObjects().Count()); + + // cic 1 should now have both aggregates + Assert.AreEqual(2,cic1.RootCohortAggregateContainer.GetAllAggregateConfigurationsRecursively().Count); + + Assert.AreEqual("Root1",cic1.RootCohortAggregateContainer.Name); + Assert.AreEqual("Root2",cic1.RootCohortAggregateContainer.GetSubContainers()[0].Name); + + } } } diff --git a/Rdmp.Core.Tests/CommandExecution/CommandCliTests.cs b/Rdmp.Core.Tests/CommandExecution/CommandCliTests.cs index 755bd5aae2..f6f97aa0eb 100644 --- a/Rdmp.Core.Tests/CommandExecution/CommandCliTests.cs +++ b/Rdmp.Core.Tests/CommandExecution/CommandCliTests.cs @@ -5,6 +5,8 @@ // 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 Moq; using Rdmp.Core.CommandExecution; using Rdmp.Core.CommandLine.Interactive; using ReusableLibraryCode.Checks; @@ -35,5 +37,14 @@ protected CommandInvoker GetInvoker() return invoker; } + + protected Mock GetMockActivator() + { + var mock = new Mock(); + mock.Setup(m => m.RepositoryLocator).Returns(RepositoryLocator); + mock.Setup(m => m.GetDelegates()).Returns(new List()); + mock.Setup(m => m.Show(It.IsAny())); + return mock; + } } } \ No newline at end of file diff --git a/Rdmp.Core.Tests/CommandExecution/ExecuteCommandListTests.cs b/Rdmp.Core.Tests/CommandExecution/ExecuteCommandListTests.cs new file mode 100644 index 0000000000..fefbb0327c --- /dev/null +++ b/Rdmp.Core.Tests/CommandExecution/ExecuteCommandListTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) The University of Dundee 2018-2019 +// 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 Moq; +using NUnit.Framework; +using Rdmp.Core.CommandExecution.AtomicCommands; +using Rdmp.Core.CommandLine.Interactive.Picking; +using Rdmp.Core.Curation.Data; +using System.Text.RegularExpressions; + +namespace Rdmp.Core.Tests.CommandExecution +{ + class TestsExecuteCommandList : CommandCliTests + { + [Test] + public void Test_ExecuteCommandList_NoCataloguesParsing() + { + foreach(var cat in RepositoryLocator.CatalogueRepository.GetAllObjects()) + cat.DeleteInDatabase(); + + Assert.IsEmpty(RepositoryLocator.CatalogueRepository.GetAllObjects()); + + GetInvoker().ExecuteCommand(typeof(ExecuteCommandList), + new CommandLineObjectPicker(new string[]{ "Catalogue"}, RepositoryLocator)); + } + + [Test] + public void Test_ExecuteCommandList_OneCatalogueParsing() + { + var c = WhenIHaveA(); + + GetInvoker().ExecuteCommand(typeof(ExecuteCommandList), + new CommandLineObjectPicker(new string[]{ "Catalogue"}, RepositoryLocator)); + + c.DeleteInDatabase(); + } + [Test] + public void Test_ExecuteCommandList_OneCatalogue() + { + var c = WhenIHaveA(); + c.Name = "fff"; + c.SaveToDatabase(); + + var mock = GetMockActivator(); + + var cmd = new ExecuteCommandList(mock.Object,new []{c}); + Assert.IsFalse(cmd.IsImpossible,cmd.ReasonCommandImpossible); + + cmd.Execute(); + + string contents = Regex.Escape($"{c.ID}:fff"); + + // Called once + mock.Verify(m => m.Show(It.IsRegex(contents)), Times.Once()); + + c.DeleteInDatabase(); + } + } +} \ No newline at end of file diff --git a/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandAssociateCatalogueWithLoadMetadata.cs b/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandAssociateCatalogueWithLoadMetadata.cs new file mode 100644 index 0000000000..ec147f7b46 --- /dev/null +++ b/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandAssociateCatalogueWithLoadMetadata.cs @@ -0,0 +1,42 @@ +// Copyright (c) The University of Dundee 2018-2019 +// 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.Picking; +using Rdmp.Core.Curation.Data; +using Rdmp.Core.Curation.Data.DataLoad; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rdmp.Core.Tests.CommandExecution +{ + class TestExecuteCommandAssociateCatalogueWithLoadMetadata : CommandCliTests + { + + [Test] + public void TestExecuteCommandAssociateCatalogueWithLoadMetadata_Simple() + { + var cata1 = new Catalogue(RepositoryLocator.CatalogueRepository,"fff"); + var cata2 = new Catalogue(RepositoryLocator.CatalogueRepository,"bbb"); + + Assert.IsNull(cata1.LoadMetadata); + Assert.IsNull(cata2.LoadMetadata); + + var lmd = new LoadMetadata(RepositoryLocator.CatalogueRepository,"mylmd"); + + GetInvoker().ExecuteCommand(typeof(ExecuteCommandAssociateCatalogueWithLoadMetadata), + new CommandLineObjectPicker(new[]{$"LoadMetadata:{lmd.ID}", "Catalogue:fff"}, RepositoryLocator)); + + cata1.RevertToDatabaseState(); + cata2.RevertToDatabaseState(); + + Assert.AreEqual(lmd.ID,cata1.LoadMetadata_ID); + Assert.IsNull(cata2.LoadMetadata); + } + } +} diff --git a/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandDescribeCommand.cs b/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandDescribeCommand.cs index 500ebd8710..eeb94bf57f 100644 --- a/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandDescribeCommand.cs +++ b/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandDescribeCommand.cs @@ -16,16 +16,8 @@ namespace Rdmp.Core.Tests.CommandExecution { - class TestExecuteCommandDescribeCommand : UnitTests + class TestExecuteCommandDescribeCommand : CommandCliTests { - private Mock GetMock() - { - var mock = new Mock(); - mock.Setup(m => m.RepositoryLocator).Returns(RepositoryLocator); - mock.Setup(m => m.GetDelegates()).Returns(new List()); - mock.Setup(m => m.Show(It.IsAny())); - return mock; - } /// /// Asserts that the help text matches your text @@ -34,7 +26,7 @@ private Mock GetMock() /// private void AssertHelpIs(string expectedHelp, Type forCommand) { - var mock = GetMock(); + var mock = GetMockActivator(); var cmd = new ExecuteCommandDescribeCommand(mock.Object, forCommand); Assert.IsFalse(cmd.IsImpossible,cmd.ReasonCommandImpossible); diff --git a/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandSet.cs b/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandSet.cs index 9254bddba8..f3de5afc0f 100644 --- a/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandSet.cs +++ b/Rdmp.Core.Tests/CommandExecution/TestExecuteCommandSet.cs @@ -12,6 +12,7 @@ using Rdmp.Core.CommandExecution.AtomicCommands; using Rdmp.Core.CommandLine.Interactive.Picking; using Rdmp.Core.Curation.Data; +using Rdmp.Core.Curation.Data.DataLoad; using Tests.Common; namespace Rdmp.Core.Tests.CommandExecution @@ -43,5 +44,33 @@ public void Test_CatalogueDescription_Null() Assert.IsNull(cata.Description); } + + [Test] + public void TestExecuteCommandSet_SetArrayValueFromCLI() + { + var pta = WhenIHaveA(); + pta.SetType(typeof(TableInfo[])); + pta.Name = "TablesToIsolate"; + pta.SaveToDatabase(); + + var t1 = WhenIHaveA(); + var t2 = WhenIHaveA(); + var t3 = WhenIHaveA(); + var t4 = WhenIHaveA(); + + var ids = t1.ID + "," + t2.ID + "," + t3.ID + "," + t4.ID; + + Assert.IsNull(pta.Value); + Assert.IsNull(pta.GetValueAsSystemType()); + + GetInvoker().ExecuteCommand(typeof(ExecuteCommandSet),new CommandLineObjectPicker(new []{"ProcessTaskArgument:TablesToIsolate" ,"Value",ids},RepositoryLocator)); + + Assert.AreEqual(ids,pta.Value); + + Assert.Contains(t1,(TableInfo[])pta.GetValueAsSystemType()); + Assert.Contains(t2,(TableInfo[])pta.GetValueAsSystemType()); + Assert.Contains(t3,(TableInfo[])pta.GetValueAsSystemType()); + Assert.Contains(t4,(TableInfo[])pta.GetValueAsSystemType()); + } } } diff --git a/Rdmp.Core.Tests/CommandLine/CommandLineObjectPickerTests.cs b/Rdmp.Core.Tests/CommandLine/CommandLineObjectPickerTests.cs index 26bd55517e..f151ab8d03 100644 --- a/Rdmp.Core.Tests/CommandLine/CommandLineObjectPickerTests.cs +++ b/Rdmp.Core.Tests/CommandLine/CommandLineObjectPickerTests.cs @@ -120,6 +120,34 @@ public void Test_PickCatalogueByName_PickTwo() Assert.AreEqual(2,picker[0].DatabaseEntities.Count); } + [Test] + public void TestPicker_TypeYieldsEmptyArrayOfObjects() + { + foreach(var cat in RepositoryLocator.CatalogueRepository.GetAllObjects()) + cat.DeleteInDatabase(); + + Assert.IsEmpty(RepositoryLocator.CatalogueRepository.GetAllObjects()); + + //when interpreting the string "Catalogue" for a command + var picker = new CommandLineObjectPicker(new []{"Catalogue" },RepositoryLocator); + + //we can pick it as either a Catalogue or a collection of all the Catalogues + Assert.AreEqual(typeof(Catalogue),picker.Arguments.Single().Type); + Assert.IsEmpty(picker.Arguments.Single().DatabaseEntities); + + //when interpretting as a Type we get Catalogue + Assert.IsTrue(picker.Arguments.First().HasValueOfType(typeof(Type))); + Assert.AreEqual(typeof(Catalogue),picker.Arguments.Single().GetValueForParameterOfType(typeof(Type))); + + //if it is looking for an ienumerable of objects + Assert.IsTrue(picker.Arguments.First().HasValueOfType(typeof(IMapsDirectlyToDatabaseTable[]))); + Assert.IsEmpty((IMapsDirectlyToDatabaseTable[])picker.Arguments.First().GetValueForParameterOfType(typeof(IMapsDirectlyToDatabaseTable[]))); + + Assert.IsTrue(picker.Arguments.First().HasValueOfType(typeof(Catalogue[]))); + Assert.IsEmpty(((Catalogue[])picker.Arguments.First().GetValueForParameterOfType(typeof(Catalogue[]))).ToArray()); + + } + [TestCase(typeof(PickDatabase))] [TestCase(typeof(PickTable))] [TestCase(typeof(PickObjectByID))] diff --git a/Rdmp.Core.Tests/Curation/Integration/LoadProgressUnitTests.cs b/Rdmp.Core.Tests/Curation/Integration/LoadProgressUnitTests.cs new file mode 100644 index 0000000000..028777f895 --- /dev/null +++ b/Rdmp.Core.Tests/Curation/Integration/LoadProgressUnitTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) The University of Dundee 2018-2019 +// 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.Curation; +using Rdmp.Core.Curation.Data; +using Rdmp.Core.DataLoad.Engine.Job.Scheduling; +using Rdmp.Core.DataLoad.Engine.LoadProcess.Scheduling.Strategy; +using ReusableLibraryCode.Checks; +using ReusableLibraryCode.Progress; +using System; +using System.IO; +using Tests.Common; + +namespace Rdmp.Core.Tests.Curation.Integration +{ + public class LoadProgressUnitTests : UnitTests + { + [Test] + public void LoadProgress_Checks_BadDates() + { + var lp = WhenIHaveA(); + + lp.Check(new ThrowImmediatelyCheckNotifier()); + + //Bad Origin Date + lp.OriginDate = DateTime.Now.AddDays(1); + Assert.Throws(()=>lp.Check(new ThrowImmediatelyCheckNotifier())); + + //Back to normal + lp.RevertToDatabaseState(); + lp.Check(new ThrowImmediatelyCheckNotifier()); + + //Bad ProgressDate + lp.DataLoadProgress = DateTime.Now.AddDays(1); + Assert.Throws(()=>lp.Check(new ThrowImmediatelyCheckNotifier())); + + //Back to normal + lp.RevertToDatabaseState(); + lp.Check(new ThrowImmediatelyCheckNotifier()); + + + //negative progress + lp.OriginDate = new DateTime(2001,1,1); + lp.DataLoadProgress = new DateTime(2000,1,1); + Assert.Throws(()=>lp.Check(new ThrowImmediatelyCheckNotifier())); + + // valid progress (1 year) + lp.OriginDate = new DateTime(2001,1,1); + lp.DataLoadProgress = new DateTime(2002,1,1); + lp.Check(new ThrowImmediatelyCheckNotifier()); + } + + [Test] + public void LoadProgress_JobFactory_NoDates() + { + var lp = WhenIHaveA(); + + + + lp.OriginDate = new DateTime(2001,1,1); + + // We are fully up-to-date + lp.DataLoadProgress = DateTime.Now; + + lp.Check(new ThrowImmediatelyCheckNotifier()); + + var stratFactory = new JobDateGenerationStrategyFactory(new AnyAvailableLoadProgressSelectionStrategy(lp.LoadMetadata)); + var strat = stratFactory.Create(lp,new ThrowImmediatelyDataLoadEventListener()); + + var dir = LoadDirectory.CreateDirectoryStructure(new DirectoryInfo(TestContext.CurrentContext.WorkDirectory),"LoadProgress_JobFactory_NoDates",true); + + var lmd = lp.LoadMetadata; + lmd.LocationOfFlatFiles = dir.RootPath.FullName; + + foreach(var cata in lmd.GetAllCatalogues()) + { + cata.LoggingDataTask = "ff"; + cata.SaveToDatabase(); + } + + + lmd.SaveToDatabase(); + + + var jobFactory = new SingleScheduledJobFactory(lp,strat,999,lp.LoadMetadata,null); + var ex = Assert.Throws(()=>jobFactory.Create(RepositoryLocator,new ThrowImmediatelyDataLoadEventListener(),null)); + + Assert.AreEqual("DatesToRetrieve was empty for load 'MyLoad'. Possibly the load is already up to date?",ex.Message); + + // We have 1 day to load (date is the last fully loaded date) + lp.DataLoadProgress = DateTime.Now.AddDays(-2); + lp.SaveToDatabase(); + + strat = stratFactory.Create(lp,new ThrowImmediatelyDataLoadEventListener()); + jobFactory = new SingleScheduledJobFactory(lp,strat,999,lp.LoadMetadata,null); + + var job = jobFactory.Create(RepositoryLocator,new ThrowImmediatelyDataLoadEventListener(),null); + Assert.AreEqual(1,((ScheduledDataLoadJob)job).DatesToRetrieve.Count); + } + } +} diff --git a/Rdmp.Core.Tests/Curation/Integration/PipelineTests.cs b/Rdmp.Core.Tests/Curation/Integration/PipelineTests.cs index 9c11ad784c..40a000e204 100644 --- a/Rdmp.Core.Tests/Curation/Integration/PipelineTests.cs +++ b/Rdmp.Core.Tests/Curation/Integration/PipelineTests.cs @@ -159,5 +159,36 @@ public void CloneAPipeline(bool revertAfterClone) p.DeleteInDatabase(); p2.DeleteInDatabase(); } + + [Test] + public void CloneAPipeline_BrokenPipes() + { + Pipeline p = new Pipeline(CatalogueRepository); + + //Setup a pipeline with a source component type that doesn't exist + var source = new PipelineComponent(CatalogueRepository, p, typeof (DelimitedFlatFileAttacher), 0); + source.Class = "Trollololol"; + + var arg = source.CreateNewArgument(); + + //Also give the source component a non existent argument + arg.GetType().GetProperty("Type").SetValue(arg,"fffffzololz"); + arg.SaveToDatabase(); + + p.SourcePipelineComponent_ID = source.ID; + p.SaveToDatabase(); + + Assert.AreEqual("fffffzololz",p.Source.GetAllArguments().Single().Type); + + var clone = p.Clone(); + + Assert.AreEqual(clone.Source.Class,p.Source.Class); + Assert.AreEqual("fffffzololz",clone.Source.GetAllArguments().Single().Type); + + p.DeleteInDatabase(); + clone.DeleteInDatabase(); + + + } } } diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandAssociateCatalogueWithLoadMetadata.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandAssociateCatalogueWithLoadMetadata.cs index 23f7f99c39..e0a40fb3ed 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandAssociateCatalogueWithLoadMetadata.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandAssociateCatalogueWithLoadMetadata.cs @@ -9,6 +9,7 @@ using Rdmp.Core.Curation.Data; using Rdmp.Core.Curation.Data.DataLoad; using Rdmp.Core.Icons.IconProvision; +using Rdmp.Core.Repositories.Construction; using ReusableLibraryCode.Icons.IconProvision; namespace Rdmp.Core.CommandExecution.AtomicCommands @@ -19,7 +20,22 @@ public class ExecuteCommandAssociateCatalogueWithLoadMetadata:BasicCommandExecut private readonly Catalogue[] _availableCatalogues; private readonly ICatalogue[] _otherCatalogues; private Catalogue[] _chosenCatalogues; + + [UseWithObjectConstructor] + public ExecuteCommandAssociateCatalogueWithLoadMetadata(IBasicActivateItems activator, LoadMetadata loadMetadata, Catalogue[] toAssociate) : this(activator,loadMetadata) + { + //if command is possible, select those that are available for association + if(!IsImpossible) + { + _chosenCatalogues = _availableCatalogues.Intersect(toAssociate).ToArray(); + + if(_chosenCatalogues.Length == 0) + SetImpossible($"None of the provided Catalogues are available for association with the LoadMetadata '{loadMetadata}'"); + } + + + } public ExecuteCommandAssociateCatalogueWithLoadMetadata(IBasicActivateItems activator, LoadMetadata loadMetadata) : base(activator) { _loadMetadata = loadMetadata; diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandImportCohortIdentificationConfiguration.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandImportCohortIdentificationConfiguration.cs new file mode 100644 index 0000000000..f53b3abffd --- /dev/null +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandImportCohortIdentificationConfiguration.cs @@ -0,0 +1,64 @@ +// Copyright (c) The University of Dundee 2018-2019 +// 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 Rdmp.Core.Icons.IconProvision; +using Rdmp.Core.Repositories; +using ReusableLibraryCode.Icons.IconProvision; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace Rdmp.Core.CommandExecution.AtomicCommands +{ + /// + /// Clone and import one or more into a root or subcontainer of another + /// + public class ExecuteCommandImportCohortIdentificationConfiguration : BasicCommandExecution + { + public CohortAggregateContainer IntoContainer { get; } + public CohortIdentificationConfiguration[] ToImport{get;} + + public ExecuteCommandImportCohortIdentificationConfiguration(IBasicActivateItems activator,CohortIdentificationConfiguration[] toImport, CohortAggregateContainer intoContainer) : base(activator) + { + ToImport = toImport; + IntoContainer = intoContainer; + + if(IntoContainer == null) + { + SetImpossible("You must specify a container"); + return; + } + + } + public override Image GetImage(IIconProvider iconProvider) + { + return iconProvider.GetImage(RDMPConcept.CohortIdentificationConfiguration,OverlayKind.Add); + } + public override void Execute() + { + base.Execute(); + + var import = ToImport; + + if(import == null) + if(!SelectMany(BasicActivator.RepositoryLocator.CatalogueRepository.GetAllObjects(),out import)) + return; + + if(import == null || !import.Any()) + return; + + + var merger = new CohortIdentificationConfigurationMerger((CatalogueRepository)BasicActivator.RepositoryLocator.CatalogueRepository); + merger.Import(import,IntoContainer); + + Publish(IntoContainer); + } + + } +} diff --git a/Rdmp.Core/CommandLine/Interactive/Picking/CommandLineObjectPickerArgumentValue.cs b/Rdmp.Core/CommandLine/Interactive/Picking/CommandLineObjectPickerArgumentValue.cs index 4b3f6cb354..58b9ce9c82 100644 --- a/Rdmp.Core/CommandLine/Interactive/Picking/CommandLineObjectPickerArgumentValue.cs +++ b/Rdmp.Core/CommandLine/Interactive/Picking/CommandLineObjectPickerArgumentValue.cs @@ -226,16 +226,18 @@ public CommandLineObjectPickerArgumentValue Merge(IEnumerable + /// Clone and import one or more into the target + /// + /// + /// The container into which you want to add the + public void Import(CohortIdentificationConfiguration[] cics, CohortAggregateContainer into) + { + var cicInto = into.GetCohortIdentificationConfiguration(); + + if(cicInto == null) + throw new ArgumentException($"Cannot import into orphan container '{into}'",nameof(into)); + + //clone them + var cicClones = new CohortIdentificationConfiguration[cics.Length]; + try + { + for (int i = 0; i < cics.Length; i++) + { + cicClones[i] = cics[i].CreateClone(new ThrowImmediatelyCheckNotifier()); + } + } + catch(Exception ex) + { + throw new Exception("Error during pre import cloning stage, no import will be attempted",ex); + } + + + using(_repository.BeginNewTransactedConnection()) + { + //Grab the root container of each of the input cics + foreach(CohortIdentificationConfiguration cic in cicClones) + { + var container = cic.RootCohortAggregateContainer; + + //clear them to avoid dual parentage + cic.RootCohortAggregateContainer_ID = null; + cic.SaveToDatabase(); + + //add them into the target SET operation container you are importing into + into.AddChild(container); + + // Make the new name of all the AggregateConfigurations match the owner of import into container + foreach(var child in container.GetAllAggregateConfigurationsRecursively()) + EnsureNamingConvention(cicInto,child); + + // Delete the old now empty clones + cic.DeleteInDatabase(); + } + + //finish transaction + _repository.EndTransactedConnection(true); + } + } + + + private void EnsureNamingConvention(CohortIdentificationConfiguration cic, AggregateConfiguration ac) { //clear any old cic_x prefixes diff --git a/Rdmp.Core/Curation/Data/ILoadProgress.cs b/Rdmp.Core/Curation/Data/ILoadProgress.cs index 58f6766a6e..64a414acf1 100644 --- a/Rdmp.Core/Curation/Data/ILoadProgress.cs +++ b/Rdmp.Core/Curation/Data/ILoadProgress.cs @@ -8,6 +8,7 @@ using MapsDirectlyToDatabaseTable; using Rdmp.Core.Curation.Data.Cache; using Rdmp.Core.Curation.Data.DataLoad; +using ReusableLibraryCode.Checks; namespace Rdmp.Core.Curation.Data { @@ -15,7 +16,7 @@ namespace Rdmp.Core.Curation.Data /// Describes the progress of a large iterative load which cannot be completed in a single batch. Includes start and end dates for what is trying to /// be loaded as well as how far through that process progress has been made up to date. /// - public interface ILoadProgress :INamed + public interface ILoadProgress :INamed, ICheckable { /// /// The date the dataset starts at, this is in dataset time e.g. if you have prescribing records held from 2001-01-01 to present then the is 2001-01-01 diff --git a/Rdmp.Core/Curation/Data/LoadProgress.cs b/Rdmp.Core/Curation/Data/LoadProgress.cs index 0366e3a312..cb683e4450 100644 --- a/Rdmp.Core/Curation/Data/LoadProgress.cs +++ b/Rdmp.Core/Curation/Data/LoadProgress.cs @@ -14,11 +14,12 @@ using Rdmp.Core.Curation.Data.DataLoad; using Rdmp.Core.Repositories; using ReusableLibraryCode.Annotations; +using ReusableLibraryCode.Checks; namespace Rdmp.Core.Curation.Data { /// - public class LoadProgress : DatabaseEntity, ILoadProgress + public class LoadProgress : DatabaseEntity, ILoadProgress, ICheckable { #region Database Properties private bool _isDisabled; @@ -124,5 +125,19 @@ public override string ToString() { return Name + " ID=" + ID; } + + public void Check(ICheckNotifier notifier) + { + if(OriginDate != null && DataLoadProgress != null) + if(OriginDate > DataLoadProgress) + notifier.OnCheckPerformed(new CheckEventArgs($"OriginDate of '{Name}' is set after DataLoadProgress date. LoadProgress cannot have negative progress",CheckResult.Fail)); + + if(OriginDate != null && OriginDate > DateTime.Now) + notifier.OnCheckPerformed(new CheckEventArgs($"OriginDate cannot be in the future ({Name})",CheckResult.Fail)); + + if(DataLoadProgress != null && DataLoadProgress > DateTime.Now) + notifier.OnCheckPerformed(new CheckEventArgs($"DataLoadProgress cannot be in the future ({Name})",CheckResult.Fail)); + + } } } diff --git a/Rdmp.Core/Curation/Data/Pipelines/IPipelineComponentArgument.cs b/Rdmp.Core/Curation/Data/Pipelines/IPipelineComponentArgument.cs index 1ae2fda4bf..7fa4de1389 100644 --- a/Rdmp.Core/Curation/Data/Pipelines/IPipelineComponentArgument.cs +++ b/Rdmp.Core/Curation/Data/Pipelines/IPipelineComponentArgument.cs @@ -20,5 +20,11 @@ public interface IPipelineComponentArgument : IArgument, IMapsDirectlyToDatabase /// per public property with on the . /// int PipelineComponent_ID { get; set; } + + /// + /// Creates a new copy of the current argument and associates it with + /// + /// + void Clone(PipelineComponent intoTargetComponent); } } \ No newline at end of file diff --git a/Rdmp.Core/Curation/Data/Pipelines/PipelineComponent.cs b/Rdmp.Core/Curation/Data/Pipelines/PipelineComponent.cs index a9316c6f4b..ac973fe641 100644 --- a/Rdmp.Core/Curation/Data/Pipelines/PipelineComponent.cs +++ b/Rdmp.Core/Curation/Data/Pipelines/PipelineComponent.cs @@ -142,15 +142,9 @@ public PipelineComponent Clone(Pipeline intoTargetPipeline) var cataRepo = (ICatalogueRepository) intoTargetPipeline.Repository; var clone = new PipelineComponent(cataRepo, intoTargetPipeline, GetClassAsSystemType(), Order); - foreach (IPipelineComponentArgument argument in PipelineComponentArguments) + foreach (var argument in PipelineComponentArguments) { - var cloneArg = new PipelineComponentArgument(cataRepo, clone); - - cloneArg.Name = argument.Name; - cloneArg.Value = argument.Value; - cloneArg.SetType(argument.GetSystemType()); - cloneArg.Description = argument.Description; - cloneArg.SaveToDatabase(); + argument.Clone(clone); } clone.Name = Name; diff --git a/Rdmp.Core/Curation/Data/Pipelines/PipelineComponentArgument.cs b/Rdmp.Core/Curation/Data/Pipelines/PipelineComponentArgument.cs index 14da761d2d..44562b2c98 100644 --- a/Rdmp.Core/Curation/Data/Pipelines/PipelineComponentArgument.cs +++ b/Rdmp.Core/Curation/Data/Pipelines/PipelineComponentArgument.cs @@ -87,5 +87,17 @@ public IHasDependencies[] GetObjectsDependingOnThis() { return new IHasDependencies[0]; } + + /// + public void Clone(PipelineComponent intoTargetComponent) + { + var cloneArg = new PipelineComponentArgument(intoTargetComponent.CatalogueRepository, intoTargetComponent); + + cloneArg.Name = Name; + cloneArg.Value = Value; + cloneArg.Type = Type; + cloneArg.Description = Description; + cloneArg.SaveToDatabase(); + } } } diff --git a/Rdmp.Core/DataLoad/Engine/Checks/CheckEntireDataLoadProcess.cs b/Rdmp.Core/DataLoad/Engine/Checks/CheckEntireDataLoadProcess.cs index 22b395a71d..a023ddb48d 100644 --- a/Rdmp.Core/DataLoad/Engine/Checks/CheckEntireDataLoadProcess.cs +++ b/Rdmp.Core/DataLoad/Engine/Checks/CheckEntireDataLoadProcess.cs @@ -47,6 +47,8 @@ public void Check(ICheckNotifier notifier) //If the load is a progressable (loaded over time) then make sure any associated caches are compatible with the load ProcessTasks foreach (ILoadProgress loadProgress in LoadMetadata.LoadProgresses) { + loadProgress.Check(notifier); + var cp = loadProgress.CacheProgress; if(cp != null) { diff --git a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs index 5b80fb6565..06b03058c0 100644 --- a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs +++ b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/MultipleScheduleJobFactory.cs @@ -47,7 +47,7 @@ public override bool HasJobs() return _scheduleList.Any(loadProgress => _availableSchedules[loadProgress].GetTotalNumberOfJobs(OverrideNumberOfDaysToLoad??loadProgress.DefaultNumberOfDaysToLoadEachTime, false) > 0); } - public override IDataLoadJob Create(IRDMPPlatformRepositoryServiceLocator repositoryLocator,IDataLoadEventListener listener,HICDatabaseConfiguration configuration) + protected override ScheduledDataLoadJob CreateImpl(IRDMPPlatformRepositoryServiceLocator repositoryLocator,IDataLoadEventListener listener,HICDatabaseConfiguration configuration) { ScheduledDataLoadJob job; var loadProgress = _scheduleList[_lastScheduleId]; diff --git a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/ScheduledJobFactory.cs b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/ScheduledJobFactory.cs index ae2c4bb1bd..4aa6aab464 100644 --- a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/ScheduledJobFactory.cs +++ b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/ScheduledJobFactory.cs @@ -9,6 +9,8 @@ using Rdmp.Core.Logging; using Rdmp.Core.Repositories; using ReusableLibraryCode.Progress; +using System; +using System.Linq; namespace Rdmp.Core.DataLoad.Engine.Job.Scheduling { @@ -27,7 +29,20 @@ protected ScheduledJobFactory(int? overrideNumberOfDaysToLoad, ILoadMetadata loa LogManager = logManager; } - public abstract IDataLoadJob Create(IRDMPPlatformRepositoryServiceLocator repositoryLocator, IDataLoadEventListener listener,HICDatabaseConfiguration configuration); + public abstract bool HasJobs(); + + + public IDataLoadJob Create(IRDMPPlatformRepositoryServiceLocator repositoryLocator, IDataLoadEventListener listener,HICDatabaseConfiguration configuration) + { + var job = CreateImpl(repositoryLocator,listener,configuration); + + if(job.DatesToRetrieve == null || !job.DatesToRetrieve.Any()) + throw new Exception($"DatesToRetrieve was empty for load '{LoadMetadata}'. Possibly the load is already up to date?"); + + return job; + } + + protected abstract ScheduledDataLoadJob CreateImpl(IRDMPPlatformRepositoryServiceLocator repositoryLocator, IDataLoadEventListener listener, HICDatabaseConfiguration configuration); } } \ No newline at end of file diff --git a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs index de0f3c8d3d..24a3c48782 100644 --- a/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs +++ b/Rdmp.Core/DataLoad/Engine/Job/Scheduling/SingleScheduledJobFactory.cs @@ -11,6 +11,8 @@ using Rdmp.Core.Logging; using Rdmp.Core.Repositories; using ReusableLibraryCode.Progress; +using System; +using System.Linq; namespace Rdmp.Core.DataLoad.Engine.Job.Scheduling { @@ -34,7 +36,7 @@ public override bool HasJobs() return _jobDateGenerationStrategy.GetTotalNumberOfJobs(OverrideNumberOfDaysToLoad??_loadProgress.DefaultNumberOfDaysToLoadEachTime, false) > 0; } - public override IDataLoadJob Create(IRDMPPlatformRepositoryServiceLocator repositoryLocator,IDataLoadEventListener listener,HICDatabaseConfiguration configuration) + protected override ScheduledDataLoadJob CreateImpl(IRDMPPlatformRepositoryServiceLocator repositoryLocator,IDataLoadEventListener listener,HICDatabaseConfiguration configuration) { var LoadDirectory = new LoadDirectory(LoadMetadata.LocationOfFlatFiles); return new ScheduledDataLoadJob(repositoryLocator,JobDescription, LogManager, LoadMetadata, LoadDirectory, listener,configuration) diff --git a/Rdmp.UI/ExtractionUIs/FilterUIs/ExtractionFilterUI.cs b/Rdmp.UI/ExtractionUIs/FilterUIs/ExtractionFilterUI.cs index 4a551a0dac..777fc3c7fa 100644 --- a/Rdmp.UI/ExtractionUIs/FilterUIs/ExtractionFilterUI.cs +++ b/Rdmp.UI/ExtractionUIs/FilterUIs/ExtractionFilterUI.cs @@ -110,33 +110,6 @@ private void FigureOutGlobalsAndAutoComplete() _autoCompleteProvider.RegisterForEvents(QueryEditor); } - - /// - /// Gives the user an option to save the changes to the filter (if they have unsaved changes) call things for example when closing the host form. - /// - public override void ConsultAboutClosing(object sender, FormClosingEventArgs e) - { - if (_extractionFilter != null && _extractionFilter.HasLocalChanges().Evaluation == ChangeDescription.DatabaseCopyDifferent) - if (Activator.YesNo( - "You have unsaved changes to Filter \"" + _extractionFilter.Name + - "\", would you like to save these now?", "Save Changes to Filter?")) - ObjectSaverButton1.Save(); - else - { - try - { - //So there are local changes to the filter but the user doesnt want to save them. We need to undo the local changes to the - //object that we have a reference to. This is important because other classes might still have references to that object too - //so we fetch a fresh copy out of the database (RevertChanges) and set each of the properties to the original (last saved) values - _extractionFilter.RevertToDatabaseState(); - } - catch (Exception ex) - { - ExceptionViewer.Show("Failed to revert changes on filter, did you delete it?",ex); - } - } - } - private bool BeforeSave(DatabaseEntity databaseEntity) { SubstituteQueryEditorTextIfContainsLineComments(); diff --git a/Rdmp.UI/Menus/CohortAggregateContainerMenu.cs b/Rdmp.UI/Menus/CohortAggregateContainerMenu.cs index 813625f8e7..4cd5da9aff 100644 --- a/Rdmp.UI/Menus/CohortAggregateContainerMenu.cs +++ b/Rdmp.UI/Menus/CohortAggregateContainerMenu.cs @@ -48,7 +48,8 @@ public CohortAggregateContainerMenu(RDMPContextMenuStripArgs args, CohortAggrega Items.Add("Add Aggregate(s) into container", _activator.CoreIconProvider.GetImage(RDMPConcept.AggregateGraph, OverlayKind.Import), (s, e) => AddAggregates()); Items.Add("Import (Copy of) Cohort Set into container", _activator.CoreIconProvider.GetImage(RDMPConcept.CohortAggregate, OverlayKind.Import), (s, e) => AddCohortAggregate()); - + Add(new ExecuteCommandImportCohortIdentificationConfiguration(_activator,null,container)); + foreach (ToolStripMenuItem item in Items) item.Enabled = item.Enabled && (cic != null && !cic.Frozen); diff --git a/Rdmp.UI/SimpleControls/ObjectSaverButton.cs b/Rdmp.UI/SimpleControls/ObjectSaverButton.cs index 7248a8ded5..10b47fb64d 100644 --- a/Rdmp.UI/SimpleControls/ObjectSaverButton.cs +++ b/Rdmp.UI/SimpleControls/ObjectSaverButton.cs @@ -242,7 +242,8 @@ private bool IsDifferent() public void CheckForUnsavedChangesAnOfferToSave() { - if (_o == null) + // If there is no object or it does not exist don't try to save it + if (_o == null || !_o.Exists()) return; if (_isEnabled) diff --git a/Reusable/MapsDirectlyToDatabaseTable/MapsDirectlyToDatabaseTable.csproj b/Reusable/MapsDirectlyToDatabaseTable/MapsDirectlyToDatabaseTable.csproj index 9ce8ca5145..86a0d63329 100644 --- a/Reusable/MapsDirectlyToDatabaseTable/MapsDirectlyToDatabaseTable.csproj +++ b/Reusable/MapsDirectlyToDatabaseTable/MapsDirectlyToDatabaseTable.csproj @@ -23,7 +23,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs index 46a3c9d80e..fcb5bb420f 100644 --- a/SharedAssemblyInfo.cs +++ b/SharedAssemblyInfo.cs @@ -6,7 +6,6 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// These should be replaced with correct values by the release process -[assembly: AssemblyVersion("4.1.5")] -[assembly: AssemblyFileVersion("4.1.5")] -[assembly: AssemblyInformationalVersion("4.1.5")] +[assembly: AssemblyVersion("4.1.6")] +[assembly: AssemblyFileVersion("4.1.6")] +[assembly: AssemblyInformationalVersion("4.1.6")] diff --git a/appveyor.yml b/appveyor.yml index 0fb4d0f58d..5a55ce9b4e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,13 @@ services: - mysql - postgresql101 +cache: + - '%USERPROFILE%\.nuget\packages -> **\*.csproj' + - C:\ProgramData\chocolatey\bin -> appveyor.yml + - C:\ProgramData\chocolatey\lib -> appveyor.yml + before_build: +- cmd: if defined APPVEYOR_PULL_REQUEST_NUMBER appveyor exit - dotnet restore --packages ./packages - choco install opencover.portable