/// <summary> /// Creates a entirely new copy of the <see cref="CohortIdentificationConfiguration"/> with all new IDs on the root and all child objects. This includes /// filters, patient index tables, parameters, set containers etc. /// <para>This is done in a transaction so that if it fails halfway through you won't end up with half a clone configuration</para> /// </summary> /// <param name="notifier">Event listener for reporting cloning progress and any problems</param> /// <returns></returns> public CohortIdentificationConfiguration CreateClone(ICheckNotifier notifier) { //todo this would be nice if it was ICatalogueRepository but transaction is super SQLy var cataRepo = ((CatalogueRepository)Repository); //start a new super transaction using (cataRepo.BeginNewTransactedConnection()) { try { notifier.OnCheckPerformed(new CheckEventArgs("Super Transaction started on Catalogue Repository", CheckResult.Success)); var clone = new CohortIdentificationConfiguration(cataRepo, Name + " (Clone)"); notifier.OnCheckPerformed(new CheckEventArgs("Created clone configuration '" + clone.Name + "' with ID " + clone.ID + " called " + clone, CheckResult.Success)); //clone the global parameters foreach (var p in GetAllParameters()) { notifier.OnCheckPerformed(new CheckEventArgs("Cloning global parameter " + p.ParameterName, CheckResult.Success)); var cloneP = new AnyTableSqlParameter(cataRepo, clone, p.ParameterSQL); cloneP.Comment = p.Comment; cloneP.Value = p.Value; cloneP.SaveToDatabase(); } //key is the original, value is the clone var parentToCloneJoinablesDictionary = new Dictionary <JoinableCohortAggregateConfiguration, JoinableCohortAggregateConfiguration>(); //clone the joinables foreach (var joinable in GetAllJoinables()) { //clone the aggregate which has permission to be joinable var cloneJoinableAggregate = joinable.AggregateConfiguration.CreateClone(); //clone the join permission var cloneJoinable = new JoinableCohortAggregateConfiguration(cataRepo, clone, cloneJoinableAggregate); parentToCloneJoinablesDictionary.Add(joinable, cloneJoinable); } clone.ClonedFrom_ID = ID; clone.RootCohortAggregateContainer_ID = RootCohortAggregateContainer.CloneEntireTreeRecursively(notifier, this, clone, parentToCloneJoinablesDictionary).ID; clone.SaveToDatabase(); notifier.OnCheckPerformed(new CheckEventArgs("Clone creation successful, about to commit Super Transaction", CheckResult.Success)); cataRepo.EndTransactedConnection(true); notifier.OnCheckPerformed(new CheckEventArgs("Super Transaction committed successfully", CheckResult.Success)); return(clone); } catch (Exception e) { cataRepo.EndTransactedConnection(false); notifier.OnCheckPerformed(new CheckEventArgs("Cloning failed, See Exception for details, the Super Transaction was rolled back successfully though", CheckResult.Fail, e)); } } return(null); }
private void EnsureNamingConvention(CohortIdentificationConfiguration cic, AggregateConfiguration ac) { //clear any old cic_x prefixes ac.Name = Regex.Replace(ac.Name, $@"^({CohortIdentificationConfiguration.CICPrefix }\d+_?)+", ""); ac.SaveToDatabase(); //and add the new correct one cic.EnsureNamingConvention(ac); }
/// <summary> /// Clone and import one or more <see cref="CohortIdentificationConfiguration"/> into the target <paramref name="into"/> /// </summary> /// <param name="cics"></param> /// <param name="into">The container into which you want to add the <paramref name="cics"/></param> 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); } }
/// <summary> /// Creates a new CohortAggregateContainer tree containing a clone container for each container in the original tree and a clone AggregateConfiguration for each in the original tree /// but with a rename in which AggregateConfigurations in the first tree are expected to start cic_X where X is the original cohort identification configuration ID, this will be replaced /// with the new clone's ID /// </summary> /// <param name="notifier"></param> /// <param name="original"></param> /// <param name="clone"></param> /// <param name="parentToCloneJoinablesDictionary"></param> public CohortAggregateContainer CloneEntireTreeRecursively(ICheckNotifier notifier, CohortIdentificationConfiguration original, CohortIdentificationConfiguration clone, Dictionary <JoinableCohortAggregateConfiguration, JoinableCohortAggregateConfiguration> parentToCloneJoinablesDictionary) { //what is in us? var contents = GetOrderedContents(); //clone us with same order (in parents) var cloneContainer = new CohortAggregateContainer((ICatalogueRepository)Repository, Operation); cloneContainer.Name = Name; cloneContainer.Order = Order; cloneContainer.SaveToDatabase(); //for each thing in us foreach (IOrderable content in contents) { int order = content.Order; var config = content as AggregateConfiguration; var container = content as CohortAggregateContainer; //its a config, clone the config and add it to the clone container if (config != null) { var configClone = clone.ImportAggregateConfigurationAsIdentifierList(config, null, false); notifier.OnCheckPerformed(new CheckEventArgs("Created clone dataset " + configClone + " with ID " + configClone.ID, CheckResult.Success)); cloneContainer.AddChild(configClone, order); //if the original used any joinable patient index tables var usedJoins = config.PatientIndexJoinablesUsed; //our dictionary should have a record of it along with a clone patient index table we should hook our clone up to foreach (var j in usedJoins) { //for some reason the CohortIdentificationConfiguration didn't properly clone the joinable permission or didn't add it to the dictionary if (!parentToCloneJoinablesDictionary.ContainsKey(j.JoinableCohortAggregateConfiguration)) { throw new KeyNotFoundException("Configuration " + configClone + " uses Patient Index Table " + j.AggregateConfiguration + " but our dictionary did not have the key, why was that joinable not cloned?"); } //we do have a clone copy of the joinable permission, set the clone aggregate var cloneJoinable = parentToCloneJoinablesDictionary[j.JoinableCohortAggregateConfiguration]; var cloneJoinUse = cloneJoinable.AddUser(configClone); cloneJoinUse.JoinType = j.JoinType; cloneJoinUse.SaveToDatabase(); //Now! (brace yourself). Some the filters in the AggregateConfiguration we just cloned might reference a table called ix2934 or whetever, this //is the Joinable we need to do a replace to point them at the correct ix number (although if they are good users they will have aliased any //patient index columns anyway) if (configClone.RootFilterContainer_ID != null) { foreach (var clonedFilter in SqlQueryBuilderHelper.GetAllFiltersUsedInContainerTreeRecursively(configClone.RootFilterContainer)) { var oldTableAlias = j.GetJoinTableAlias(); var newTableAlias = cloneJoinUse.GetJoinTableAlias(); clonedFilter.WhereSQL = clonedFilter.WhereSQL.Replace(oldTableAlias, newTableAlias); clonedFilter.SaveToDatabase(); } } } } //its another container (a subcontainer), recursively call the clone operation on it and add that subtree to teh clone container if (container != null) { var cloneSubContainer = container.CloneEntireTreeRecursively(notifier, original, clone, parentToCloneJoinablesDictionary); notifier.OnCheckPerformed(new CheckEventArgs("Created clone container " + cloneSubContainer + " with ID " + cloneSubContainer.ID, CheckResult.Success)); cloneContainer.AddChild(cloneSubContainer); } } //return the clone we created return(cloneContainer); }
/// <summary> /// Clones and combines two or more <see cref="CohortIdentificationConfiguration"/> into a single new cic. /// </summary> /// <param name="cics"></param> /// <param name="operation"></param> /// <returns>The new merged CohortIdentificationConfiguration which contains all the provided <paramref name="cics"/> </returns> public CohortIdentificationConfiguration Merge(CohortIdentificationConfiguration[] cics, SetOperation operation) { if (cics.Length <= 1) { throw new ArgumentException("You must select at least 2 cics to merge", nameof(cics)); } //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 merge cloning stage, no merge will be attempted", ex); } using (_repository.BeginNewTransactedConnection()) { // Create a new master configuration var cicMaster = new CohortIdentificationConfiguration(_repository, $"Merged cics (IDs {string.Join(",",cics.Select(c=>c.ID))})"); // With a single top level container with the provided operation cicMaster.CreateRootContainerIfNotExists(); var rootContainer = cicMaster.RootCohortAggregateContainer; rootContainer.Operation = operation; rootContainer.SaveToDatabase(); //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 to the new master cic root container rootContainer.AddChild(container); // Make the new name of all the AggregateConfigurations match the new master cic foreach (var child in container.GetAllAggregateConfigurationsRecursively()) { EnsureNamingConvention(cicMaster, child); } // Delete the old now empty clones cic.DeleteInDatabase(); } //finish transaction _repository.EndTransactedConnection(true); return(cicMaster); } }
/// <summary> /// Splits the root container of a <see cref="CohortIdentificationConfiguration"/> into multiple new cic. /// </summary> /// <param name="rootContainer"></param> /// <returns>All new configurations unmerged out of the <paramref name="rootContainer"/></returns> public CohortIdentificationConfiguration[] UnMerge(CohortAggregateContainer rootContainer) { if (!rootContainer.IsRootContainer()) { throw new ArgumentException("Container must be a root container to be unmerged", nameof(rootContainer)); } if (rootContainer.GetAggregateConfigurations().Any()) { throw new ArgumentException("Container must contain only sub-containers (i.e. no aggregates)", nameof(rootContainer)); } if (rootContainer.GetSubContainers().Length <= 1) { throw new ArgumentException("Container must contain 2+ sub-containers to be unmerged", nameof(rootContainer)); } var cic = rootContainer.GetCohortIdentificationConfiguration(); var toReturn = new List <CohortIdentificationConfiguration>(); try { // clone the input cic cic = cic.CreateClone(new ThrowImmediatelyCheckNotifier()); // grab the new clone root container rootContainer = cic.RootCohortAggregateContainer; } catch (Exception ex) { throw new Exception("Error during pre merge cloning stage, no UnMerge will be attempted", ex); } using (_repository.BeginNewTransactedConnection()) { // For each of these foreach (var subContainer in rootContainer.GetSubContainers().OrderBy(c => c.Order)) { // create a new config var newCic = new CohortIdentificationConfiguration(_repository, $"Un Merged { subContainer.Name } ({subContainer.ID }) "); //take the container we are splitting out subContainer.MakeIntoAnOrphan(); //make it the root container of the new cic newCic.RootCohortAggregateContainer_ID = subContainer.ID; newCic.SaveToDatabase(); // Make the new name of all the AggregateConfigurations match the new cic foreach (var child in subContainer.GetAllAggregateConfigurationsRecursively()) { EnsureNamingConvention(newCic, child); } toReturn.Add(newCic); } //Now delete the original clone that we unmerged the containers out of cic.DeleteInDatabase(); //finish transaction _repository.EndTransactedConnection(true); } return(toReturn.ToArray()); }