Skip to content

Commit

Permalink
Intervention validations (#5159)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom-Szendrey authored Oct 17, 2024
1 parent a42f28c commit 08310bf
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 7 deletions.
2 changes: 2 additions & 0 deletions packages/client/hmi-client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</router-view>
<tera-footer />
<tera-common-modal-dialogs />
<ConfirmDialog class="w-4" />
</template>

<script setup lang="ts">
Expand All @@ -20,6 +21,7 @@ import { useProjects } from '@/composables/project';
import { useCurrentRoute } from '@/router';
import { ToastSeverity, ToastSummaries, useToastService } from '@/services/toast';
import { Project } from '@/types/Types';
import ConfirmDialog from 'primevue/confirmdialog';
const toast = useToastService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defineProps<{
selected?: boolean;
}>();
const emit = defineEmits(['use-intervention']);
const emit = defineEmits(['use-intervention', 'delete-intervention-policy']);
const contextMenu = ref();
const contextMenuItems = [
Expand All @@ -32,6 +32,13 @@ const contextMenuItems = [
command() {
emit('use-intervention');
}
},
{
label: 'Delete',
icon: 'pi pi-trash',
command() {
emit('delete-intervention-policy');
}
}
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
:selected="selectedPolicy?.id === policy.id"
@click="onReplacePolicy(policy)"
@use-intervention="onReplacePolicy(policy)"
@delete-intervention-policy="onDeleteInterventionPolicy(policy)"
/>
</li>
</ul>
Expand Down Expand Up @@ -183,7 +184,8 @@ import {
blankIntervention,
flattenInterventionData,
getInterventionPolicyById,
updateInterventionPolicy
updateInterventionPolicy,
deleteInterventionPolicy
} from '@/services/intervention-policy';
import Accordion from 'primevue/accordion';
import AccordionTab from 'primevue/accordiontab';
Expand Down Expand Up @@ -406,6 +408,23 @@ const onReplacePolicy = (policy: InterventionPolicy) => {
}
};
const onDeleteInterventionPolicy = (policy: InterventionPolicy) => {
confirm.require({
message: `Are you sure you want to delete the configuration ${policy.name}?`,
header: 'Delete Confirmation',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Confirm',
rejectLabel: 'Cancel',
accept: async () => {
if (policy.id) {
await deleteInterventionPolicy(policy.id);
const modelId = props.node.inputs[0].value?.[0];
fetchInterventionPolicies(modelId);
}
}
});
};
const addIntervention = () => {
// by default add the first parameter with a static intervention
knobs.value.transientInterventionPolicy.interventions.push(blankIntervention);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ public ResponseEntity<InterventionPolicy> createIntervention(
currentUserService.get().getId(),
projectId
);
try {
item.validateInterventionPolicy();
} catch (final Exception e) {
final String error = "Failed to validate intervention";
log.error(error, e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
}
try {
return ResponseEntity.status(HttpStatus.CREATED).body(
interventionService.createAsset(item, projectId, permission)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import lombok.Data;
import lombok.EqualsAndHashCode;
Expand Down Expand Up @@ -40,4 +42,45 @@ public InterventionPolicy clone() {
}
return clone;
}

/**
* Check each intervention this policy contains
* Within all static interventions
* if there are duplicate keys appliedTo and timestep this will throw an error
* If any timestep is negative this will throw an error
* */
public void validateInterventionPolicy() throws Exception {
final Set<String> duplicateCheckSet = new HashSet<>();
for (int i = 0; i < this.interventions.size(); i++) {
final Intervention intervention = this.interventions.get(i);
// For each static intervention within the policy:
for (int j = 0; j < intervention.getStaticInterventions().size(); j++) {
final StaticIntervention staticIntervention = intervention.getStaticInterventions().get(j);
// Check for negative timestamps:
final Number time = staticIntervention.getTimestep();
if (time.doubleValue() < 0) {
final String errorMessage = String.format(
"The intervention %s has a timestep %s which is less than 0.",
this.getName(),
time.toString()
);
throw new Exception(errorMessage);
}
// Check for duplicate appliedTo timestep pairs:
final String key =
staticIntervention.getAppliedTo() + "atTheTime:" + staticIntervention.getTimestep().toString();
if (duplicateCheckSet.contains(key)) {
final String errorMessage = String.format(
"The intervention %s has duplicate applied to: %s and time: %s pairs.",
intervention.getName(),
staticIntervention.getAppliedTo(),
staticIntervention.getTimestep().toString()
);
throw new Exception(errorMessage);
} else {
duplicateCheckSet.add(key);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package software.uncharted.terarium.hmiserver.models;

import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import software.uncharted.terarium.hmiserver.TerariumApplicationTests;
import software.uncharted.terarium.hmiserver.models.simulationservice.interventions.Intervention;
import software.uncharted.terarium.hmiserver.models.simulationservice.interventions.InterventionPolicy;
import software.uncharted.terarium.hmiserver.models.simulationservice.interventions.StaticIntervention;

public class InterventionPolicyTests extends TerariumApplicationTests {

@Test
void testValidateWithNegative() throws Exception {
final InterventionPolicy interventionPolicy = new InterventionPolicy();
final List<Intervention> interventions = new ArrayList<Intervention>();
final Intervention negativeIntervention = new Intervention();
final List<StaticIntervention> staticInterventions = new ArrayList<StaticIntervention>();
final StaticIntervention negativeStatic = new StaticIntervention();

negativeStatic.setAppliedTo("beta");
negativeStatic.setTimestep(-1);
negativeStatic.setValue(10);

staticInterventions.add(negativeStatic);

negativeIntervention.setName("NegativeInterventionName");
negativeIntervention.setStaticInterventions(staticInterventions);

interventions.add(negativeIntervention);

interventionPolicy.setName("testNegativeFails");
interventionPolicy.setDescription("mydescription");
interventionPolicy.setInterventions(interventions);

Assertions.assertThrowsExactly(
Exception.class,
() -> interventionPolicy.validateInterventionPolicy(),
"The intervention NegativeInterventionName has a timestep -1 which is less than 0."
);
}

@Test
void testValidateWithDuplicateInIntervention() throws Exception {
final InterventionPolicy interventionPolicy = new InterventionPolicy();
final List<Intervention> interventions = new ArrayList<Intervention>();
final Intervention intervention = new Intervention();
final List<StaticIntervention> staticInterventions = new ArrayList<StaticIntervention>();
final StaticIntervention staticOne = new StaticIntervention();
final StaticIntervention staticTwo = new StaticIntervention();

staticOne.setAppliedTo("beta");
staticOne.setTimestep(1);
staticOne.setValue(10);

staticTwo.setAppliedTo("beta");
staticTwo.setTimestep(1);
staticTwo.setValue(20);

staticInterventions.add(staticOne);
staticInterventions.add(staticTwo);

intervention.setName("DuplicateName");
intervention.setStaticInterventions(staticInterventions);

interventions.add(intervention);

interventionPolicy.setName("testDuplicateInInterventionFails");
interventionPolicy.setDescription("mydescription");
interventionPolicy.setInterventions(interventions);

Assertions.assertThrowsExactly(
Exception.class,
() -> interventionPolicy.validateInterventionPolicy(),
"The intervention DuplicateName has duplicate applied to: beta and time: 1 pairs."
);
}

@Test
void testValidateWithDuplicateInPolicy() throws Exception {
final InterventionPolicy interventionPolicy = new InterventionPolicy();
final List<Intervention> interventions = new ArrayList<Intervention>();
final Intervention interventionOne = new Intervention();
final Intervention interventionTwo = new Intervention();
final List<StaticIntervention> staticInterventionsOne = new ArrayList<StaticIntervention>();
final List<StaticIntervention> staticInterventionsTwo = new ArrayList<StaticIntervention>();
final StaticIntervention staticOne = new StaticIntervention();
final StaticIntervention staticTwo = new StaticIntervention();

staticOne.setAppliedTo("beta");
staticOne.setTimestep(1);
staticOne.setValue(10);

staticTwo.setAppliedTo("beta");
staticTwo.setTimestep(1);
staticTwo.setValue(20);

staticInterventionsOne.add(staticOne);
staticInterventionsTwo.add(staticTwo);

interventionOne.setName("IntOne");
interventionOne.setStaticInterventions(staticInterventionsOne);

interventionTwo.setName("IntTwo");
interventionTwo.setStaticInterventions(staticInterventionsTwo);

interventions.add(interventionOne);
interventions.add(interventionTwo);

interventionPolicy.setName("testDuplicateInPolicyFails");
interventionPolicy.setDescription("mydescription");
interventionPolicy.setInterventions(interventions);

Assertions.assertThrowsExactly(
Exception.class,
() -> interventionPolicy.validateInterventionPolicy(),
"The intervention IntTwo has duplicate applied to: beta and time: 1 pairs."
);
}

@Test
void testValidateSuccess() throws Exception {
final InterventionPolicy interventionPolicy = new InterventionPolicy();
final List<Intervention> interventions = new ArrayList<Intervention>();
final Intervention interventionOne = new Intervention();
final Intervention interventionTwo = new Intervention();
final List<StaticIntervention> staticInterventionsOne = new ArrayList<StaticIntervention>();
final List<StaticIntervention> staticInterventionsTwo = new ArrayList<StaticIntervention>();
final StaticIntervention staticOne = new StaticIntervention();
final StaticIntervention staticTwo = new StaticIntervention();

staticOne.setAppliedTo("beta");
staticOne.setTimestep(1);
staticOne.setValue(10);

staticTwo.setAppliedTo("beta");
staticTwo.setTimestep(2);
staticTwo.setValue(20);

staticInterventionsOne.add(staticOne);
staticInterventionsTwo.add(staticTwo);

interventionOne.setName("IntOne");
interventionOne.setStaticInterventions(staticInterventionsOne);

interventionTwo.setName("IntTwo");
interventionTwo.setStaticInterventions(staticInterventionsTwo);

interventions.add(interventionOne);
interventions.add(interventionTwo);

interventionPolicy.setName("testNoDuplicatePass");
interventionPolicy.setDescription("mydescription");
interventionPolicy.setInterventions(interventions);

Assertions.assertAll(() -> interventionPolicy.validateInterventionPolicy());
}
}
18 changes: 13 additions & 5 deletions testing/manual/intervention-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,36 @@ Report any issues into GitHub: [open an issue](https://github.com/DARPA-ASKEM/te
5. Set the Parameter gamma to 0.5
6. Check that the charts reflect the intervention
### 5. Create a dynamic parameter criteria
### 5. Try to create an invalid intervention
1. Create an intervention that has a negative timestamp within a static intervention.
Expect to see a toaster error explaining where you have an invalid intervention
2. Create an intervention that contains two static interventions each with the same parameter and time selected
Expect to see a toaster error explaining you have duplicates.
3. Create two interventions each containing at least one static intervention that has the same parameter and time selected
Expect to see a toaster error explaining you have duplicates.
### 6. Create a dynamic parameter criteria
1. Click `+ Add intervention`
2. Name it `Dynamic Parameter` and change it to _Dynamic_.
3. Set Parameter `beta` to `0.0009` when `Susceptible` crosses the threshold of `700`.
### 6. Create a static state criteria
### 7. Create a static state criteria
1. Click `+ Add intervention`
2. Name it `Static State` and leave it as _Static_.
3. Set State `I` to value `600` starting at timestep 40`.
### 7. Create a dynamic state criteria
### 8. Create a dynamic state criteria
1. Click `+ Add intervention`
2. Name it `Dynamic State` and change it to _Dynamic_.
3. Set State `I` to `100` when `S` crosses the threshold of `800`.
### 8. Save the intervention policy
### 9. Save the intervention policy
1. Save the intervention policy
2. Check the preview of the intervention policy
1. Does the AI assisted _description_ match the criteria?
2. Are the _criteria_ visible in the chart?
### 9. Simulate with the intervention policy
### 10. Simulate with the intervention policy
1. Add the operator `Configure model` to the workflow
2. Connect the model to the operator
3. Add the operator `Simulate` to the workflow
Expand Down

0 comments on commit 08310bf

Please sign in to comment.