diff --git a/Application/ResearchDataManagementPlatform/WindowManagement/ActivateItems.cs b/Application/ResearchDataManagementPlatform/WindowManagement/ActivateItems.cs index 4ca7a92805..b7f6996013 100644 --- a/Application/ResearchDataManagementPlatform/WindowManagement/ActivateItems.cs +++ b/Application/ResearchDataManagementPlatform/WindowManagement/ActivateItems.cs @@ -832,6 +832,11 @@ public void StartSession(string sessionName, IEnumerable + GetCohortHoldoutLookupRequest(externalCohortTable, project, cic)); + + var ui = new Rdmp.UI.CohortUI.CreateHoldoutLookup.CreateHoldoutLookupUI(this, externalCohortTable, cic); + + if (!string.IsNullOrWhiteSpace(cic.Description)) + ui.CohortDescription = $"{cic.Description} ({Environment.UserName} - {DateTime.Now})"; + return ui.ShowDialog() == DialogResult.OK ? ui.Result : null; + } + public override ICatalogue CreateAndConfigureCatalogue(ITableInfo tableInfo, ColumnInfo[] extractionIdentifierColumns, string initialDescription, IProject projectSpecific, string folder) { + if(extractionIdentifierColumns is not null) + { + return base.CreateAndConfigureCatalogue(tableInfo, extractionIdentifierColumns, initialDescription, projectSpecific, folder); + } // if on wrong Thread if (_mainDockPanel?.InvokeRequired ?? false) return _mainDockPanel.Invoke(() => CreateAndConfigureCatalogue(tableInfo, extractionIdentifierColumns, diff --git a/Rdmp.Core/CohortCommitting/Pipeline/CohortHoldoutLookupRequest.cs b/Rdmp.Core/CohortCommitting/Pipeline/CohortHoldoutLookupRequest.cs new file mode 100644 index 0000000000..2299ce6249 --- /dev/null +++ b/Rdmp.Core/CohortCommitting/Pipeline/CohortHoldoutLookupRequest.cs @@ -0,0 +1,67 @@ +// 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 System; +using System.Data; +using System.Globalization; +using System.Security.Permissions; +using NPOI.SS.Formula.Functions; +using Rdmp.Core.Curation.Data.Cohort; +using Rdmp.Core.Curation.Data.Pipelines; +using Rdmp.Core.DataFlowPipeline; +using Rdmp.Core.DataFlowPipeline.Requirements; +using Rdmp.Core.MapsDirectlyToDatabaseTable; +using Rdmp.Core.ReusableLibraryCode.Checks; + +namespace Rdmp.Core.CohortCommitting.Pipeline; + +/// +/// All details required to create a holdout set from a cohort +/// +public sealed class CohortHoldoutLookupRequest : PipelineUseCase, ICanBeSummarised, ICohortHoldoutLookupRequest +{ + public CohortIdentificationConfiguration CIC { get; set; } + public int Count { get; set; } + public bool IsPercent { get; set; } + + public string Description { get; set; } + + public string WhereQuery { get; set; } + + public string Name { get; set; } + + public DateTime MinDate { get; set; } + public DateTime MaxDate { get; set; } + public string DateColumnName { get; set; } + public CohortHoldoutLookupRequest(CohortIdentificationConfiguration cic, string name, int count, bool isPercent, string description = "", string minDate = null, string maxDate = null, string dateColumnName = null) + { + CIC = cic; + Name = name; + Count = count; + IsPercent = isPercent; + Description = description; + if (DateTime.TryParseExact(minDate, "DD/MM/YYYY", CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedMinDate)) + MinDate = parsedMinDate; + if (DateTime.TryParseExact(maxDate, "DD/MM/YYYY", CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedMaxDate)) + MaxDate = parsedMaxDate; + DateColumnName = dateColumnName; + AddInitializationObject(this); + } + public string GetSummary(bool includeName, bool includeId) => $"Cohort Holdout: {Name}"; + + + protected override IDataFlowPipelineContext GenerateContextImpl() => + new DataFlowPipelineContext + { + MustHaveDestination = typeof(ICohortPipelineDestination), + MustHaveSource = typeof(IDataFlowSource) + }; + + public void Check(ICheckNotifier notifier) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Rdmp.Core/CohortCommitting/Pipeline/ICohortHoldoutLookupRequest.cs b/Rdmp.Core/CohortCommitting/Pipeline/ICohortHoldoutLookupRequest.cs new file mode 100644 index 0000000000..f3231b14d7 --- /dev/null +++ b/Rdmp.Core/CohortCommitting/Pipeline/ICohortHoldoutLookupRequest.cs @@ -0,0 +1,19 @@ +// Copyright (c) The University of Dundee 2018-2023 +// 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.Pipelines; +using Rdmp.Core.ReusableLibraryCode.Checks; + +namespace Rdmp.Core.CohortCommitting.Pipeline; + + +/// +/// See CohortHoldoutLookupRequest +/// +public interface ICohortHoldoutLookupRequest : ICheckable, IPipelineUseCase +{ + +} + diff --git a/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs b/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs index 110d6b0f71..86fb978081 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommandFactory.cs @@ -472,6 +472,8 @@ public IEnumerable CreateCommands(object o) yield return new ExecuteCommandFreezeCohortIdentificationConfiguration(_activator, cic, !cic.Frozen) { Weight = -50.5f }; + yield return new ExecuteCommandCreateHoldoutLookup(_activator, cic) + { Weight = -50.5f }; var clone = new ExecuteCommandCloneCohortIdentificationConfiguration(_activator) { Weight = -50.4f, OverrideCommandName = "Clone" }.SetTarget(cic); diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/CatalogueCreationCommands/ExecuteCommandCreateNewCatalogueByImportingFile.cs b/Rdmp.Core/CommandExecution/AtomicCommands/CatalogueCreationCommands/ExecuteCommandCreateNewCatalogueByImportingFile.cs index 9d91705fca..63fa9bc580 100644 --- a/Rdmp.Core/CommandExecution/AtomicCommands/CatalogueCreationCommands/ExecuteCommandCreateNewCatalogueByImportingFile.cs +++ b/Rdmp.Core/CommandExecution/AtomicCommands/CatalogueCreationCommands/ExecuteCommandCreateNewCatalogueByImportingFile.cs @@ -31,6 +31,8 @@ public class ExecuteCommandCreateNewCatalogueByImportingFile : CatalogueCreation { private readonly DiscoveredDatabase _targetDatabase; private IPipeline _pipeline; + private readonly string _extractionIdentifier; + private readonly string _initialDescription; public FileInfo File { get; private set; } @@ -62,13 +64,17 @@ public ExecuteCommandCreateNewCatalogueByImportingFile(IBasicActivateItems activ "Pipeline for reading the source file, applying any transforms and writing to the database")] Pipeline pipeline, [DemandsInitialization(Desc_ProjectSpecificParameter)] - Project projectSpecific) : base(activator, projectSpecific, null) + Project projectSpecific, + string initialDescription=null) : base(activator, projectSpecific, null) + { File = file; _targetDatabase = targetDatabase; _pipeline = pipeline; + _extractionIdentifier = extractionIdentifier; UseTripleDotSuffix = true; CheckFile(); + _initialDescription = initialDescription; } @@ -150,16 +156,21 @@ private void OnPipelineCompleted(object sender, PipelineEngineEventArgs args, Di var importer = new TableInfoImporter(BasicActivator.RepositoryLocator.CatalogueRepository, tbl); importer.DoImport(out var ti, out _); - - var cata = BasicActivator.CreateAndConfigureCatalogue(ti, null, + var extractionIdentifiers = _extractionIdentifier is null ? null : ti.ColumnInfos.Where(t => t.Name == _extractionIdentifier).ToArray(); + var cata = BasicActivator.CreateAndConfigureCatalogue(ti, extractionIdentifiers, $"Import of file '{File.FullName}' by {Environment.UserName} on {DateTime.Now}", ProjectSpecific, TargetFolder); - if (cata != null) + if (cata == null) return; + + if(_initialDescription is not null) { - Publish(cata); - Emphasise(cata); + cata.Description = _initialDescription; + cata.SaveToDatabase(); } + + Publish(cata); + Emphasise(cata); } public override Image GetImage(IIconProvider iconProvider) => diff --git a/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateHoldoutLookup.cs b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateHoldoutLookup.cs new file mode 100644 index 0000000000..4abc88ddae --- /dev/null +++ b/Rdmp.Core/CommandExecution/AtomicCommands/ExecuteCommandCreateHoldoutLookup.cs @@ -0,0 +1,205 @@ +// Copyright (c) The University of Dundee 2018-2023 +// 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 FAnsi.Discovery; +using Rdmp.Core.CohortCommitting.Pipeline; +using Rdmp.Core.CommandExecution.AtomicCommands.CatalogueCreationCommands; +using Rdmp.Core.Curation.Data.Cohort; +using Rdmp.Core.Curation.Data.Pipelines; +using Rdmp.Core.DataExport.Data; +using Rdmp.Core.DataViewing; +using Rdmp.Core.Icons.IconProvision; +using Rdmp.Core.ReusableLibraryCode.DataAccess; +using Rdmp.Core.ReusableLibraryCode.Icons.IconProvision; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.IO; +using System.Linq; +using System.Text; + +namespace Rdmp.Core.CommandExecution.AtomicCommands; + +public class ExecuteCommandCreateHoldoutLookup : BasicCommandExecution +{ + private readonly CohortIdentificationConfiguration _cic; + readonly IBasicActivateItems _activator; + private DiscoveredServer _server; + private DataTable _dataTable; + + + public ExecuteCommandCreateHoldoutLookup(IBasicActivateItems activator, + CohortIdentificationConfiguration cic) : base(activator) + { + _cic = cic; + _activator = activator; + } + + public override string GetCommandName() => "Create Holdout"; + + /// + /// Describes in a user friendly way the activity of picking an + /// + /// + private static DialogArgs GetChooseCohortDialogArgs() => + new() + { + WindowTitle = "Choose where to save cohort", + TaskDescription = + "Select the Cohort Database in which to store the identifiers. If you have multiple methods of anonymising cohorts or manage different types of identifiers (e.g. CHI lists, ECHI lists and/or BarcodeIDs) then you must pick the Cohort Database that matches your cohort identifier type/anonymisation protocol.", + EntryLabel = "Select Cohort Database", + AllowAutoSelect = true + }; + + private DataTable LoadDataTable(DiscoveredServer server, string sql) + { + + var dt = new DataTable(); + + try + { + using var con = server.GetConnection(); + con.Open(); + using var cmd = server.GetCommand(sql, con); + cmd.CommandTimeout = 10000; + var adapter = server.GetDataAdapter(cmd); + dt.BeginLoadData(); + adapter.Fill(dt); + dt.EndLoadData(); + con.Close(); + } + catch (Exception e) + { + GlobalError("Unable to access datatable",e); + } + return dt; + + } + + private const string HoldoutShuffle = "_HoldoutShuffle"; + + public override void Execute() + { + base.Execute(); + + SelectOne(GetChooseCohortDialogArgs(), + BasicActivator.RepositoryLocator.DataExportRepository, + out ExternalCohortTable ect); + if (ect is null) + return; + + var holdoutRequest = BasicActivator.GetCohortHoldoutLookupRequest(ect, null, _cic); + if(holdoutRequest is null) + return; + + var cohortConfiguration = new ViewCohortIdentificationConfigurationSqlCollection(_cic); + var sql = cohortConfiguration.GetSql(); + _server = DataAccessPortal + .ExpectServer(cohortConfiguration.GetDataAccessPoint(), DataAccessContext.InternalDataProcessing, false); + _server.TestConnection(); + _dataTable = LoadDataTable(_server, sql); + if(_dataTable.Rows.Count == 0) + { + Show("Unable to Access Cohort"); + return; + } + StringBuilder sb = new(); + + var columnNames = _dataTable.Columns.Cast(). + Select(static column => column.ColumnName); + sb.AppendLine(string.Join(",", columnNames)); + _dataTable.Columns.Add(HoldoutShuffle); + Random rnd = new(); + foreach (DataRow row in _dataTable.Rows) + { + row[HoldoutShuffle] = rnd.Next(); + } + var beforeDate = holdoutRequest.MaxDate; + var afterDate = holdoutRequest.MinDate; + var dateColumn = holdoutRequest.DateColumnName; + var hasMinDate = false; + var hasMaxDate = false; + + + if (columnNames.Contains(dateColumn)) + { + if (beforeDate.Date != DateTime.MinValue) + { + //has max date + hasMaxDate = true; + } + if (afterDate.Date != DateTime.MinValue) + { + //has min date + hasMinDate = true; + } + } + + if (hasMinDate || hasMaxDate) + { + foreach(DataRow row in _dataTable.Rows) + { + if (hasMaxDate && DateTime.Parse(row[dateColumn].ToString()) > beforeDate) { + row.Delete(); + } + else if (hasMinDate && DateTime.Parse(row[dateColumn].ToString()) < afterDate) + { + row.Delete(); + } + } + } + _dataTable.DefaultView.Sort = HoldoutShuffle; + _dataTable = _dataTable.DefaultView.ToTable(); + _dataTable.Columns.Remove(HoldoutShuffle); + var rowCount = holdoutRequest.Count; + var rows = _dataTable.Rows.Cast().Take(rowCount); + if (holdoutRequest.IsPercent) + { + if (rowCount > 100) + { + rowCount = 100; + } + rowCount = (int)Math.Ceiling((float)_dataTable.Rows.Count / 100 * rowCount); + rows = _dataTable.Rows.Cast().Take(rowCount); + } + + var dataRows = rows as DataRow[] ?? rows.ToArray(); + if (!dataRows.Any()) + { + Show("Holdout would be empty with current configuration. Will not create holdout."); + return; + } + + foreach (var row in dataRows) + { + sb.AppendLine(string.Join(",", row.ItemArray.Select(static field => field?.ToString()))); + } + + File.WriteAllText($"{holdoutRequest.Name}.csv", sb.ToString()); + var fi = new FileInfo($"{holdoutRequest.Name}.csv"); + + var columns = _dataTable.Columns.Cast().Select(c=>c.ColumnName).ToList(); + + BasicActivator.SelectObject("Select an Extraction Identifier", columns.ToArray(), out var extractionIdentifier); + if (extractionIdentifier == null) + return; + + var db = SelectDatabase(true, "Select a Database to store the new Holdout."); + if(db == null) return; + + var pipe = _activator.RepositoryLocator.CatalogueRepository.GetAllObjects().OrderByDescending(static p => p.ID) + .FirstOrDefault(static p => p.Name.Contains("BULK INSERT: CSV Import File (automated column-type detection)")); + + var importCommand = new ExecuteCommandCreateNewCatalogueByImportingFile(_activator, fi, extractionIdentifier, db, pipe, null,holdoutRequest.Description); + importCommand.Execute(); + + } + + public override Image GetImage(IIconProvider iconProvider) => iconProvider.GetImage(RDMPConcept.CohortAggregate,OverlayKind.Link); +} diff --git a/Rdmp.Core/CommandExecution/BasicActivateItems.cs b/Rdmp.Core/CommandExecution/BasicActivateItems.cs index c7917c0c79..79350d31c7 100644 --- a/Rdmp.Core/CommandExecution/BasicActivateItems.cs +++ b/Rdmp.Core/CommandExecution/BasicActivateItems.cs @@ -664,6 +664,16 @@ public virtual CohortCreationRequest GetCohortCreationRequest(ExternalCohortTabl RepositoryLocator.DataExportRepository, cohortInitialDescription); } + /// + public virtual CohortHoldoutLookupRequest GetCohortHoldoutLookupRequest(ExternalCohortTable externalCohortTable, IProject project, CohortIdentificationConfiguration cic) + { + + if (!TypeText("Name", "Enter name for cohort", 255, null, out var name, false)) + throw new Exception("User chose not to enter a name for the cohort and none was provided"); + + return new CohortHoldoutLookupRequest(cic, "empty", 1,false,"",""); + } + /// public virtual ICatalogue CreateAndConfigureCatalogue(ITableInfo tableInfo, ColumnInfo[] extractionIdentifierColumns, string initialDescription, IProject projectSpecific, diff --git a/Rdmp.Core/CommandExecution/IBasicActivateItems.cs b/Rdmp.Core/CommandExecution/IBasicActivateItems.cs index 08151a4cf8..3c7db33354 100644 --- a/Rdmp.Core/CommandExecution/IBasicActivateItems.cs +++ b/Rdmp.Core/CommandExecution/IBasicActivateItems.cs @@ -173,6 +173,10 @@ public interface IBasicActivateItems CohortCreationRequest GetCohortCreationRequest(ExternalCohortTable externalCohortTable, IProject project, string cohortInitialDescription); + + + CohortHoldoutLookupRequest GetCohortHoldoutLookupRequest(ExternalCohortTable externalCohortTable, IProject project, CohortIdentificationConfiguration cic); + /// /// Show all objects in RDMP (with search). If a single selection is made then invoke the callback /// diff --git a/Rdmp.Core/QueryBuilding/Options/AggregateBuilderCohortOptions.cs b/Rdmp.Core/QueryBuilding/Options/AggregateBuilderCohortOptions.cs index c041e4abcb..95ea58cdb1 100644 --- a/Rdmp.Core/QueryBuilding/Options/AggregateBuilderCohortOptions.cs +++ b/Rdmp.Core/QueryBuilding/Options/AggregateBuilderCohortOptions.cs @@ -83,7 +83,7 @@ public bool ShouldBeEnabled(AggregateEditorSection section, AggregateConfigurati return section switch { AggregateEditorSection.Extractable => false, - AggregateEditorSection.TOPX => false, + AggregateEditorSection.TOPX => true, AggregateEditorSection.PIVOT => false, AggregateEditorSection.AXIS => false, _ => throw new ArgumentOutOfRangeException(nameof(section)) diff --git a/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.Designer.cs b/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.Designer.cs new file mode 100644 index 0000000000..65cf84bc9a --- /dev/null +++ b/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.Designer.cs @@ -0,0 +1,385 @@ +using Org.BouncyCastle.Asn1.Crmf; +using Rdmp.UI.ChecksUI; +using Rdmp.UI.SimpleControls; +using ScintillaNET; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace Rdmp.UI.CohortUI.CreateHoldoutLookup; + +partial class CreateHoldoutLookupUI +{ + /// + /// 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() + { + gbNewCohort = new GroupBox(); + label7 = new Label(); + tbName = new TextBox(); + gbChooseCohortType = new GroupBox(); + label4 = new Label(); + textBox4 = new TextBox(); + label3 = new Label(); + textBox3 = new TextBox(); + label2 = new Label(); + textBox2 = new TextBox(); + label1 = new Label(); + comboBox1 = new ComboBox(); + numericUpDown1 = new NumericUpDown(); + groupBox3 = new GroupBox(); + label9 = new Label(); + tbDescription = new TextBox(); + btnClearProject = new Button(); + btnOk = new Button(); + ragSmiley1 = new RAGSmiley(); + gbDescription = new GroupBox(); + taskDescriptionLabel1 = new SimpleDialogs.TaskDescriptionLabel(); + panel1 = new Panel(); + panel2 = new Panel(); + gbNewCohort.SuspendLayout(); + gbChooseCohortType.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)numericUpDown1).BeginInit(); + groupBox3.SuspendLayout(); + gbDescription.SuspendLayout(); + panel1.SuspendLayout(); + panel2.SuspendLayout(); + SuspendLayout(); + // + // gbNewCohort + // + gbNewCohort.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + gbNewCohort.Controls.Add(label7); + gbNewCohort.Controls.Add(tbName); + gbNewCohort.Location = new System.Drawing.Point(13, 24); + gbNewCohort.Margin = new Padding(4, 3, 4, 3); + gbNewCohort.Name = "gbNewCohort"; + gbNewCohort.Padding = new Padding(4, 3, 4, 3); + gbNewCohort.Size = new System.Drawing.Size(1005, 54); + gbNewCohort.TabIndex = 0; + gbNewCohort.TabStop = false; + gbNewCohort.Text = "Holdout Cohort Name"; + // + // label7 + // + label7.AutoSize = true; + label7.Location = new System.Drawing.Point(7, 25); + label7.Margin = new Padding(4, 0, 4, 0); + label7.Name = "label7"; + label7.Size = new System.Drawing.Size(42, 15); + label7.TabIndex = 0; + label7.Text = "Name:"; + // + // tbName + // + tbName.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + tbName.Location = new System.Drawing.Point(58, 22); + tbName.Margin = new Padding(4, 3, 4, 3); + tbName.Name = "tbName"; + tbName.Size = new System.Drawing.Size(939, 23); + tbName.TabIndex = 1; + tbName.TextChanged += tbName_TextChanged; + // + // gbChooseCohortType + // + gbChooseCohortType.Controls.Add(label4); + gbChooseCohortType.Controls.Add(textBox4); + gbChooseCohortType.Controls.Add(label3); + gbChooseCohortType.Controls.Add(textBox3); + gbChooseCohortType.Controls.Add(label2); + gbChooseCohortType.Controls.Add(textBox2); + gbChooseCohortType.Controls.Add(label1); + gbChooseCohortType.Controls.Add(comboBox1); + gbChooseCohortType.Controls.Add(numericUpDown1); + gbChooseCohortType.Dock = DockStyle.Top; + gbChooseCohortType.Location = new System.Drawing.Point(0, 0); + gbChooseCohortType.Margin = new Padding(4, 3, 4, 3); + gbChooseCohortType.Name = "gbChooseCohortType"; + gbChooseCohortType.Padding = new Padding(4, 3, 4, 3); + gbChooseCohortType.Size = new System.Drawing.Size(1048, 107); + gbChooseCohortType.TabIndex = 9; + gbChooseCohortType.TabStop = false; + gbChooseCohortType.Text = "1. Define Holdout settings"; + gbChooseCohortType.Enter += gbChooseCohortType_Enter; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(444, 69); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(77, 15); + label4.TabIndex = 11; + label4.Text = "Date Column"; + label4.Click += label4_Click; + // + // textBox4 + // + textBox4.Location = new System.Drawing.Point(527, 66); + textBox4.Name = "textBox4"; + textBox4.Size = new System.Drawing.Size(100, 23); + textBox4.TabIndex = 10; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(229, 61); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(91, 30); + label3.TabIndex = 9; + label3.Text = "Max Date\r\n(DD/MM/YYYY)"; + label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + label3.Click += label3_Click; + // + // textBox3 + // + textBox3.Location = new System.Drawing.Point(326, 66); + textBox3.Name = "textBox3"; + textBox3.Size = new System.Drawing.Size(100, 23); + textBox3.TabIndex = 8; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(16, 61); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(91, 30); + label2.TabIndex = 7; + label2.Text = "Min Date\r\n(DD/MM/YYYY)"; + label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + label2.Click += label2_Click; + // + // textBox2 + // + textBox2.Location = new System.Drawing.Point(113, 66); + textBox2.Name = "textBox2"; + textBox2.Size = new System.Drawing.Size(100, 23); + textBox2.TabIndex = 6; + textBox2.TextChanged += textBox2_TextChanged; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(171, 20); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(110, 15); + label1.TabIndex = 5; + label1.Text = "of people in Cohort"; + label1.Click += label1_Click; + // + // comboBox1 + // + comboBox1.AllowDrop = true; + comboBox1.FormattingEnabled = true; + comboBox1.Items.AddRange(new object[] { "%", "#" }); + comboBox1.Location = new System.Drawing.Point(122, 17); + comboBox1.Name = "comboBox1"; + comboBox1.Size = new System.Drawing.Size(43, 23); + comboBox1.TabIndex = 4; + // + // numericUpDown1 + // + numericUpDown1.Location = new System.Drawing.Point(20, 18); + numericUpDown1.Name = "numericUpDown1"; + numericUpDown1.Size = new System.Drawing.Size(87, 23); + numericUpDown1.TabIndex = 3; + // + // groupBox3 + // + groupBox3.Controls.Add(gbNewCohort); + groupBox3.Dock = DockStyle.Top; + groupBox3.Location = new System.Drawing.Point(0, 107); + groupBox3.Margin = new Padding(4, 3, 4, 3); + groupBox3.Name = "groupBox3"; + groupBox3.Padding = new Padding(4, 3, 4, 3); + groupBox3.Size = new System.Drawing.Size(1048, 90); + groupBox3.TabIndex = 10; + groupBox3.TabStop = false; + groupBox3.Text = "2. Configure Cohort (doesn't exist yet, next screen will actually create it)"; + // + // label9 + // + label9.AutoSize = true; + label9.Location = new System.Drawing.Point(7, 24); + label9.Margin = new Padding(4, 0, 4, 0); + label9.Name = "label9"; + label9.Size = new System.Drawing.Size(64, 15); + label9.TabIndex = 2; + label9.Text = "Comment:"; + // + // tbDescription + // + tbDescription.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + tbDescription.Location = new System.Drawing.Point(71, 24); + tbDescription.Margin = new Padding(4, 3, 4, 3); + tbDescription.Multiline = true; + tbDescription.Name = "tbDescription"; + tbDescription.Size = new System.Drawing.Size(969, 67); + tbDescription.TabIndex = 3; + tbDescription.TextChanged += tbDescription_TextChanged; + // + // btnClearProject + // + btnClearProject.Anchor = AnchorStyles.Top; + btnClearProject.Location = new System.Drawing.Point(528, 7); + btnClearProject.Margin = new Padding(4, 3, 4, 3); + btnClearProject.Name = "btnClearProject"; + btnClearProject.Size = new System.Drawing.Size(141, 27); + btnClearProject.TabIndex = 14; + btnClearProject.Text = "Cancel"; + btnClearProject.UseVisualStyleBackColor = true; + btnClearProject.Click += btnCancel_Click; + // + // btnOk + // + btnOk.Anchor = AnchorStyles.Top; + btnOk.Location = new System.Drawing.Point(372, 7); + btnOk.Margin = new Padding(4, 3, 4, 3); + btnOk.Name = "btnOk"; + btnOk.Size = new System.Drawing.Size(149, 27); + btnOk.TabIndex = 13; + btnOk.Text = "Ok"; + btnOk.UseVisualStyleBackColor = true; + btnOk.Click += btnOk_Click; + // + // ragSmiley1 + // + ragSmiley1.AlwaysShowHandCursor = false; + ragSmiley1.Anchor = AnchorStyles.Top; + ragSmiley1.BackColor = System.Drawing.Color.Transparent; + ragSmiley1.Location = new System.Drawing.Point(335, 4); + ragSmiley1.Margin = new Padding(5, 3, 5, 3); + ragSmiley1.Name = "ragSmiley1"; + ragSmiley1.Size = new System.Drawing.Size(30, 30); + ragSmiley1.TabIndex = 12; + // + // gbDescription + // + gbDescription.Controls.Add(label9); + gbDescription.Controls.Add(tbDescription); + gbDescription.Dock = DockStyle.Top; + gbDescription.Location = new System.Drawing.Point(0, 197); + gbDescription.Margin = new Padding(4, 3, 4, 3); + gbDescription.Name = "gbDescription"; + gbDescription.Padding = new Padding(4, 3, 4, 3); + gbDescription.Size = new System.Drawing.Size(1048, 99); + gbDescription.TabIndex = 11; + gbDescription.TabStop = false; + gbDescription.Text = "3. Enter Description Of Holdout"; + // + // taskDescriptionLabel1 + // + taskDescriptionLabel1.AutoSize = true; + taskDescriptionLabel1.AutoSizeMode = AutoSizeMode.GrowAndShrink; + taskDescriptionLabel1.Dock = DockStyle.Top; + taskDescriptionLabel1.Location = new System.Drawing.Point(0, 0); + taskDescriptionLabel1.Name = "taskDescriptionLabel1"; + taskDescriptionLabel1.Size = new System.Drawing.Size(1048, 42); + taskDescriptionLabel1.TabIndex = 19; + // + // panel1 + // + panel1.Controls.Add(panel2); + panel1.Controls.Add(gbDescription); + panel1.Controls.Add(groupBox3); + panel1.Controls.Add(gbChooseCohortType); + panel1.Dock = DockStyle.Fill; + panel1.Location = new System.Drawing.Point(0, 42); + panel1.Name = "panel1"; + panel1.Size = new System.Drawing.Size(1048, 509); + panel1.TabIndex = 20; + // + // panel2 + // + panel2.Controls.Add(btnOk); + panel2.Controls.Add(ragSmiley1); + panel2.Controls.Add(btnClearProject); + panel2.Dock = DockStyle.Top; + panel2.Location = new System.Drawing.Point(0, 296); + panel2.Name = "panel2"; + panel2.Size = new System.Drawing.Size(1048, 55); + panel2.TabIndex = 20; + // + // CreateHoldoutLookupUI + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(1048, 551); + Controls.Add(panel1); + Controls.Add(taskDescriptionLabel1); + Margin = new Padding(4, 3, 4, 3); + Name = "CreateHoldoutLookupUI"; + StartPosition = FormStartPosition.CenterScreen; + Text = "Create Cohort"; + Load += CohortHoldoutCreationRequestUI_Load; + gbNewCohort.ResumeLayout(false); + gbNewCohort.PerformLayout(); + gbChooseCohortType.ResumeLayout(false); + gbChooseCohortType.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)numericUpDown1).EndInit(); + groupBox3.ResumeLayout(false); + gbDescription.ResumeLayout(false); + gbDescription.PerformLayout(); + panel1.ResumeLayout(false); + panel2.ResumeLayout(false); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + //private System.Windows.Forms.Label label3; + //private System.Windows.Forms.Label lblExternalCohortTable; + private GroupBox gbNewCohort; + private Label label7; + private TextBox tbName; + //private System.Windows.Forms.Button btnNewProject; + private GroupBox gbChooseCohortType; + private GroupBox groupBox3; + private Label label9; + private TextBox tbDescription; + //private System.Windows.Forms.Label lblProject; + //private System.Windows.Forms.Label label11; + private Button btnClearProject; + private Button btnOk; + private RAGSmiley ragSmiley1; + private GroupBox gbDescription; + //private System.Windows.Forms.PictureBox pbProject; + //private System.Windows.Forms.PictureBox pbCohortSource; + //private System.Windows.Forms.Button btnExisting; + //private System.Windows.Forms.Label lblErrorNoProjectNumber; + //private System.Windows.Forms.TextBox tbSetProjectNumber; + //private System.Windows.Forms.Button btnClear; + private SimpleDialogs.TaskDescriptionLabel taskDescriptionLabel1; + private Panel panel1; + private Panel panel2; + //private System.Windows.Forms.CheckBox checkBox1; + private Label label1; + private ComboBox comboBox1; + private NumericUpDown numericUpDown1; + private Label label2; + private TextBox textBox2; + private Label label3; + private TextBox textBox3; + private Label label4; + private TextBox textBox4; +} diff --git a/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.cs b/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.cs new file mode 100644 index 0000000000..c9bccf1c81 --- /dev/null +++ b/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.cs @@ -0,0 +1,133 @@ +// 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 System; +using System.Windows.Forms; +using Rdmp.Core.CohortCommitting.Pipeline; +using Rdmp.Core.CommandExecution; +using Rdmp.Core.Curation.Data.Cohort; +using Rdmp.Core.DataExport.Data; +using Rdmp.UI.ItemActivation; +using Rdmp.UI.TestsAndSetup.ServicePropogation; + +namespace Rdmp.UI.CohortUI.CreateHoldoutLookup; + +/// +/// Once you have created a cohort for your holdout, this dialog allows you to configure how many and from when to store +/// a catalogue for use as exclusion fields in other cohorts +/// +public partial class CreateHoldoutLookupUI : RDMPForm +{ + private readonly IExternalCohortTable _target; + private readonly CohortIdentificationConfiguration _cic; + + + public string CohortDescription + { + get => tbDescription.Text; + set => tbDescription.Text = value; + } + + + public CreateHoldoutLookupUI(IActivateItems activator, IExternalCohortTable target, CohortIdentificationConfiguration cic = null) : + base(activator) + { + _target = target; + + InitializeComponent(); + + if (_target == null) + return; + + _cic = cic; + tbName.Text = $"holdout_{cic?.Name ?? ""}"; + + taskDescriptionLabel1.SetupFor(new DialogArgs + { + TaskDescription = + "For Details, see: https://github.com/HicServices/RDMP/blob/0b4eea78a99e58d75ae5189ec491f5ef54b52e1e/Rdmp.UI/CohortUI/CreateHoldoutLookup/Holdouts.md" + }); + } + + public CohortHoldoutLookupRequest Result { get; set; } + + private void btnOk_Click(object sender, EventArgs e) + { + var name = tbName.Text; + var minDate = textBox2.Text; + var maxDate = textBox3.Text; + var dateColumnName = textBox4.Text; + var description = tbDescription.Text; + Result = new CohortHoldoutLookupRequest(_cic, name, decimal.ToInt32(numericUpDown1.Value), + comboBox1.Text == "%", description, minDate, maxDate, dateColumnName); + DialogResult = DialogResult.OK; + Close(); + } + private void btnCancel_Click(object sender, EventArgs e) + { + Result = null; + DialogResult = DialogResult.Cancel; + Close(); + } + + private void CohortHoldoutCreationRequestUI_Load(object sender, EventArgs e) + { + _target.Check(ragSmiley1); + } + + private void tbName_TextChanged(object sender, EventArgs e) + { + } + + private void btnClear_Click(object sender, EventArgs e) + { + } + + + private void gbChooseCohortType_Enter(object sender, EventArgs e) + { + + + } + + + private void label1_Click(object sender, EventArgs e) + { + + + } + + private void checkBox1_CheckedChanged(object sender, EventArgs e) + { + + } + + private void label2_Click(object sender, EventArgs e) + { + + } + + private void textBox2_TextChanged(object sender, EventArgs e) + { + + } + + private void label3_Click(object sender, EventArgs e) + { + + } + + private void label4_Click(object sender, EventArgs e) + { + + } + + private void tbDescription_TextChanged(object sender, EventArgs e) + { + + } +} diff --git a/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.resx b/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.resx new file mode 100644 index 0000000000..af32865ec1 --- /dev/null +++ b/Rdmp.UI/CohortUI/CreateHoldoutLookup/CreateHoldoutLookupUI.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/CohortUI/CreateHoldoutLookup/Holdouts.md b/Rdmp.UI/CohortUI/CreateHoldoutLookup/Holdouts.md new file mode 100644 index 0000000000..6e3654ed3a --- /dev/null +++ b/Rdmp.UI/CohortUI/CreateHoldoutLookup/Holdouts.md @@ -0,0 +1,16 @@ +# Holdout Cohorts in RDMP +Holdout cohorts are groups of people that we want to exclude from extractions. This could be for a variety of reasons, notably as holdout data for ML model validation. +## How do I create a holdout? +1. Decide what you want to holdout + + This May be a certain number of people with a certain condition, or a complex distribution of attributes. +2. Create a Cohort that describes these people + + You may need to create multiple cohorts depending on how complex your holdout criteria is. + N.B. you can limit cohort sizes by editing the cohort aggregate filter to limit the count +3. Right click on the cohort and create a holdout from them + This gives you a number of options to limit the holdout based on counts and dates. The cohort is shuffled before the holdout is selected. +4. This creates a catalogue containing the extraction identifiers for the holdout +5. Add the holdout catalogue(s) as exception clauses to your main extraction cohort to exclude them from the extraction + + \ No newline at end of file