Get started by creating a
@@ -232,7 +229,11 @@ const viewOptions = ref([
const myFilteredSortedProjects = computed(() => {
const projects = useProjects().allProjects.value;
if (!projects) return [];
- const myProjects = projects.filter(({ userPermission }) => ['creator', 'writer'].includes(userPermission ?? ''));
+ const myProjects = projects.filter(
+ ({ userPermission, publicProject }) =>
+ // I can edit the project, or I can view the project and it's not public
+ ['creator', 'writer'].includes(userPermission ?? '') || (userPermission === 'reader' && !publicProject)
+ );
return filterAndSortProjects(myProjects);
});
diff --git a/packages/client/hmi-client/src/services/code.ts b/packages/client/hmi-client/src/services/code.ts
index a96c87cf85..4f242b1d70 100644
--- a/packages/client/hmi-client/src/services/code.ts
+++ b/packages/client/hmi-client/src/services/code.ts
@@ -167,7 +167,7 @@ function getProgrammingLanguage(fileName: string): ProgrammingLanguage {
case 'zip':
return ProgrammingLanguage.Zip;
default:
- return ProgrammingLanguage.Python; // TODO do we need an "unknown" language?
+ return ProgrammingLanguage.Python;
}
}
diff --git a/packages/client/hmi-client/src/services/notebook.ts b/packages/client/hmi-client/src/services/notebook.ts
index a250a2100a..3054680955 100644
--- a/packages/client/hmi-client/src/services/notebook.ts
+++ b/packages/client/hmi-client/src/services/notebook.ts
@@ -33,72 +33,3 @@ export const saveCodeToState = (
}
return state;
};
-
-/**
- * Create a notebook from a code
- * @param code code to be added to the notebook
- * @param executeResult output of the code execution if any
- * @param language language of the code
- * @param llmQuery llm query used to generate the code if any
- * @param thought llm thought generated from the query if any
- * @returns
- */
-export const createNotebookFromCode = (
- code: string,
- language: string,
- executionResult?: any,
- llmQuery?: string,
- llmThoughts: any[] = []
-) => {
- // TODO: Consider using jataware/beaker-kernel library to generate notebook (https://github.com/jataware/beaker-kernel/blob/886b2b3913ca1460f0301a5cd97cbcf15de609bc/beaker-ts/src/notebook.ts#L414)
- const notebook = {
- nbformat: 4,
- nbformat_minor: 5,
- cells: [] as any[],
- metadata: {
- kernelspec: {
- display_name: 'Beaker Kernel',
- name: 'beaker',
- language: 'beaker'
- },
- language_info: {
- name: language,
- display_name: language
- }
- }
- };
- if (llmQuery) {
- const beakerQueryCell = {
- cell_type: 'query',
- events: llmThoughts.map((thought) => ({ type: 'thought', content: thought.content })),
- metadata: {},
- source: llmQuery,
- status: 'idle'
- };
- notebook.cells.push(beakerQueryCell);
- }
- const beakerCodeCell = {
- cell_type: 'code',
- execution_count: 1,
- metadata: {},
- outputs: [] as any[],
- source: code,
- status: 'idle'
- };
- if (executionResult) {
- // Make a shallow copy of the execution result
- const data = { ...executionResult };
- // Make sure the values of the data is stringified as the beaker summary endpoint seem to have issue with object json value
- Object.keys(data).forEach((type) => {
- if (typeof data[type] !== 'string') {
- data[type] = JSON.stringify(data[type]);
- }
- });
- beakerCodeCell.outputs.push({
- output_type: 'execute_result',
- data
- });
- }
- notebook.cells.push(beakerCodeCell);
- return notebook;
-};
diff --git a/packages/client/hmi-client/src/stores/auth.ts b/packages/client/hmi-client/src/stores/auth.ts
index 9e9d89a572..e4c94dfdc9 100644
--- a/packages/client/hmi-client/src/stores/auth.ts
+++ b/packages/client/hmi-client/src/stores/auth.ts
@@ -3,6 +3,7 @@ import type { User } from '@/types/Types';
import axios, { AxiosHeaders } from 'axios';
import { computed, ref } from 'vue';
import { createOidc, Oidc } from 'oidc-spa';
+import { RoleType } from '@/types/Types';
/**
* Main store used for authentication
@@ -70,6 +71,11 @@ const useAuthStore = defineStore('auth', () => {
await loadUserModel();
};
+ const isAdmin = computed(() => {
+ if (!user.value) return false;
+ return user.value?.roles.some((r) => r.name === RoleType.Admin);
+ });
+
return {
login,
logout,
@@ -78,7 +84,8 @@ const useAuthStore = defineStore('auth', () => {
updateUser,
loadUserModel,
userInitials,
- init
+ init,
+ isAdmin
};
});
diff --git a/packages/client/hmi-client/src/types/ResponsiveMatrix.ts b/packages/client/hmi-client/src/types/ResponsiveMatrix.ts
index 659d60b93a..bfdc5daa57 100644
--- a/packages/client/hmi-client/src/types/ResponsiveMatrix.ts
+++ b/packages/client/hmi-client/src/types/ResponsiveMatrix.ts
@@ -50,9 +50,6 @@ export type DataConfig = {
export type RowColConfig = {
borderEnabled: boolean;
borderWidth: number;
- // TODO
- // labelFormatterFn: (val :any, idx: number) => string
- // labelAltFn?: (val: any, idx: number) => string,
};
export type VisConfig = {
diff --git a/packages/client/hmi-client/src/types/common.ts b/packages/client/hmi-client/src/types/common.ts
index 4a828b18b8..0e45c3fc05 100644
--- a/packages/client/hmi-client/src/types/common.ts
+++ b/packages/client/hmi-client/src/types/common.ts
@@ -43,26 +43,6 @@ export interface ModelConfigTableData {
tableFormattedMatrix?: ModelConfigTableData[];
}
-// TODO: Wherever these are used - investigate using an actual map instead, this has been avoided due to v-model not playing well with maps
-// But a solution might be found here: https://stackoverflow.com/questions/37130105/does-vue-support-reactivity-on-map-and-set-data-types/64512468#64512468
-export interface StringValueMap {
- [key: string]: string;
-}
-
-export interface NumericValueMap {
- [key: string]: number;
-}
-
-export interface AnyValueMap {
- [key: string]: any;
-}
-
-export enum ViewType {
- LIST = 'list',
- MATRIX = 'matrix',
- GRAPH = 'graph'
-}
-
// Side panel
export type SidePanelTab = {
name: string;
@@ -83,11 +63,6 @@ export interface AssetItem extends AssetRoute {
assetCreatedOn?: string;
}
-export type CodeRequest = {
- asset: AssetItem;
- code?: string;
-};
-
// TODO this should come from the back end, and we should also have maps for the "categories" of types (artifacts, models, datasets, etc)
export enum AcceptedTypes {
PDF = 'application/pdf',
diff --git a/packages/funman/funman-version.txt b/packages/funman/funman-version.txt
index b1e9266670..dacce77631 100644
--- a/packages/funman/funman-version.txt
+++ b/packages/funman/funman-version.txt
@@ -1 +1 @@
-e9b66753937dbce63a9dd7aafbaa12b6cc93d097
+c617b498eb28c75629784fc3b54309f82777fe7d
diff --git a/packages/server/src/main/java/software/uncharted/terarium/hmiserver/controller/dataservice/InterventionController.java b/packages/server/src/main/java/software/uncharted/terarium/hmiserver/controller/dataservice/InterventionController.java
index 55e7d64543..8c211b1ad7 100644
--- a/packages/server/src/main/java/software/uncharted/terarium/hmiserver/controller/dataservice/InterventionController.java
+++ b/packages/server/src/main/java/software/uncharted/terarium/hmiserver/controller/dataservice/InterventionController.java
@@ -108,6 +108,13 @@ public ResponseEntity 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)
diff --git a/packages/server/src/main/java/software/uncharted/terarium/hmiserver/models/simulationservice/interventions/InterventionPolicy.java b/packages/server/src/main/java/software/uncharted/terarium/hmiserver/models/simulationservice/interventions/InterventionPolicy.java
index d69d1589ed..c2015db6f4 100644
--- a/packages/server/src/main/java/software/uncharted/terarium/hmiserver/models/simulationservice/interventions/InterventionPolicy.java
+++ b/packages/server/src/main/java/software/uncharted/terarium/hmiserver/models/simulationservice/interventions/InterventionPolicy.java
@@ -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;
@@ -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 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);
+ }
+ }
+ }
+ }
}
diff --git a/packages/server/src/main/java/software/uncharted/terarium/hmiserver/service/data/WorkflowService.java b/packages/server/src/main/java/software/uncharted/terarium/hmiserver/service/data/WorkflowService.java
index 30428d4902..2f144d8efb 100644
--- a/packages/server/src/main/java/software/uncharted/terarium/hmiserver/service/data/WorkflowService.java
+++ b/packages/server/src/main/java/software/uncharted/terarium/hmiserver/service/data/WorkflowService.java
@@ -13,6 +13,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import software.uncharted.terarium.hmiserver.configuration.Config;
import software.uncharted.terarium.hmiserver.models.dataservice.workflow.Workflow;
@@ -23,6 +24,7 @@
import software.uncharted.terarium.hmiserver.utils.rebac.Schema;
@Service
+@Slf4j
public class WorkflowService extends TerariumAssetServiceWithoutSearch {
public WorkflowService(
@@ -95,6 +97,21 @@ public Optional updateAsset(
node.setVersion(1L);
}
nodeMap.put(node.getId(), node);
+
+ // Debugging possible sync issue - October 2024
+ log.info("Node id=" + node.getId() + ", status=" + node.getStatus() + ", deleted=" + node.getIsDeleted());
+ if (node.getOutputs() != null) {
+ for (final JsonNode o : node.getOutputs()) {
+ final JsonNode oValue = o.get("value").get(0);
+ if (oValue != null) {
+ log.info(" Out: " + oValue.asText());
+ } else {
+ log.info(" Out: null");
+ }
+ }
+ } else {
+ log.info(" no outputs");
+ }
}
}
if (asset.getEdges() != null) {
diff --git a/packages/server/src/test/java/software/uncharted/terarium/hmiserver/models/InterventionPolicyTests.java b/packages/server/src/test/java/software/uncharted/terarium/hmiserver/models/InterventionPolicyTests.java
new file mode 100644
index 0000000000..451f8fd407
--- /dev/null
+++ b/packages/server/src/test/java/software/uncharted/terarium/hmiserver/models/InterventionPolicyTests.java
@@ -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 interventions = new ArrayList();
+ final Intervention negativeIntervention = new Intervention();
+ final List staticInterventions = new ArrayList();
+ 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 interventions = new ArrayList();
+ final Intervention intervention = new Intervention();
+ final List staticInterventions = new ArrayList();
+ 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 interventions = new ArrayList();
+ final Intervention interventionOne = new Intervention();
+ final Intervention interventionTwo = new Intervention();
+ final List staticInterventionsOne = new ArrayList();
+ final List staticInterventionsTwo = new ArrayList();
+ 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 interventions = new ArrayList();
+ final Intervention interventionOne = new Intervention();
+ final Intervention interventionTwo = new Intervention();
+ final List staticInterventionsOne = new ArrayList();
+ final List staticInterventionsTwo = new ArrayList();
+ 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());
+ }
+}
diff --git a/testing/manual/intervention-policy.md b/testing/manual/intervention-policy.md
index 415b2c2097..4147c775c4 100644
--- a/testing/manual/intervention-policy.md
+++ b/testing/manual/intervention-policy.md
@@ -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