From 7003f007076802014e6031c9e8f9c47afd2b26b7 Mon Sep 17 00:00:00 2001 From: Shefali Date: Tue, 9 Jan 2024 23:15:12 -0800 Subject: [PATCH 1/6] Add activity states domain object and interceptor to auto create one --- index.html | 1 + .../activityStatesInterceptor.js | 51 +++++++++++ .../createActivityStatesIdentifier.js | 9 ++ src/plugins/activityStates/plugin.js | 42 +++++++++ src/plugins/activityStates/pluginSpec.js | 89 +++++++++++++++++++ src/plugins/plugins.js | 2 + 6 files changed, 194 insertions(+) create mode 100644 src/plugins/activityStates/activityStatesInterceptor.js create mode 100644 src/plugins/activityStates/createActivityStatesIdentifier.js create mode 100644 src/plugins/activityStates/plugin.js create mode 100644 src/plugins/activityStates/pluginSpec.js diff --git a/index.html b/index.html index 89bfd069ca..aa8f97d657 100644 --- a/index.html +++ b/index.html @@ -108,6 +108,7 @@ openmct.install(openmct.plugins.Espresso()); openmct.install(openmct.plugins.MyItems()); + openmct.install(openmct.plugins.ActivityStates()); openmct.install( openmct.plugins.PlanLayout({ creatable: true diff --git a/src/plugins/activityStates/activityStatesInterceptor.js b/src/plugins/activityStates/activityStatesInterceptor.js new file mode 100644 index 0000000000..8106e8deef --- /dev/null +++ b/src/plugins/activityStates/activityStatesInterceptor.js @@ -0,0 +1,51 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2023, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +import { ACTIVITYSTATES_KEY, ACTIVITYSTATES_TYPE } from './createActivityStatesIdentifier.js'; + +function activityStatesInterceptor(openmct, identifierObject, name) { + const activityStatesModel = { + identifier: identifierObject, + name, + type: ACTIVITYSTATES_TYPE, + activities: {}, + location: null + }; + + return { + appliesTo: (identifier) => { + return identifier.key === ACTIVITYSTATES_KEY; + }, + invoke: (identifier, object) => { + if (!object || openmct.objects.isMissing(object)) { + openmct.objects.save(activityStatesModel); + + return activityStatesModel; + } + + return object; + }, + priority: openmct.priority.HIGH + }; +} + +export default activityStatesInterceptor; diff --git a/src/plugins/activityStates/createActivityStatesIdentifier.js b/src/plugins/activityStates/createActivityStatesIdentifier.js new file mode 100644 index 0000000000..fd281acbaf --- /dev/null +++ b/src/plugins/activityStates/createActivityStatesIdentifier.js @@ -0,0 +1,9 @@ +export const ACTIVITYSTATES_KEY = 'activity-states'; +export const ACTIVITYSTATES_TYPE = 'activity-states'; + +export function createActivityStatesIdentifier(namespace = '') { + return { + key: ACTIVITYSTATES_KEY, + namespace + }; +} diff --git a/src/plugins/activityStates/plugin.js b/src/plugins/activityStates/plugin.js new file mode 100644 index 0000000000..f90262eb87 --- /dev/null +++ b/src/plugins/activityStates/plugin.js @@ -0,0 +1,42 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2023, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +import activityStatesInterceptor from './activityStatesInterceptor.js'; +import { createActivityStatesIdentifier } from './createActivityStatesIdentifier.js'; + +const ACTIVITYSTATES_DEFAULT_NAME = 'Activity States'; + +export default function ActivityStatesPlugin( + name = ACTIVITYSTATES_DEFAULT_NAME, + namespace = '', + priority = undefined +) { + return function install(openmct) { + const identifier = createActivityStatesIdentifier(namespace); + + if (priority === undefined) { + priority = openmct.priority.LOW; + } + + openmct.objects.addGetInterceptor(activityStatesInterceptor(openmct, identifier, name)); + }; +} diff --git a/src/plugins/activityStates/pluginSpec.js b/src/plugins/activityStates/pluginSpec.js new file mode 100644 index 0000000000..0b481fbdb4 --- /dev/null +++ b/src/plugins/activityStates/pluginSpec.js @@ -0,0 +1,89 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2023, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +import { createOpenMct, resetApplicationState } from 'utils/testing'; + +import { + ACTIVITYSTATES_KEY, + createActivityStatesIdentifier +} from './createActivityStatesIdentifier.js'; + +const MISSING_NAME = `Missing: ${ACTIVITYSTATES_KEY}`; +const DEFAULT_NAME = 'Activity States'; +const activityStatesIdentifier = createActivityStatesIdentifier(); + +describe('the plugin', () => { + let openmct; + let missingObj = { + identifier: activityStatesIdentifier, + type: 'unknown', + name: MISSING_NAME + }; + + describe('with no arguments passed in', () => { + beforeEach((done) => { + openmct = createOpenMct(); + openmct.install(openmct.plugins.ActivityStates()); + + openmct.on('start', done); + openmct.startHeadless(); + }); + + afterEach(() => { + return resetApplicationState(openmct); + }); + + it('when installed, adds "Activity States"', async () => { + const activityStatesObject = await openmct.objects.get(activityStatesIdentifier); + expect(activityStatesObject.name).toBe(DEFAULT_NAME); + expect(activityStatesObject).toBeDefined(); + }); + + describe('adds an interceptor that returns a "Activity States" model for', () => { + let activityStatesObject; + let mockNotFoundProvider; + let activeProvider; + + beforeEach(async () => { + mockNotFoundProvider = { + get: () => Promise.reject(new Error('Not found')), + create: () => Promise.resolve(missingObj), + update: () => Promise.resolve(missingObj) + }; + + activeProvider = mockNotFoundProvider; + spyOn(openmct.objects, 'getProvider').and.returnValue(activeProvider); + activityStatesObject = await openmct.objects.get(activityStatesIdentifier); + }); + + it('missing objects', () => { + let idsMatch = openmct.objects.areIdsEqual( + activityStatesObject.identifier, + activityStatesIdentifier + ); + + expect(activityStatesObject).toBeDefined(); + expect(idsMatch).toBeTrue(); + }); + }); + }); +}); diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 1583fdfc39..f26ccd5372 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -27,6 +27,7 @@ import ExampleUser from '../../example/exampleUser/plugin.js'; import ExampleFaultSource from '../../example/faultManagement/exampleFaultSource.js'; import GeneratorPlugin from '../../example/generator/plugin.js'; import ExampleImagery from '../../example/imagery/plugin.js'; +import ActivityStatesPlugin from './activityStates/plugin.js'; import AutoflowPlugin from './autoflow/AutoflowTabularPlugin.js'; import BarChartPlugin from './charts/bar/plugin.js'; import ScatterPlotPlugin from './charts/scatter/plugin.js'; @@ -101,6 +102,7 @@ plugins.LocalTimeSystem = LocalTimeSystem; plugins.RemoteClock = RemoteClock; plugins.MyItems = MyItems; +plugins.ActivityStates = ActivityStatesPlugin; plugins.StaticRootPlugin = StaticRootPlugin; From 92a30a34852b97cf211f78d9c1e8eded1e8bffcc Mon Sep 17 00:00:00 2001 From: Shefali Date: Tue, 9 Jan 2024 23:16:12 -0800 Subject: [PATCH 2/6] Add activity state inspector option --- src/api/objects/ObjectAPI.js | 8 +- src/plugins/plan/components/PlanView.vue | 1 + .../components/PlanActivitiesView.vue | 114 +++++++++++++----- .../components/PlanActivityPropertiesView.vue | 83 +++++++++++++ .../components/PlanActivityStatusView.vue | 114 ++++++++++++++++++ ...ivityView.vue => PlanActivityTimeView.vue} | 30 ++--- 6 files changed, 305 insertions(+), 45 deletions(-) create mode 100644 src/plugins/plan/inspector/components/PlanActivityPropertiesView.vue create mode 100644 src/plugins/plan/inspector/components/PlanActivityStatusView.vue rename src/plugins/plan/inspector/components/{PlanActivityView.vue => PlanActivityTimeView.vue} (72%) diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index e58480efee..fea90b164c 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -99,7 +99,13 @@ export default class ObjectAPI { this.cache = {}; this.interceptorRegistry = new InterceptorRegistry(); - this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'restricted-notebook', 'plan', 'annotation']; + this.SYNCHRONIZED_OBJECT_TYPES = [ + 'notebook', + 'restricted-notebook', + 'plan', + 'annotation', + 'activity-states' + ]; this.errors = { Conflict: ConflictError diff --git a/src/plugins/plan/components/PlanView.vue b/src/plugins/plan/components/PlanView.vue index 2596bcb6bc..a5d3afd073 100644 --- a/src/plugins/plan/components/PlanView.vue +++ b/src/plugins/plan/components/PlanView.vue @@ -488,6 +488,7 @@ export default { }, start: rawActivity.start, end: rawActivity.end, + description: rawActivity.description, row: currentRow, textLines: textLines, textStart: textStart, diff --git a/src/plugins/plan/inspector/components/PlanActivitiesView.vue b/src/plugins/plan/inspector/components/PlanActivitiesView.vue index e2d80be38a..dcc3ba0da2 100644 --- a/src/plugins/plan/inspector/components/PlanActivitiesView.vue +++ b/src/plugins/plan/inspector/components/PlanActivitiesView.vue @@ -20,21 +20,39 @@ at runtime from the About dialog for additional information. --> diff --git a/src/plugins/plan/inspector/components/PlanActivityStatusView.vue b/src/plugins/plan/inspector/components/PlanActivityStatusView.vue new file mode 100644 index 0000000000..b7e4a53420 --- /dev/null +++ b/src/plugins/plan/inspector/components/PlanActivityStatusView.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/src/plugins/plan/inspector/components/PlanActivityView.vue b/src/plugins/plan/inspector/components/PlanActivityTimeView.vue similarity index 72% rename from src/plugins/plan/inspector/components/PlanActivityView.vue rename to src/plugins/plan/inspector/components/PlanActivityTimeView.vue index 4860e1f9e7..89884115e3 100644 --- a/src/plugins/plan/inspector/components/PlanActivityView.vue +++ b/src/plugins/plan/inspector/components/PlanActivityTimeView.vue @@ -21,17 +21,19 @@ --> @@ -64,10 +66,10 @@ export default { }, methods: { setProperties() { - Object.keys(this.activity).forEach((key) => { - if (this.activity[key].label) { - const label = this.activity[key].label; - const value = String(this.activity[key].value); + Object.keys(this.activity.timeProperties).forEach((key) => { + if (this.activity.timeProperties[key].label) { + const label = this.activity.timeProperties[key].label; + const value = String(this.activity.timeProperties[key].value); this.timeProperties[this.timeProperties.length] = { id: uuid(), From 4d4f83ee95ed7ccab2922baaf913e707d4575d74 Mon Sep 17 00:00:00 2001 From: Shefali Date: Wed, 10 Jan 2024 10:13:40 -0800 Subject: [PATCH 3/6] Only save status if we have a unique ids for activities --- .../components/PlanActivitiesView.vue | 26 ++++++++++++++----- .../components/PlanActivityPropertiesView.vue | 5 ++-- .../components/PlanActivityStatusView.vue | 2 +- .../components/PlanActivityTimeView.vue | 5 ++-- src/plugins/plan/util.js | 4 +++ 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/plugins/plan/inspector/components/PlanActivitiesView.vue b/src/plugins/plan/inspector/components/PlanActivitiesView.vue index dcc3ba0da2..6bd9c8ed31 100644 --- a/src/plugins/plan/inspector/components/PlanActivitiesView.vue +++ b/src/plugins/plan/inspector/components/PlanActivitiesView.vue @@ -23,26 +23,26 @@
@@ -80,6 +80,11 @@ export default { heading: '' }; }, + computed: { + canPersistState() { + return this.activities.length === 1 && this.activities[0].id; + } + }, mounted() { this.setFormatters(); this.getPlanData(this.selection); @@ -142,7 +147,8 @@ export default { this.activities.splice(0); this.selectedActivities.forEach((selectedActivity, index) => { const activity = { - uuid: selectedActivity.uuid ?? selectedActivity.name, + id: selectedActivity.id, + key: selectedActivity.id ?? selectedActivity.name, timeProperties: { start: { label: propertyLabels.start, @@ -189,6 +195,8 @@ export default { let latestEnd; let gap; let overlap; + let id; + let key; //Sort by start time let selectedActivities = this.selectedActivities.sort(this.sortFn); @@ -207,6 +215,8 @@ export default { earliestStart = Math.min(earliestStart, selectedActivity.start); latestEnd = Math.max(latestEnd, selectedActivity.end); } else { + id = selectedActivity.id; + key = selectedActivity.id ?? selectedActivity.name; earliestStart = selectedActivity.start; latestEnd = selectedActivity.end; } @@ -214,6 +224,8 @@ export default { let totalTime = latestEnd - earliestStart; const activity = { + id, + key, timeProperties: { earliestStart: { label: propertyLabels.earliestStart, diff --git a/src/plugins/plan/inspector/components/PlanActivityPropertiesView.vue b/src/plugins/plan/inspector/components/PlanActivityPropertiesView.vue index b0fa628729..1e5e945b04 100644 --- a/src/plugins/plan/inspector/components/PlanActivityPropertiesView.vue +++ b/src/plugins/plan/inspector/components/PlanActivityPropertiesView.vue @@ -32,8 +32,6 @@