Skip to content

Commit

Permalink
Fixes #2761 change transport direction (#2818)
Browse files Browse the repository at this point in the history
* Fixes #2761 change transport direction

* Update after code review
  • Loading branch information
msevestre committed Oct 24, 2023
1 parent 11ac4fa commit c6157b3
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 15 deletions.
33 changes: 28 additions & 5 deletions src/PKSim.Core/Services/SimulationBuildingBlockUpdater.cs
Expand Up @@ -42,8 +42,10 @@ public interface ISimulationBuildingBlockUpdater
/// Returns false if a simple parameter value update cannot be performed (i.e. structural change was made)
/// </summary>
/// <param name="templateBuildingBlock">Template building block as defined in repository</param>
/// <param name="usedBuildingBlock">Used building block (the one based on the template building block at a given time)</param>
/// <returns></returns>
/// <param name="usedBuildingBlock">
/// Used building block (the one based on the template building block at a given time). It
/// it assumed that the object is loaded (e.g. reference to building block can be used
/// </param>
bool QuickUpdatePossibleFor(IPKSimBuildingBlock templateBuildingBlock, UsedBuildingBlock usedBuildingBlock);

/// <summary>
Expand All @@ -61,7 +63,7 @@ public interface ISimulationBuildingBlockUpdater
bool BuildingBlockSupportsQuickUpdate(IPKSimBuildingBlock templateBuildingBlock);

/// <summary>
/// Returns whether a building block comparison is available for the building block
/// Returns whether a building block comparison is available for the building block
/// </summary>
bool BuildingBlockSupportComparison(IPKSimBuildingBlock templateBuildingBlock);
}
Expand Down Expand Up @@ -140,7 +142,28 @@ public bool QuickUpdatePossibleFor(IPKSimBuildingBlock templateBuildingBlock, Us
if (!BuildingBlockSupportsQuickUpdate(templateBuildingBlock))
return false;

return templateBuildingBlock.StructureVersion == usedBuildingBlock.StructureVersion;
//not the same structure, easy return
var sameStructureVersion = templateBuildingBlock.StructureVersion == usedBuildingBlock.StructureVersion;
if (!sameStructureVersion)
return false;

//For individual, there is a special handling required as we need to also check that the used expression profile have the same structure version
if (templateBuildingBlock is not Individual individualTemplate)
return true;

//It has to be available by construction
var usedIndividual = usedBuildingBlock.BuildingBlock as Individual;
//but we return false just in case :)
if (usedIndividual == null)
return false;

//let's compare the expression profile in each individuals and see if they are comparable
return individualTemplate.AllExpressionProfiles().All(x =>
{
//assume we can find it by name. Otherwise => structural change
var usedExpressionProfile = usedIndividual.AllExpressionProfiles().FindByName(x.Name);
return usedExpressionProfile != null && x.StructureVersion == usedExpressionProfile.StructureVersion;
});
}

public void UpdateProtocolsInSimulation(Simulation simulation)
Expand All @@ -151,7 +174,7 @@ public void UpdateProtocolsInSimulation(Simulation simulation)
UpdateMultipleUsedBuildingBlockInSimulationFromTemplate(simulation, protocolProperties.Select(x => x.Protocol), PKSimBuildingBlockType.Protocol);

var allSimulationProtocols = simulation.AllBuildingBlocks<Protocol>().ToList();
//update selected protocol with references in simulation instead of templats
//update selected protocol with references in simulation instead of templates
protocolProperties.Each(x => x.Protocol = allSimulationProtocols.FindByName(x.Protocol.Name));
}

Expand Down
10 changes: 8 additions & 2 deletions src/PKSim.Presentation/Services/SimulationTask.cs
Expand Up @@ -95,8 +95,11 @@ public override Simulation AddToProject()
_buildingBlockTask.Load(templateBuildingBlock);
_buildingBlockTask.Load(simulation);

//Now that the simulation is loaded, we want to make sure that the captured ref to used building block also has a valid reference to the underlying building block
var loadedUsedBuildingBlock = simulation.UsedBuildingBlockByTemplateId(usedBuildingBlock.TemplateId);

