diff --git a/CHANGELOG.md b/CHANGELOG.md index 172ecdb1dd..3bd8a30350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ + # Changelog All notable changes to this project will be documented in this file. @@ -7,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [8.4.0] - Unreleased +- Add Ordering to Filters + ## [8.3.1] - Unreleased - Improve Performance of regenerating problems with child providers diff --git a/Rdmp.Core/Curation/Data/Aggregation/AggregateFilter.cs b/Rdmp.Core/Curation/Data/Aggregation/AggregateFilter.cs index 7b23404346..307a7ef8aa 100644 --- a/Rdmp.Core/Curation/Data/Aggregation/AggregateFilter.cs +++ b/Rdmp.Core/Curation/Data/Aggregation/AggregateFilter.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. @@ -35,6 +35,7 @@ public class AggregateFilter : ConcreteFilter, IDisableable private int? _clonedFromExtractionFilterID; private int? _associatedColumnInfoID; private bool _isDisabled; + private int _order; /// public override int? ClonedFromExtractionFilter_ID @@ -90,6 +91,8 @@ public IEnumerable AggregateFilterParameters ? Repository.GetObjectByID(FilterContainer_ID.Value) : null; + public override int Order { get => _order; set => SetField(ref _order, value); } + #endregion public AggregateFilter() @@ -121,6 +124,7 @@ internal AggregateFilter(ICatalogueRepository repository, DbDataReader r) : base Name = r["Name"] as string; IsMandatory = (bool)r["IsMandatory"]; ClonedFromExtractionFilter_ID = ObjectToNullableInt(r["ClonedFromExtractionFilter_ID"]); + Order = int.Parse(r["Order"].ToString()); var associatedColumnInfo_ID = r["AssociatedColumnInfo_ID"]; if (associatedColumnInfo_ID != DBNull.Value) diff --git a/Rdmp.Core/Curation/Data/ConcreteFilter.cs b/Rdmp.Core/Curation/Data/ConcreteFilter.cs index c29ebbdeca..b8fe1f49de 100644 --- a/Rdmp.Core/Curation/Data/ConcreteFilter.cs +++ b/Rdmp.Core/Curation/Data/ConcreteFilter.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. @@ -25,7 +25,7 @@ namespace Rdmp.Core.Curation.Data; /// ConcreteFilter is used to provide UI editing of an IFilter without having to add persistence / DatabaseEntity logic to IFilter (which would break /// SpontaneouslyInventedFilters) /// -public abstract class ConcreteFilter : DatabaseEntity, IFilter, ICheckable +public abstract class ConcreteFilter : DatabaseEntity, IFilter, ICheckable, IOrderable { /// protected ConcreteFilter(IRepository repository, DbDataReader r) : base(repository, r) @@ -100,6 +100,7 @@ public bool IsMandatory /// [NoMappingToDatabase] public abstract IContainer FilterContainer { get; } + public abstract int Order { get; set; } #endregion diff --git a/Rdmp.Core/Curation/Data/ExtractionFilter.cs b/Rdmp.Core/Curation/Data/ExtractionFilter.cs index f6ce6f8d4c..febd12b629 100644 --- a/Rdmp.Core/Curation/Data/ExtractionFilter.cs +++ b/Rdmp.Core/Curation/Data/ExtractionFilter.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,7 @@ public class ExtractionFilter : ConcreteFilter, IHasDependencies, IInjectKnown _knownExtractionFilterParameterSets; + private int _order; /// /// The column in the which is best/most associated with this filter. A filter can query any column in any of the table(s) under @@ -133,6 +134,8 @@ internal ExtractionFilter(ICatalogueRepository repository, DbDataReader r) Description = r["Description"] as string; Name = r["Name"] as string; IsMandatory = (bool)r["IsMandatory"]; + Order = int.Parse(r["Order"].ToString()); + ClearAllInjections(); } @@ -154,6 +157,7 @@ public override int? ClonedFromExtractionFilter_ID set => throw new NotSupportedException( "ClonedFromExtractionFilter_ID is only supported on lower level filters e.g. DeployedExtractionFilter and AggregateFilter"); } + public override int Order { get => _order; set => SetField(ref _order,value); } /// public IHasDependencies[] GetObjectsThisDependsOn() diff --git a/Rdmp.Core/Curation/Data/Spontaneous/SpontaneouslyInventedFilter.cs b/Rdmp.Core/Curation/Data/Spontaneous/SpontaneouslyInventedFilter.cs index 8bbadc4f85..dd8458426f 100644 --- a/Rdmp.Core/Curation/Data/Spontaneous/SpontaneouslyInventedFilter.cs +++ b/Rdmp.Core/Curation/Data/Spontaneous/SpontaneouslyInventedFilter.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. @@ -22,6 +22,7 @@ public class SpontaneouslyInventedFilter : ConcreteFilter { private readonly MemoryCatalogueRepository _repo; private readonly ISqlParameter[] _filterParametersIfAny; + private int _order =0; /// /// Creates a new temporary (unsaveable) filter in the given memory @@ -68,6 +69,8 @@ public SpontaneouslyInventedFilter(MemoryCatalogueRepository repo, IFilter copyF ? _repo.GetObjectByID(FilterContainer_ID.Value) : null; + public override int Order { get => _order; set => SetField(ref _order, value); } + public override ColumnInfo GetColumnInfoIfExists() => null; public override IFilterFactory GetFilterFactory() => null; diff --git a/Rdmp.Core/DataExport/Data/DeployedExtractionFilter.cs b/Rdmp.Core/DataExport/Data/DeployedExtractionFilter.cs index d1e7960ade..122eece307 100644 --- a/Rdmp.Core/DataExport/Data/DeployedExtractionFilter.cs +++ b/Rdmp.Core/DataExport/Data/DeployedExtractionFilter.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. @@ -36,6 +36,7 @@ public class DeployedExtractionFilter : ConcreteFilter private int? _clonedFromExtractionFilterID; private int? _filterContainerID; + private int _order; /// public override int? ClonedFromExtractionFilter_ID @@ -69,6 +70,8 @@ public override int? FilterContainer_ID ? Repository.GetObjectByID(FilterContainer_ID.Value) : null; + public override int Order { get => _order; set => SetField(ref _order, value); } + #endregion /// @@ -138,6 +141,7 @@ internal DeployedExtractionFilter(IDataExportRepository repository, DbDataReader FilterContainer_ID = null; ClonedFromExtractionFilter_ID = ObjectToNullableInt(r["ClonedFromExtractionFilter_ID"]); + Order = int.Parse(r["Order"].ToString()); } /// diff --git a/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql b/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql index e794b1e35c..418fcf7e9d 100644 --- a/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql +++ b/Rdmp.Core/Databases/CatalogueDatabase/runAfterCreateDatabase/CreateCatalogue.sql @@ -141,6 +141,7 @@ CREATE TABLE [dbo].[AggregateFilter]( [AssociatedColumnInfo_ID] [int] NULL, [ID] [int] IDENTITY(1,1) NOT NULL, [SoftwareVersion] [nvarchar](50) NOT NULL, + [Order] [int] NOT NULL DEFAULT 0 CONSTRAINT [PK_AggregateFilter] PRIMARY KEY CLUSTERED ( [ID] ASC @@ -464,6 +465,7 @@ CREATE TABLE [dbo].[ExtractionFilter]( [Name] [varchar](100) NOT NULL, [IsMandatory] [bit] NOT NULL, [SoftwareVersion] [nvarchar](50) NOT NULL, + [Order] [int] NOT NULL DEFAULT 0 CONSTRAINT [PK_ExtractionFilter] PRIMARY KEY CLUSTERED ( [ID] ASC diff --git a/Rdmp.Core/Databases/CatalogueDatabase/up/087_AddAggregateFilterOrdering.sql b/Rdmp.Core/Databases/CatalogueDatabase/up/087_AddAggregateFilterOrdering.sql new file mode 100644 index 0000000000..0d3e473416 --- /dev/null +++ b/Rdmp.Core/Databases/CatalogueDatabase/up/087_AddAggregateFilterOrdering.sql @@ -0,0 +1,13 @@ +----Version: 8.4.0 +----Description: Add Order to Aggregate Filters + +if not exists (select 1 from sys.columns where name = 'Order' and OBJECT_NAME(object_id) = 'AggregateFilter') +BEGIN +ALTER TABLE [dbo].[AggregateFilter] +ADD [Order] [int] NOT NULL DEFAULT 0 WITH VALUES +END +if not exists (select 1 from sys.columns where name = 'Order' and OBJECT_NAME(object_id) = 'ExtractionFilter') +BEGIN +ALTER TABLE [dbo].[ExtractionFilter] +ADD [Order] [int] NOT NULL DEFAULT 0 WITH VALUES +END \ No newline at end of file diff --git a/Rdmp.Core/Databases/DataExportDatabase/runAfterCreateDatabase/CreateDataExportManager.sql b/Rdmp.Core/Databases/DataExportDatabase/runAfterCreateDatabase/CreateDataExportManager.sql index 15f75c2cc4..ac1c03a92a 100644 Binary files a/Rdmp.Core/Databases/DataExportDatabase/runAfterCreateDatabase/CreateDataExportManager.sql and b/Rdmp.Core/Databases/DataExportDatabase/runAfterCreateDatabase/CreateDataExportManager.sql differ diff --git a/Rdmp.Core/Databases/DataExportDatabase/up/026_AddFilterOrder.sql b/Rdmp.Core/Databases/DataExportDatabase/up/026_AddFilterOrder.sql new file mode 100644 index 0000000000..1181d65c9b --- /dev/null +++ b/Rdmp.Core/Databases/DataExportDatabase/up/026_AddFilterOrder.sql @@ -0,0 +1,8 @@ +----Version: 8.4.0 +----Description: Add Order to Aggregate Filters + +if not exists (select 1 from sys.columns where name = 'Order' and OBJECT_NAME(object_id) = 'DeployedExtractionFilter') +BEGIN +ALTER TABLE [dbo].[DeployedExtractionFilter] +ADD [Order] [int] NOT NULL DEFAULT 0 WITH VALUES +END \ No newline at end of file diff --git a/Rdmp.Core/Rdmp.Core.csproj b/Rdmp.Core/Rdmp.Core.csproj index 449cc6abdb..3f2209d40d 100644 --- a/Rdmp.Core/Rdmp.Core.csproj +++ b/Rdmp.Core/Rdmp.Core.csproj @@ -129,6 +129,7 @@ + @@ -157,6 +158,7 @@ + @@ -255,6 +257,7 @@ + @@ -297,6 +300,7 @@ + diff --git a/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandReorderFilter.cs b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandReorderFilter.cs new file mode 100644 index 0000000000..e6483fa5a1 --- /dev/null +++ b/Rdmp.UI/CommandExecution/AtomicCommands/ExecuteCommandReorderFilter.cs @@ -0,0 +1,62 @@ +// 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; +using Rdmp.Core.MapsDirectlyToDatabaseTable; +using Rdmp.UI.ItemActivation; +using System; +using System.Linq; + +namespace Rdmp.UI.CommandExecution.AtomicCommands; + +public class ExecuteCommandReorderFilter : BasicUICommandExecution +{ + private ConcreteFilter _source; + private ConcreteFilter _target; + private InsertOption _insertOption; + + public ExecuteCommandReorderFilter(IActivateItems activator, ConcreteFilter source, ConcreteFilter destination, InsertOption insertOption) : base(activator) + { + _source = source; + _target = destination; + _insertOption = insertOption; + if (_source.FilterContainer_ID is null || _target.FilterContainer_ID is null) + { + SetImpossible("Both filters must exist within some container in order to be orderable"); + } + if (_source.FilterContainer_ID != _target.FilterContainer_ID) + { + SetImpossible("Cannot reorder filters as they do not share a parent"); + } + } + + public override void Execute() + { + var order = _target.Order; + + var filters = _target.FilterContainer.GetFilters().Where(f => f is ConcreteFilter).Select(f => (ConcreteFilter)f).ToArray(); + Array.Sort( + filters, + delegate (ConcreteFilter a, ConcreteFilter b) { return a.Order.CompareTo(b.Order); } + ); + if (!filters.All(c => c.Order != order)) + { + foreach (var orderable in filters) + { + if (orderable.Order < order) + orderable.Order--; + else if (orderable.Order > order) + orderable.Order++; + else //collision on order + orderable.Order += _insertOption == InsertOption.InsertAbove ? 1 : -1; + ((ISaveable)orderable).SaveToDatabase(); + } + } + _source.Order = order; + _source.SaveToDatabase(); + Publish(_target.FilterContainer); + } +} diff --git a/Rdmp.UI/CommandExecution/Proposals/ProposeExecutionWhenTargetIsConcreteFilter.cs b/Rdmp.UI/CommandExecution/Proposals/ProposeExecutionWhenTargetIsConcreteFilter.cs index 93f6fb3c1c..98d290b1b0 100644 --- a/Rdmp.UI/CommandExecution/Proposals/ProposeExecutionWhenTargetIsConcreteFilter.cs +++ b/Rdmp.UI/CommandExecution/Proposals/ProposeExecutionWhenTargetIsConcreteFilter.cs @@ -5,7 +5,9 @@ // You should have received a copy of the GNU General Public License along with RDMP. If not, see . using Rdmp.Core.CommandExecution; +using Rdmp.Core.CommandExecution.Combining; using Rdmp.Core.Curation.Data; +using Rdmp.UI.CommandExecution.AtomicCommands; using Rdmp.UI.ExtractionUIs.FilterUIs; using Rdmp.UI.ItemActivation; @@ -24,8 +26,16 @@ public override void Activate(ConcreteFilter target) ItemActivator.Activate(target); } - public override ICommandExecution ProposeExecution(ICombineToMakeCommand cmd, ConcreteFilter target, - InsertOption insertOption = InsertOption.Default) => - //currently nothing can be dropped onto a filter - null; + public override ICommandExecution ProposeExecution(ICombineToMakeCommand cmd, ConcreteFilter targetFilter, + InsertOption insertOption = InsertOption.Default) + { + return cmd switch + { + FilterCombineable sourceFilterCommand => + !sourceFilterCommand.Filter.Equals(targetFilter) && sourceFilterCommand.Filter is ConcreteFilter && sourceFilterCommand.Filter.FilterContainer_ID == targetFilter.FilterContainer_ID ? + new ExecuteCommandReorderFilter(ItemActivator, (ConcreteFilter)sourceFilterCommand.Filter, targetFilter, insertOption) + : null, + _ => null + }; + } } \ No newline at end of file diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs index 559a541653..dd904c2b97 100644 --- a/SharedAssemblyInfo.cs +++ b/SharedAssemblyInfo.cs @@ -10,6 +10,6 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("8.3.0")] -[assembly: AssemblyFileVersion("8.3.0")] -[assembly: AssemblyInformationalVersion("8.3.0")] \ No newline at end of file +[assembly: AssemblyVersion("8.4.0")] +[assembly: AssemblyFileVersion("8.4.0")] +[assembly: AssemblyInformationalVersion("8.4.0")] \ No newline at end of file