//check if quick update possible. if yes =>perform quick update
if (_simulationBuildingBlockUpdater.QuickUpdatePossibleFor(templateBuildingBlock, usedBuildingBlock))
if (_simulationBuildingBlockUpdater.QuickUpdatePossibleFor(templateBuildingBlock, loadedUsedBuildingBlock))
{
var updateCommand = _blockParametersToSimulationUpdater.UpdateParametersFromBuildingBlockInSimulation(templateBuildingBlock, simulation);
_buildingBlockTask.AddCommandToHistory(updateCommand);
Expand All @@ -115,8 +118,11 @@ public override Simulation AddToProject()
_buildingBlockTask.Load(templateBuildingBlock);
_buildingBlockTask.Load(simulation);

//Now that the simulation is loaded, we want to make sure that the captured ref to used building block also has a valid reference to the underlying building block
var loadedUsedBuildingBlock = simulation.UsedBuildingBlockByTemplateId(usedBuildingBlock.TemplateId);

//check if quick update possible. if yes =>perform quick update
if (_simulationBuildingBlockUpdater.QuickUpdatePossibleFor(templateBuildingBlock, usedBuildingBlock))
if (_simulationBuildingBlockUpdater.QuickUpdatePossibleFor(templateBuildingBlock, loadedUsedBuildingBlock))
{
var updateCommand = _simulationParametersToBlockUpdater.UpdateParametersFromSimulationInBuildingBlock(simulation, templateBuildingBlock);
_buildingBlockTask.AddCommandToHistory(updateCommand);
Expand Down
102 changes: 95 additions & 7 deletions tests/PKSim.Tests/Core/SimulationBuildingBlockUpdaterSpecs.cs
@@ -1,10 +1,10 @@
using FakeItEasy;
using OSPSuite.BDDHelper;
using OSPSuite.BDDHelper.Extensions;
using FakeItEasy;
using OSPSuite.Core.Domain;
using PKSim.Core.Mappers;
using PKSim.Core.Model;
using PKSim.Core.Services;
using OSPSuite.Core.Domain;

namespace PKSim.Core
{
Expand Down Expand Up @@ -133,6 +133,94 @@ public void should_return_false_if_the_building_block_being_updated_is_a_populat
}
}

public abstract class concern_for_SimulationBuildingBlockUpdaterForIndividual : concern_for_SimulationBuildingBlockUpdater
{
protected Individual _templateIndividual;
protected UsedBuildingBlock _usedIndividual;
protected ExpressionProfile _templateExpression;
protected Individual _individual;

protected override void Context()
{
base.Context();
_templateIndividual = new Individual
{
Id = "templateId",
StructureVersion = 1
};

_templateExpression = DomainHelperForSpecs.CreateExpressionProfile<IndividualEnzyme>("Human", "CYP");
_templateExpression.Id = "templateExpressionProfile";
_templateExpression.StructureVersion = 5;

_templateIndividual.AddExpressionProfile(_templateExpression);

_individual = new Individual
{
StructureVersion = _templateIndividual.StructureVersion,
};


_usedIndividual = new UsedBuildingBlock(_templateIndividual.Id, PKSimBuildingBlockType.Individual)
{
StructureVersion = _templateIndividual.StructureVersion,
BuildingBlock = _individual
};
}
}

public class When_asked_if_a_quick_update_possible_between_an_individual_an_a_used_individual_with_expression_profile_presenting_a_structural_change : concern_for_SimulationBuildingBlockUpdaterForIndividual
{
protected override void Context()
{
base.Context();
var expression = DomainHelperForSpecs.CreateExpressionProfile<IndividualEnzyme>("Human", "CYP");
expression.StructureVersion = _templateExpression.StructureVersion + 1;
_individual.AddExpressionProfile(expression);
}

[Observation]
public void should_not_allow_for_a_quick_update()
{
sut.QuickUpdatePossibleFor(_templateIndividual, _usedIndividual).ShouldBeFalse();
}
}

public class When_asked_if_a_quick_update_possible_between_an_individual_an_a_used_individual_with_expression_profile_presenting_no_structural_change : concern_for_SimulationBuildingBlockUpdaterForIndividual
{
protected override void Context()
{
base.Context();
var expression = DomainHelperForSpecs.CreateExpressionProfile<IndividualEnzyme>("Human", "CYP");
expression.StructureVersion = _templateExpression.StructureVersion;
_individual.AddExpressionProfile(expression);
}

[Observation]
public void should_allow_for_a_quick_update()
{
sut.QuickUpdatePossibleFor(_templateIndividual, _usedIndividual).ShouldBeTrue();
}
}

public class When_asked_if_a_quick_update_possible_between_an_individual_an_a_used_individual_with_expression_profile_missing_by_name : concern_for_SimulationBuildingBlockUpdaterForIndividual
{
protected override void Context()
{
base.Context();

var expression = DomainHelperForSpecs.CreateExpressionProfile<IndividualEnzyme>("Human", "AnotherCYP");
expression.StructureVersion = _templateExpression.StructureVersion;
_individual.AddExpressionProfile(expression);
}

[Observation]
public void should_not_allow_for_a_quick_update()
{
sut.QuickUpdatePossibleFor(_templateIndividual, _usedIndividual).ShouldBeFalse();
}
}

public class When_updating_the_formulation_defined_in_a_simulation_according_to_the_formulation_mapping : concern_for_SimulationBuildingBlockUpdater
{
private Simulation _simulation;
Expand Down Expand Up @@ -169,23 +257,23 @@ public void should_add_one_instance_of_each_used_formulation_to_the_simulation()
public class When_updating_the_formulation_defined_in_a_simulation_according_to_the_formulation_mapping_using_both_a_template_and_a_simulation_formulation_for_the_same_formulation : concern_for_SimulationBuildingBlockUpdater
{
private Simulation _simulation;

protected override void Context()
{
base.Context();
var formulation = new Formulation().WithName("F");
_simulation = new IndividualSimulation { Properties = new SimulationProperties() };
_simulation = new IndividualSimulation {Properties = new SimulationProperties()};
var formulationUsedInSimulation = new Formulation().WithName("F");
var compoundProperties = new CompoundProperties();
compoundProperties.ProtocolProperties.AddFormulationMapping(new FormulationMapping { Formulation = formulation });
compoundProperties.ProtocolProperties.AddFormulationMapping(new FormulationMapping { Formulation = formulationUsedInSimulation });
compoundProperties.ProtocolProperties.AddFormulationMapping(new FormulationMapping {Formulation = formulation});
compoundProperties.ProtocolProperties.AddFormulationMapping(new FormulationMapping {Formulation = formulationUsedInSimulation});
_simulation.Properties.AddCompoundProperties(compoundProperties);
}

[Observation]
public void should_throw_an_exception()
{
The.Action(()=>sut.UpdateFormulationsInSimulation(_simulation)).ShouldThrowAn<PKSimException>();
The.Action(() => sut.UpdateFormulationsInSimulation(_simulation)).ShouldThrowAn<PKSimException>();
}
}

Expand Down
6 changes: 5 additions & 1 deletion tests/PKSim.Tests/Presentation/SimulationTaskSpecs.cs
Expand Up @@ -70,6 +70,7 @@ public class When_the_simulation_task_is_asked_to_perform_an_update_from_a_templ
private IPKSimBuildingBlock _templateBuildingBlock;
private IPKSimCommand _updateCommand;
private UsedBuildingBlock _usedBuildingBlock;
private UsedBuildingBlock _loadedUsedBuildingBlock;

protected override void Context()
{
Expand All @@ -78,7 +79,10 @@ protected override void Context()
_templateBuildingBlock = A.Fake<IPKSimBuildingBlock>();
_updateCommand = A.Fake<IPKSimCommand>();
_usedBuildingBlock = A.Fake<UsedBuildingBlock>();
A.CallTo(() => _simulationBuildingBlockUpdater.QuickUpdatePossibleFor(_templateBuildingBlock, _usedBuildingBlock)).Returns(true);
_loadedUsedBuildingBlock = A.Fake<UsedBuildingBlock>();
//make sure we a new instance to ensure usage of loaded used building block
A.CallTo(() => _simulation.UsedBuildingBlockByTemplateId(_usedBuildingBlock.TemplateId)).Returns(_loadedUsedBuildingBlock);
A.CallTo(() => _simulationBuildingBlockUpdater.QuickUpdatePossibleFor(_templateBuildingBlock, _loadedUsedBuildingBlock)).Returns(true);
A.CallTo(() => _blockParametersToSimulationUpdater.UpdateParametersFromBuildingBlockInSimulation(_templateBuildingBlock, _simulation)).Returns(_updateCommand);
}

Expand Down

0 comments on commit c6157b3

Please sign in to comment.