mirror of
https://github.com/nasa/openmct.git
synced 2024-12-20 05:37:53 +00:00
Have annotations work with domain objects that have dots (#7065)
* migrating to new structure - wip * notebooks work, now to plots and images * resolve conflicts * fix search * add to readme * spelling * fix unit test * add search by view for big search speedup * spelling * fix out of order search * improve reliability of plot tagging tests
This commit is contained in:
parent
05c7a81630
commit
482f1f68dd
@ -41,7 +41,7 @@ test.describe('Plot Tagging', () => {
|
|||||||
* @param {Number} yEnd a telemetry item with a plot
|
* @param {Number} yEnd a telemetry item with a plot
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async function createTags({ page, canvas, xEnd = 700, yEnd = 480 }) {
|
async function createTags({ page, canvas, xEnd = 700, yEnd = 520 }) {
|
||||||
await canvas.hover({ trial: true });
|
await canvas.hover({ trial: true });
|
||||||
|
|
||||||
//Alt+Shift Drag Start to select some points to tag
|
//Alt+Shift Drag Start to select some points to tag
|
||||||
@ -284,7 +284,7 @@ test.describe('Plot Tagging', () => {
|
|||||||
page,
|
page,
|
||||||
canvas,
|
canvas,
|
||||||
xEnd: 700,
|
xEnd: 700,
|
||||||
yEnd: 215
|
yEnd: 240
|
||||||
});
|
});
|
||||||
await basicTagsTests(page);
|
await basicTagsTests(page);
|
||||||
await testTelemetryItem(page, alphaSineWave);
|
await testTelemetryItem(page, alphaSineWave);
|
||||||
|
@ -41,7 +41,7 @@ test.describe('Plot Tagging Performance', () => {
|
|||||||
* @param {Number} yEnd a telemetry item with a plot
|
* @param {Number} yEnd a telemetry item with a plot
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async function createTags({ page, canvas, xEnd = 700, yEnd = 480 }) {
|
async function createTags({ page, canvas, xEnd = 700, yEnd = 520 }) {
|
||||||
await canvas.hover({ trial: true });
|
await canvas.hover({ trial: true });
|
||||||
|
|
||||||
//Alt+Shift Drag Start to select some points to tag
|
//Alt+Shift Drag Start to select some points to tag
|
||||||
@ -265,7 +265,7 @@ test.describe('Plot Tagging Performance', () => {
|
|||||||
page,
|
page,
|
||||||
canvas,
|
canvas,
|
||||||
xEnd: 700,
|
xEnd: 700,
|
||||||
yEnd: 215
|
yEnd: 240
|
||||||
});
|
});
|
||||||
await basicTagsTests(page);
|
await basicTagsTests(page);
|
||||||
await testTelemetryItem(page, alphaSineWave);
|
await testTelemetryItem(page, alphaSineWave);
|
||||||
|
@ -100,7 +100,7 @@ export default class AnnotationAPI extends EventEmitter {
|
|||||||
creatable: false,
|
creatable: false,
|
||||||
cssClass: 'icon-notebook',
|
cssClass: 'icon-notebook',
|
||||||
initialize: function (domainObject) {
|
initialize: function (domainObject) {
|
||||||
domainObject.targets = domainObject.targets || {};
|
domainObject.targets = domainObject.targets || [];
|
||||||
domainObject._deleted = domainObject._deleted || false;
|
domainObject._deleted = domainObject._deleted || false;
|
||||||
domainObject.originalContextPath = domainObject.originalContextPath || '';
|
domainObject.originalContextPath = domainObject.originalContextPath || '';
|
||||||
domainObject.tags = domainObject.tags || [];
|
domainObject.tags = domainObject.tags || [];
|
||||||
@ -117,10 +117,10 @@ export default class AnnotationAPI extends EventEmitter {
|
|||||||
* @property {ANNOTATION_TYPES} annotationType the type of annotation to create (e.g., PLOT_SPATIAL)
|
* @property {ANNOTATION_TYPES} annotationType the type of annotation to create (e.g., PLOT_SPATIAL)
|
||||||
* @property {Tag[]} tags tags to add to the annotation, e.g., SCIENCE for science related annotations
|
* @property {Tag[]} tags tags to add to the annotation, e.g., SCIENCE for science related annotations
|
||||||
* @property {String} contentText Some text to add to the annotation, e.g. ("This annotation is about science")
|
* @property {String} contentText Some text to add to the annotation, e.g. ("This annotation is about science")
|
||||||
* @property {Object<string, Object>} targets The targets ID keystrings and their specific properties.
|
* @property {Array<Object>} targets The targets ID keystrings and their specific properties.
|
||||||
* For plots, this will be a bounding box, e.g.: {maxY: 100, minY: 0, maxX: 100, minX: 0}
|
* For plots, this will be a bounding box, e.g.: {keyString: "d8385009-789d-457b-acc7-d50ba2fd55ea", maxY: 100, minY: 0, maxX: 100, minX: 0}
|
||||||
* For notebooks, this will be an entry ID, e.g.: {entryId: "entry-ecb158f5-d23c-45e1-a704-649b382622ba"}
|
* For notebooks, this will be an entry ID, e.g.: {entryId: "entry-ecb158f5-d23c-45e1-a704-649b382622ba"}
|
||||||
* @property {DomainObject>} targetDomainObjects the targets ID keystrings and the domain objects this annotation points to (e.g., telemetry objects for a plot)
|
* @property {DomainObject>[]} targetDomainObjects the domain objects this annotation points to (e.g., telemetry objects for a plot)
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @method create
|
* @method create
|
||||||
@ -141,11 +141,15 @@ export default class AnnotationAPI extends EventEmitter {
|
|||||||
throw new Error(`Unknown annotation type: ${annotationType}`);
|
throw new Error(`Unknown annotation type: ${annotationType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Object.keys(targets).length) {
|
if (!targets.length) {
|
||||||
throw new Error(`At least one target is required to create an annotation`);
|
throw new Error(`At least one target is required to create an annotation`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Object.keys(targetDomainObjects).length) {
|
if (targets.some((target) => !target.keyString)) {
|
||||||
|
throw new Error(`All targets require a keyString to create an annotation`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetDomainObjects.length) {
|
||||||
throw new Error(`At least one targetDomainObject is required to create an annotation`);
|
throw new Error(`At least one targetDomainObject is required to create an annotation`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +185,7 @@ export default class AnnotationAPI extends EventEmitter {
|
|||||||
const success = await this.openmct.objects.save(createdObject);
|
const success = await this.openmct.objects.save(createdObject);
|
||||||
if (success) {
|
if (success) {
|
||||||
this.emit('annotationCreated', createdObject);
|
this.emit('annotationCreated', createdObject);
|
||||||
Object.values(targetDomainObjects).forEach((targetDomainObject) => {
|
targetDomainObjects.forEach((targetDomainObject) => {
|
||||||
this.#updateAnnotationModified(targetDomainObject);
|
this.#updateAnnotationModified(targetDomainObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -321,7 +325,10 @@ export default class AnnotationAPI extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#addTagMetaInformationToTags(tags) {
|
#addTagMetaInformationToTags(tags) {
|
||||||
return tags.map((tagKey) => {
|
// Convert to Set and back to Array to remove duplicates
|
||||||
|
const uniqueTags = [...new Set(tags)];
|
||||||
|
|
||||||
|
return uniqueTags.map((tagKey) => {
|
||||||
const tagModel = this.availableTags[tagKey];
|
const tagModel = this.availableTags[tagKey];
|
||||||
tagModel.tagID = tagKey;
|
tagModel.tagID = tagKey;
|
||||||
|
|
||||||
@ -363,7 +370,8 @@ export default class AnnotationAPI extends EventEmitter {
|
|||||||
const modelAddedToResults = await Promise.all(
|
const modelAddedToResults = await Promise.all(
|
||||||
results.map(async (result) => {
|
results.map(async (result) => {
|
||||||
const targetModels = await Promise.all(
|
const targetModels = await Promise.all(
|
||||||
Object.keys(result.targets).map(async (targetID) => {
|
result.targets.map(async (target) => {
|
||||||
|
const targetID = target.keyString;
|
||||||
const targetModel = await this.openmct.objects.get(targetID);
|
const targetModel = await this.openmct.objects.get(targetID);
|
||||||
const targetKeyString = this.openmct.objects.makeKeyString(targetModel.identifier);
|
const targetKeyString = this.openmct.objects.makeKeyString(targetModel.identifier);
|
||||||
const originalPathObjects = await this.openmct.objects.getOriginalPath(targetKeyString);
|
const originalPathObjects = await this.openmct.objects.getOriginalPath(targetKeyString);
|
||||||
@ -410,13 +418,12 @@ export default class AnnotationAPI extends EventEmitter {
|
|||||||
#breakApartSeparateTargets(results) {
|
#breakApartSeparateTargets(results) {
|
||||||
const separateResults = [];
|
const separateResults = [];
|
||||||
results.forEach((result) => {
|
results.forEach((result) => {
|
||||||
Object.keys(result.targets).forEach((targetID) => {
|
result.targets.forEach((target) => {
|
||||||
|
const targetID = target.keyString;
|
||||||
const separatedResult = {
|
const separatedResult = {
|
||||||
...result
|
...result
|
||||||
};
|
};
|
||||||
separatedResult.targets = {
|
separatedResult.targets = [target];
|
||||||
[targetID]: result.targets[targetID]
|
|
||||||
};
|
|
||||||
separatedResult.targetModels = result.targetModels.filter((targetModel) => {
|
separatedResult.targetModels = result.targetModels.filter((targetModel) => {
|
||||||
const targetKeyString = this.openmct.objects.makeKeyString(targetModel.identifier);
|
const targetKeyString = this.openmct.objects.makeKeyString(targetModel.identifier);
|
||||||
|
|
||||||
|
@ -62,11 +62,12 @@ describe('The Annotation API', () => {
|
|||||||
key: 'anAnnotationKey',
|
key: 'anAnnotationKey',
|
||||||
namespace: 'fooNameSpace'
|
namespace: 'fooNameSpace'
|
||||||
},
|
},
|
||||||
targets: {
|
targets: [
|
||||||
'fooNameSpace:some-object': {
|
{
|
||||||
|
keyString: 'fooNameSpace:some-object',
|
||||||
entryId: 'fooBarEntry'
|
entryId: 'fooBarEntry'
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
mockObjectProvider = jasmine.createSpyObj('mock provider', ['create', 'update', 'get']);
|
mockObjectProvider = jasmine.createSpyObj('mock provider', ['create', 'update', 'get']);
|
||||||
@ -121,7 +122,7 @@ describe('The Annotation API', () => {
|
|||||||
tags: ['sometag'],
|
tags: ['sometag'],
|
||||||
contentText: 'fooContext',
|
contentText: 'fooContext',
|
||||||
targetDomainObjects: [mockDomainObject],
|
targetDomainObjects: [mockDomainObject],
|
||||||
targets: { fooTarget: {} }
|
targets: [{ keyString: 'fooTarget' }]
|
||||||
};
|
};
|
||||||
const annotationObject = await openmct.annotation.create(annotationCreationArguments);
|
const annotationObject = await openmct.annotation.create(annotationCreationArguments);
|
||||||
expect(annotationObject).toBeDefined();
|
expect(annotationObject).toBeDefined();
|
||||||
@ -136,7 +137,7 @@ describe('The Annotation API', () => {
|
|||||||
tags: ['sometag'],
|
tags: ['sometag'],
|
||||||
contentText: 'fooContext',
|
contentText: 'fooContext',
|
||||||
targetDomainObjects: [mockDomainObject],
|
targetDomainObjects: [mockDomainObject],
|
||||||
targets: { fooTarget: {} }
|
targets: [{ keyString: 'fooTarget' }]
|
||||||
};
|
};
|
||||||
openmct.annotation.setNamespaceToSaveAnnotations('fooNameSpace');
|
openmct.annotation.setNamespaceToSaveAnnotations('fooNameSpace');
|
||||||
const annotationObject = await openmct.annotation.create(annotationCreationArguments);
|
const annotationObject = await openmct.annotation.create(annotationCreationArguments);
|
||||||
@ -166,7 +167,7 @@ describe('The Annotation API', () => {
|
|||||||
tags: ['sometag'],
|
tags: ['sometag'],
|
||||||
contentText: 'fooContext',
|
contentText: 'fooContext',
|
||||||
targetDomainObjects: [mockDomainObject],
|
targetDomainObjects: [mockDomainObject],
|
||||||
targets: { fooTarget: {} }
|
targets: [{ keyString: 'fooTarget' }]
|
||||||
};
|
};
|
||||||
openmct.annotation.setNamespaceToSaveAnnotations('namespaceThatDoesNotExist');
|
openmct.annotation.setNamespaceToSaveAnnotations('namespaceThatDoesNotExist');
|
||||||
await openmct.annotation.create(annotationCreationArguments);
|
await openmct.annotation.create(annotationCreationArguments);
|
||||||
@ -183,7 +184,7 @@ describe('The Annotation API', () => {
|
|||||||
tags: ['sometag'],
|
tags: ['sometag'],
|
||||||
contentText: 'fooContext',
|
contentText: 'fooContext',
|
||||||
targetDomainObjects: [mockDomainObject],
|
targetDomainObjects: [mockDomainObject],
|
||||||
targets: { fooTarget: {} }
|
targets: [{ keyString: 'fooTarget' }]
|
||||||
};
|
};
|
||||||
openmct.annotation.setNamespaceToSaveAnnotations('immutableProvider');
|
openmct.annotation.setNamespaceToSaveAnnotations('immutableProvider');
|
||||||
await openmct.annotation.create(annotationCreationArguments);
|
await openmct.annotation.create(annotationCreationArguments);
|
||||||
@ -202,7 +203,7 @@ describe('The Annotation API', () => {
|
|||||||
annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK,
|
annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK,
|
||||||
tags: ['aWonderfulTag'],
|
tags: ['aWonderfulTag'],
|
||||||
contentText: 'fooContext',
|
contentText: 'fooContext',
|
||||||
targets: { 'fooNameSpace:some-object': { entryId: 'fooBarEntry' } },
|
targets: [{ keyString: 'fooNameSpace:some-object', entryId: 'fooBarEntry' }],
|
||||||
targetDomainObjects: [mockDomainObject]
|
targetDomainObjects: [mockDomainObject]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -272,17 +273,19 @@ describe('The Annotation API', () => {
|
|||||||
let comparator;
|
let comparator;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
targets = {
|
targets = [
|
||||||
fooTarget: {
|
{
|
||||||
|
keyString: 'fooTarget',
|
||||||
foo: 42
|
foo: 42
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
otherTargets = {
|
otherTargets = [
|
||||||
fooTarget: {
|
{
|
||||||
|
keyString: 'fooTarget',
|
||||||
bar: 42
|
bar: 42
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
comparator = (t1, t2) => t1.fooTarget.foo === t2.fooTarget.bar;
|
comparator = (t1, t2) => t1[0].foo === t2[0].bar;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add a comparator function', () => {
|
it('can add a comparator function', () => {
|
||||||
|
@ -435,7 +435,8 @@ class InMemorySearchProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localIndexAnnotation(objectToIndex, model) {
|
localIndexAnnotation(objectToIndex, model) {
|
||||||
Object.keys(model.targets).forEach((targetID) => {
|
model.targets.forEach((target) => {
|
||||||
|
const targetID = target.keyString;
|
||||||
if (!this.localIndexedAnnotationsByDomainObject[targetID]) {
|
if (!this.localIndexedAnnotationsByDomainObject[targetID]) {
|
||||||
this.localIndexedAnnotationsByDomainObject[targetID] = [];
|
this.localIndexedAnnotationsByDomainObject[targetID] = [];
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
function indexAnnotation(objectToIndex, model) {
|
function indexAnnotation(objectToIndex, model) {
|
||||||
Object.keys(model.targets).forEach((targetID) => {
|
model.targets.forEach((target) => {
|
||||||
|
const targetID = target.keyString;
|
||||||
if (!indexedAnnotationsByDomainObject[targetID]) {
|
if (!indexedAnnotationsByDomainObject[targetID]) {
|
||||||
indexedAnnotationsByDomainObject[targetID] = [];
|
indexedAnnotationsByDomainObject[targetID] = [];
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Flatbush from 'flatbush';
|
import Flatbush from 'flatbush';
|
||||||
|
import { toRaw } from 'vue';
|
||||||
|
|
||||||
const EXISTING_ANNOTATION_STROKE_STYLE = '#D79078';
|
const EXISTING_ANNOTATION_STROKE_STYLE = '#D79078';
|
||||||
const EXISTING_ANNOTATION_FILL_STYLE = 'rgba(202, 202, 142, 0.2)';
|
const EXISTING_ANNOTATION_FILL_STYLE = 'rgba(202, 202, 142, 0.2)';
|
||||||
const SELECTED_ANNOTATION_STROKE_COLOR = '#BD8ECC';
|
const SELECTED_ANNOTATION_STROKE_COLOR = '#BD8ECC';
|
||||||
@ -70,7 +72,9 @@ export default {
|
|||||||
// create a flatbush index for the annotations
|
// create a flatbush index for the annotations
|
||||||
const builtAnnotationsIndex = new Flatbush(this.imageryAnnotations.length);
|
const builtAnnotationsIndex = new Flatbush(this.imageryAnnotations.length);
|
||||||
this.imageryAnnotations.forEach((annotation) => {
|
this.imageryAnnotations.forEach((annotation) => {
|
||||||
const annotationRectangle = annotation.targets[this.keyString].rectangle;
|
const annotationRectangle = annotation.targets.find(
|
||||||
|
(target) => target.keyString === this.keyString
|
||||||
|
)?.rectangle;
|
||||||
const annotationRectangleForPixelDepth =
|
const annotationRectangleForPixelDepth =
|
||||||
this.transformRectangleToPixelDense(annotationRectangle);
|
this.transformRectangleToPixelDense(annotationRectangle);
|
||||||
const indexNumber = builtAnnotationsIndex.add(
|
const indexNumber = builtAnnotationsIndex.add(
|
||||||
@ -141,20 +145,17 @@ export default {
|
|||||||
this.prepareExistingAnnotationSelection(incomingSelectedAnnotations);
|
this.prepareExistingAnnotationSelection(incomingSelectedAnnotations);
|
||||||
},
|
},
|
||||||
prepareExistingAnnotationSelection(annotations) {
|
prepareExistingAnnotationSelection(annotations) {
|
||||||
const targetDomainObjects = {};
|
const targetDetails = [];
|
||||||
targetDomainObjects[this.keyString] = this.domainObject;
|
|
||||||
|
|
||||||
const targetDetails = {};
|
|
||||||
annotations.forEach((annotation) => {
|
annotations.forEach((annotation) => {
|
||||||
Object.entries(annotation.targets).forEach(([key, value]) => {
|
annotation.targets.forEach((target) => {
|
||||||
targetDetails[key] = value;
|
targetDetails.push(toRaw(target));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.selectedAnnotations = annotations;
|
this.selectedAnnotations = annotations;
|
||||||
this.drawAnnotations();
|
this.drawAnnotations();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
targetDomainObjects,
|
targetDomainObjects: [this.domainObject],
|
||||||
targetDetails
|
targetDetails
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -292,9 +293,6 @@ export default {
|
|||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
this.selectedAnnotations = [];
|
this.selectedAnnotations = [];
|
||||||
|
|
||||||
const targetDomainObjects = {};
|
|
||||||
targetDomainObjects[this.keyString] = this.domainObject;
|
|
||||||
const targetDetails = {};
|
|
||||||
const rectangleFromCanvas = {
|
const rectangleFromCanvas = {
|
||||||
x: this.newAnnotationRectangle.x,
|
x: this.newAnnotationRectangle.x,
|
||||||
y: this.newAnnotationRectangle.y,
|
y: this.newAnnotationRectangle.y,
|
||||||
@ -302,13 +300,16 @@ export default {
|
|||||||
height: this.newAnnotationRectangle.height
|
height: this.newAnnotationRectangle.height
|
||||||
};
|
};
|
||||||
const rectangleWithoutPixelScale = this.transformRectangleFromPixelDense(rectangleFromCanvas);
|
const rectangleWithoutPixelScale = this.transformRectangleFromPixelDense(rectangleFromCanvas);
|
||||||
targetDetails[this.keyString] = {
|
const targetDetails = [
|
||||||
rectangle: rectangleWithoutPixelScale,
|
{
|
||||||
time: this.image.time
|
rectangle: rectangleWithoutPixelScale,
|
||||||
};
|
time: this.image.time,
|
||||||
|
keyString: this.keyString
|
||||||
|
}
|
||||||
|
];
|
||||||
this.selectImageAnnotations({
|
this.selectImageAnnotations({
|
||||||
targetDetails,
|
targetDetails,
|
||||||
targetDomainObjects,
|
targetDomainObjects: [this.domainObject],
|
||||||
annotations: []
|
annotations: []
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -403,9 +404,10 @@ export default {
|
|||||||
if (annotation._deleted) {
|
if (annotation._deleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rectangleForPixelDensity = this.transformRectangleToPixelDense(
|
const annotationRectangle = annotation.targets.find(
|
||||||
annotation.targets[this.keyString].rectangle
|
(target) => target.keyString === this.keyString
|
||||||
);
|
)?.rectangle;
|
||||||
|
const rectangleForPixelDensity = this.transformRectangleToPixelDense(annotationRectangle);
|
||||||
if (this.isSelectedAnnotation(annotation)) {
|
if (this.isSelectedAnnotation(annotation)) {
|
||||||
this.drawRectInCanvas(
|
this.drawRectInCanvas(
|
||||||
rectangleForPixelDensity,
|
rectangleForPixelDensity,
|
||||||
|
@ -94,10 +94,10 @@ export default {
|
|||||||
return this?.selection?.[0]?.[0]?.context?.item;
|
return this?.selection?.[0]?.[0]?.context?.item;
|
||||||
},
|
},
|
||||||
targetDetails() {
|
targetDetails() {
|
||||||
return this?.selection?.[0]?.[0]?.context?.targetDetails ?? {};
|
return this?.selection?.[0]?.[0]?.context?.targetDetails ?? [];
|
||||||
},
|
},
|
||||||
shouldShowTagsEditor() {
|
shouldShowTagsEditor() {
|
||||||
const showingTagsEditor = Object.keys(this.targetDetails).length > 0;
|
const showingTagsEditor = this.targetDetails?.length > 0;
|
||||||
|
|
||||||
if (showingTagsEditor) {
|
if (showingTagsEditor) {
|
||||||
return true;
|
return true;
|
||||||
@ -106,7 +106,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
targetDomainObjects() {
|
targetDomainObjects() {
|
||||||
return this?.selection?.[0]?.[0]?.context?.targetDomainObjects ?? {};
|
return this?.selection?.[0]?.[0]?.context?.targetDomainObjects ?? [];
|
||||||
},
|
},
|
||||||
selectedAnnotations() {
|
selectedAnnotations() {
|
||||||
return this?.selection?.[0]?.[0]?.context?.annotations;
|
return this?.selection?.[0]?.[0]?.context?.annotations;
|
||||||
@ -167,9 +167,8 @@ export default {
|
|||||||
this.unobserveEntries = {};
|
this.unobserveEntries = {};
|
||||||
|
|
||||||
this.selection = selection;
|
this.selection = selection;
|
||||||
const targetKeys = Object.keys(this.targetDomainObjects);
|
this.targetDomainObjects.forEach((targetObject) => {
|
||||||
targetKeys.forEach((targetKey) => {
|
const targetKey = targetObject.keyString;
|
||||||
const targetObject = this.targetDomainObjects[targetKey];
|
|
||||||
this.lastLocalAnnotationCreations[targetKey] = targetObject?.annotationLastCreated ?? 0;
|
this.lastLocalAnnotationCreations[targetKey] = targetObject?.annotationLastCreated ?? 0;
|
||||||
if (!this.unobserveEntries[targetKey]) {
|
if (!this.unobserveEntries[targetKey]) {
|
||||||
this.unobserveEntries[targetKey] = this.openmct.objects.observe(
|
this.unobserveEntries[targetKey] = this.openmct.objects.observe(
|
||||||
|
@ -69,12 +69,12 @@ export default {
|
|||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
targets: {
|
targets: {
|
||||||
type: Object,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
targetDomainObjects: {
|
targetDomainObjects: {
|
||||||
type: Object,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
@ -201,11 +201,8 @@ export default {
|
|||||||
const contentText = `${this.annotationType} tag`;
|
const contentText = `${this.annotationType} tag`;
|
||||||
|
|
||||||
// need to get raw version of target domain objects for comparisons to work
|
// need to get raw version of target domain objects for comparisons to work
|
||||||
const rawTargetDomainObjects = {};
|
const rawTargetDomainObjects = this.targetDomainObjects.map((targetDomainObject) => {
|
||||||
Object.keys(this.targetDomainObjects).forEach((targetDomainObjectKey) => {
|
return toRaw(targetDomainObject);
|
||||||
rawTargetDomainObjects[targetDomainObjectKey] = toRaw(
|
|
||||||
this.targetDomainObjects[targetDomainObjectKey]
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
const annotationCreationArguments = {
|
const annotationCreationArguments = {
|
||||||
name: contentText,
|
name: contentText,
|
||||||
|
@ -33,7 +33,9 @@ export default class ExportNotebookAsTextAction {
|
|||||||
getTagsForEntry(entry, domainObjectKeyString, annotations) {
|
getTagsForEntry(entry, domainObjectKeyString, annotations) {
|
||||||
const foundTags = [];
|
const foundTags = [];
|
||||||
annotations.forEach((annotation) => {
|
annotations.forEach((annotation) => {
|
||||||
const target = annotation.targets?.[domainObjectKeyString];
|
const target = annotation.targets.find(
|
||||||
|
(annotationTarget) => annotationTarget.keyString === domainObjectKeyString
|
||||||
|
);
|
||||||
if (target?.entryId === entry.id) {
|
if (target?.entryId === entry.id) {
|
||||||
annotation.tags.forEach((tag) => {
|
annotation.tags.forEach((tag) => {
|
||||||
if (!foundTags.includes(tag)) {
|
if (!foundTags.includes(tag)) {
|
||||||
|
@ -395,8 +395,8 @@ export default {
|
|||||||
);
|
);
|
||||||
|
|
||||||
foundAnnotations.forEach((foundAnnotation) => {
|
foundAnnotations.forEach((foundAnnotation) => {
|
||||||
const targetId = Object.keys(foundAnnotation.targets)[0];
|
const target = foundAnnotation.targets?.[0];
|
||||||
const entryId = foundAnnotation.targets[targetId].entryId;
|
const entryId = target.entryId;
|
||||||
if (!this.notebookAnnotations[entryId]) {
|
if (!this.notebookAnnotations[entryId]) {
|
||||||
this.notebookAnnotations[entryId] = [];
|
this.notebookAnnotations[entryId] = [];
|
||||||
}
|
}
|
||||||
|
@ -51,13 +51,18 @@ async function resolveNotebookTagConflicts(localAnnotation, openmct) {
|
|||||||
throw new Error("Conflict on annotation's tag has different tags than remote");
|
throw new Error("Conflict on annotation's tag has different tags than remote");
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(localClonedAnnotation.targets).forEach((targetKey) => {
|
localClonedAnnotation.targets.forEach((target) => {
|
||||||
if (!remoteMutable.targets[targetKey]) {
|
const targetKey = target.keyString;
|
||||||
|
|
||||||
|
const remoteMutableTarget = remoteMutable.targets.find((remoteTarget) => {
|
||||||
|
return remoteTarget.keyString === targetKey;
|
||||||
|
});
|
||||||
|
if (!remoteMutableTarget) {
|
||||||
throw new Error(`Conflict on annotation's target is missing ${targetKey}`);
|
throw new Error(`Conflict on annotation's target is missing ${targetKey}`);
|
||||||
}
|
}
|
||||||
|
const localMutableTarget = localClonedAnnotation.targets.find((localTarget) => {
|
||||||
const remoteMutableTarget = remoteMutable.targets[targetKey];
|
return localTarget.keyString === targetKey;
|
||||||
const localMutableTarget = localClonedAnnotation.targets[targetKey];
|
});
|
||||||
|
|
||||||
if (remoteMutableTarget.entryId !== localMutableTarget.entryId) {
|
if (remoteMutableTarget.entryId !== localMutableTarget.entryId) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -66,13 +66,14 @@ export function selectEntry({
|
|||||||
onAnnotationChange,
|
onAnnotationChange,
|
||||||
notebookAnnotations
|
notebookAnnotations
|
||||||
}) {
|
}) {
|
||||||
const targetDetails = {};
|
|
||||||
const keyString = openmct.objects.makeKeyString(domainObject.identifier);
|
const keyString = openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
targetDetails[keyString] = {
|
const targetDetails = [
|
||||||
entryId
|
{
|
||||||
};
|
entryId,
|
||||||
const targetDomainObjects = {};
|
keyString
|
||||||
targetDomainObjects[keyString] = domainObject;
|
}
|
||||||
|
];
|
||||||
|
const targetDomainObjects = [domainObject];
|
||||||
openmct.selection.select(
|
openmct.selection.select(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -38,6 +38,7 @@ class CouchObjectProvider {
|
|||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.indicator = indicator;
|
this.indicator = indicator;
|
||||||
this.url = options.url;
|
this.url = options.url;
|
||||||
|
this.useDesignDocuments = options.useDesignDocuments;
|
||||||
this.namespace = namespace;
|
this.namespace = namespace;
|
||||||
this.objectQueue = {};
|
this.objectQueue = {};
|
||||||
this.observers = {};
|
this.observers = {};
|
||||||
@ -187,7 +188,8 @@ class CouchObjectProvider {
|
|||||||
#normalize(options) {
|
#normalize(options) {
|
||||||
if (typeof options === 'string') {
|
if (typeof options === 'string') {
|
||||||
return {
|
return {
|
||||||
url: options
|
url: options,
|
||||||
|
useDesignDocuments: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,6 +438,39 @@ class CouchObjectProvider {
|
|||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getObjectsByView({ designDoc, viewName, keysToSearch }, abortSignal) {
|
||||||
|
const stringifiedKeys = JSON.stringify(keysToSearch);
|
||||||
|
const url = `${this.url}/_design/${designDoc}/_view/${viewName}?keys=${stringifiedKeys}&include_docs=true`;
|
||||||
|
let objectModels = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
signal: abortSignal
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`HTTP request failed with status ${response.status} ${response.statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
const couchRows = result.rows;
|
||||||
|
couchRows.forEach((couchRow) => {
|
||||||
|
const couchDoc = couchRow.doc;
|
||||||
|
const objectModel = this.#getModel(couchDoc);
|
||||||
|
if (objectModel) {
|
||||||
|
objectModels.push(objectModel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
return objectModels;
|
||||||
|
}
|
||||||
|
|
||||||
async getObjectsByFilter(filter, abortSignal) {
|
async getObjectsByFilter(filter, abortSignal) {
|
||||||
let objects = [];
|
let objects = [];
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ class CouchSearchProvider {
|
|||||||
constructor(couchObjectProvider) {
|
constructor(couchObjectProvider) {
|
||||||
this.couchObjectProvider = couchObjectProvider;
|
this.couchObjectProvider = couchObjectProvider;
|
||||||
this.searchTypes = couchObjectProvider.openmct.objects.SEARCH_TYPES;
|
this.searchTypes = couchObjectProvider.openmct.objects.SEARCH_TYPES;
|
||||||
|
this.useDesignDocuments = couchObjectProvider.useDesignDocuments;
|
||||||
this.supportedSearchTypes = [
|
this.supportedSearchTypes = [
|
||||||
this.searchTypes.OBJECTS,
|
this.searchTypes.OBJECTS,
|
||||||
this.searchTypes.ANNOTATIONS,
|
this.searchTypes.ANNOTATIONS,
|
||||||
@ -102,6 +103,25 @@ class CouchSearchProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#bulkAnnotationSearch(batchIdsToSearch) {
|
#bulkAnnotationSearch(batchIdsToSearch) {
|
||||||
|
if (!batchIdsToSearch?.length) {
|
||||||
|
// nothing to search
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastAbortSignal = batchIdsToSearch[batchIdsToSearch.length - 1].abortSignal;
|
||||||
|
|
||||||
|
if (this.useDesignDocuments) {
|
||||||
|
const keysToSearch = batchIdsToSearch.map(({ keyString }) => keyString);
|
||||||
|
return this.couchObjectProvider.getObjectsByView(
|
||||||
|
{
|
||||||
|
designDoc: 'annotation_keystring_index',
|
||||||
|
viewName: 'by_keystring',
|
||||||
|
keysToSearch
|
||||||
|
},
|
||||||
|
lastAbortSignal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
selector: {
|
selector: {
|
||||||
$and: [
|
$and: [
|
||||||
@ -111,25 +131,20 @@ class CouchSearchProvider {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$or: []
|
'model.targets': {
|
||||||
|
$elemMatch: {
|
||||||
|
keyString: {
|
||||||
|
$in: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let lastAbortSignal = null;
|
|
||||||
// TODO: should remove duplicates from batchIds
|
// TODO: should remove duplicates from batchIds
|
||||||
batchIdsToSearch.forEach(({ keyString, abortSignal }) => {
|
batchIdsToSearch.forEach(({ keyString, abortSignal }) => {
|
||||||
const modelFilter = {
|
filter.selector.$and[1]['model.targets'].$elemMatch.keyString.$in.push(keyString);
|
||||||
model: {
|
|
||||||
targets: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
modelFilter.model.targets[keyString] = {
|
|
||||||
$exists: true
|
|
||||||
};
|
|
||||||
|
|
||||||
filter.selector.$and[1].$or.push(modelFilter);
|
|
||||||
lastAbortSignal = abortSignal;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.couchObjectProvider.getObjectsByFilter(filter, lastAbortSignal);
|
return this.couchObjectProvider.getObjectsByFilter(filter, lastAbortSignal);
|
||||||
@ -142,11 +157,7 @@ class CouchSearchProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const returnedData = await this.#bulkPromise;
|
const returnedData = await this.#bulkPromise;
|
||||||
// only return data that matches the keystring
|
return returnedData;
|
||||||
const filteredByKeyString = returnedData.filter((foundAnnotation) => {
|
|
||||||
return foundAnnotation.targets[keyString];
|
|
||||||
});
|
|
||||||
return filteredByKeyString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
searchForTags(tagsArray, abortSignal) {
|
searchForTags(tagsArray, abortSignal) {
|
||||||
@ -154,28 +165,33 @@ class CouchSearchProvider {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.useDesignDocuments) {
|
||||||
|
return this.couchObjectProvider.getObjectsByView(
|
||||||
|
{ designDoc: 'annotation_tags_index', viewName: 'by_tags', keysToSearch: tagsArray },
|
||||||
|
abortSignal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
selector: {
|
selector: {
|
||||||
$and: [
|
$and: [
|
||||||
{
|
{
|
||||||
'model.tags': {
|
'model.type': {
|
||||||
$elemMatch: {
|
$eq: 'annotation'
|
||||||
$or: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'model.type': {
|
'model.tags': {
|
||||||
$eq: 'annotation'
|
$elemMatch: {
|
||||||
|
$in: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
tagsArray.forEach((tag) => {
|
tagsArray.forEach((tag) => {
|
||||||
filter.selector.$and[0]['model.tags'].$elemMatch.$or.push({
|
filter.selector.$and[1]['model.tags'].$elemMatch.$in.push(tag);
|
||||||
$eq: `${tag}`
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.couchObjectProvider.getObjectsByFilter(filter, abortSignal);
|
return this.couchObjectProvider.getObjectsByFilter(filter, abortSignal);
|
||||||
|
@ -141,7 +141,7 @@ sh ./src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.s
|
|||||||
Add a line to install the CouchDB plugin for Open MCT:
|
Add a line to install the CouchDB plugin for Open MCT:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
openmct.install(openmct.plugins.CouchDB("http://localhost:5984/openmct"));
|
openmct.install(openmct.plugins.CouchDB({url: "http://localhost:5984/openmct", useDesignDocuments: false}));
|
||||||
```
|
```
|
||||||
|
|
||||||
# Validating a successful Installation
|
# Validating a successful Installation
|
||||||
@ -151,3 +151,63 @@ sh ./src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.s
|
|||||||
3. Navigate to: <http://127.0.0.1:5984/_utils/#database/openmct/_all_docs>
|
3. Navigate to: <http://127.0.0.1:5984/_utils/#database/openmct/_all_docs>
|
||||||
4. Look at the 'JSON' tab and ensure you can see the specific object you created above.
|
4. Look at the 'JSON' tab and ensure you can see the specific object you created above.
|
||||||
5. All done! 🏆
|
5. All done! 🏆
|
||||||
|
|
||||||
|
# Search Performance
|
||||||
|
|
||||||
|
For large Open MCT installations, it may be helpful to add additional CouchDB capabilities to bear to improve performance.
|
||||||
|
|
||||||
|
## Indexing
|
||||||
|
Indexing the `model.type` field in CouchDB can benefit the performance of queries significantly, particularly if there are a large number of documents in the database. An index can accelerate annotation searches by reducing the number of documents that the database needs to examine.
|
||||||
|
|
||||||
|
To create an index for `model.type`, you can use the following payload:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"index": {
|
||||||
|
"fields": ["model.type", "model.tags"]
|
||||||
|
},
|
||||||
|
"name": "type_tags_index",
|
||||||
|
"type": "json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This instructs CouchDB to create an index on the `model.type` field and the `model.tags` field. Once this index is created, queries that include a selector on `model.type` and `model.tags` (like when searching for tags) can use this index to retrieve results faster.
|
||||||
|
|
||||||
|
You can find more detailed information about indexing in CouchDB in the [official documentation](https://docs.couchdb.org/en/stable/api/database/find.html#db-index).
|
||||||
|
|
||||||
|
## Design Documents
|
||||||
|
|
||||||
|
We can also add a design document for retrieving domain objects for specific tags:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_id": "_design/annotation_tags_index",
|
||||||
|
"views": {
|
||||||
|
"by_tags": {
|
||||||
|
"map": "function (doc) { if (doc.model && doc.model.type === 'annotation' && doc.model.tags) { doc.model.tags.forEach(function (tag) { emit(tag, doc._id); }); } }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
and can be retrieved by issuing a `GET` to http://localhost:5984/openmct/_design/annotation_tags_index/_view/by_tags?keys=["TAG_ID_TO_SEARCH_FOR"]&include_docs=true
|
||||||
|
where `TAG_ID_TO_SEARCH_FOR` is the tag UUID we're looking for.
|
||||||
|
|
||||||
|
and for targets:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"_id": "_design/annotation_keystring_index",
|
||||||
|
"views": {
|
||||||
|
"by_keystring": {
|
||||||
|
"map": "function (doc) { if (doc.model && doc.model.type === 'annotation' && doc.model.targets) { doc.model.targets.forEach(function(target) { if(target.keyString) { emit(target.keyString, doc._id); } }); } }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
and can be retrieved by issuing a `GET` to http://localhost:5984/openmct/_design/annotation_keystring_index/_view/by_keystring?keys=["KEY_STRING_TO_SEARCH_FOR"]&include_docs=true
|
||||||
|
where `KEY_STRING_TO_SEARCH_FOR` is the UUID we're looking for.
|
||||||
|
|
||||||
|
To enable them in Open MCT, we need to configure the plugin `useDesignDocuments` like so:
|
||||||
|
|
||||||
|
```js
|
||||||
|
openmct.install(openmct.plugins.CouchDB({url: "http://localhost:5984/openmct", useDesignDocuments: true}));
|
||||||
|
```
|
||||||
|
@ -476,7 +476,7 @@ export default {
|
|||||||
// the annotations
|
// the annotations
|
||||||
this.freeze();
|
this.freeze();
|
||||||
// just use first annotation
|
// just use first annotation
|
||||||
const boundingBoxes = Object.values(selectedAnnotations[0].targets);
|
const boundingBoxes = selectedAnnotations[0].targets;
|
||||||
let minX = Number.MAX_SAFE_INTEGER;
|
let minX = Number.MAX_SAFE_INTEGER;
|
||||||
let minY = Number.MAX_SAFE_INTEGER;
|
let minY = Number.MAX_SAFE_INTEGER;
|
||||||
let maxX = Number.MIN_SAFE_INTEGER;
|
let maxX = Number.MIN_SAFE_INTEGER;
|
||||||
@ -863,8 +863,8 @@ export default {
|
|||||||
|
|
||||||
marqueeAnnotations(annotationsToSelect) {
|
marqueeAnnotations(annotationsToSelect) {
|
||||||
annotationsToSelect.forEach((annotationToSelect) => {
|
annotationsToSelect.forEach((annotationToSelect) => {
|
||||||
Object.keys(annotationToSelect.targets).forEach((targetKeyString) => {
|
annotationToSelect.targets.forEach((target) => {
|
||||||
const target = annotationToSelect.targets[targetKeyString];
|
const targetKeyString = target.keyString;
|
||||||
const series = this.seriesModels.find(
|
const series = this.seriesModels.find(
|
||||||
(seriesModel) => seriesModel.keyString === targetKeyString
|
(seriesModel) => seriesModel.keyString === targetKeyString
|
||||||
);
|
);
|
||||||
@ -912,17 +912,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
prepareExistingAnnotationSelection(annotations) {
|
prepareExistingAnnotationSelection(annotations) {
|
||||||
const targetDomainObjects = {};
|
const targetDomainObjects = this.config.series.models.map((series) => {
|
||||||
this.config.series.models.forEach((series) => {
|
return series.domainObject;
|
||||||
targetDomainObjects[series.keyString] = series.domainObject;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const targetDetails = {};
|
const targetDetails = [];
|
||||||
const uniqueBoundsAnnotations = [];
|
const uniqueBoundsAnnotations = [];
|
||||||
annotations.forEach((annotation) => {
|
annotations.forEach((annotation) => {
|
||||||
Object.entries(annotation.targets).forEach(([key, value]) => {
|
targetDetails.push(annotation.targets);
|
||||||
targetDetails[key] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
const boundingBoxAlreadyAdded = uniqueBoundsAnnotations.some((existingAnnotation) => {
|
const boundingBoxAlreadyAdded = uniqueBoundsAnnotations.some((existingAnnotation) => {
|
||||||
const existingBoundingBox = Object.values(existingAnnotation.targets)[0];
|
const existingBoundingBox = Object.values(existingAnnotation.targets)[0];
|
||||||
@ -1332,17 +1329,17 @@ export default {
|
|||||||
document.body.addEventListener('click', this.cancelSelection);
|
document.body.addEventListener('click', this.cancelSelection);
|
||||||
},
|
},
|
||||||
selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBoxBySeries, event) {
|
selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBoxBySeries, event) {
|
||||||
let targetDomainObjects = {};
|
let targetDomainObjects = [];
|
||||||
let targetDetails = {};
|
let targetDetails = [];
|
||||||
let annotations = [];
|
let annotations = [];
|
||||||
Object.keys(pointsInBoxBySeries).forEach((seriesKey) => {
|
Object.keys(pointsInBoxBySeries).forEach((seriesKey) => {
|
||||||
const seriesModel = this.getSeries(seriesKey);
|
const seriesModel = this.getSeries(seriesKey);
|
||||||
const boundingBoxWithId = boundingBoxPerYAxis.find(
|
const boundingBoxWithId = boundingBoxPerYAxis.find(
|
||||||
(box) => box.id === seriesModel.get('yAxisId')
|
(box) => box.id === seriesModel.get('yAxisId')
|
||||||
);
|
);
|
||||||
targetDetails[seriesKey] = boundingBoxWithId?.boundingBox;
|
targetDetails.push({ ...boundingBoxWithId?.boundingBox, keyString: seriesKey });
|
||||||
|
|
||||||
targetDomainObjects[seriesKey] = seriesModel.domainObject;
|
targetDomainObjects.push(seriesModel.domainObject);
|
||||||
});
|
});
|
||||||
this.selectPlotAnnotations({
|
this.selectPlotAnnotations({
|
||||||
targetDetails,
|
targetDetails,
|
||||||
@ -1354,8 +1351,8 @@ export default {
|
|||||||
const annotationsBySeries = {};
|
const annotationsBySeries = {};
|
||||||
rawAnnotations.forEach((rawAnnotation) => {
|
rawAnnotations.forEach((rawAnnotation) => {
|
||||||
if (rawAnnotation.targets) {
|
if (rawAnnotation.targets) {
|
||||||
const targetValues = Object.values(rawAnnotation.targets);
|
const targetValues = rawAnnotation.targets;
|
||||||
const targetKeys = Object.keys(rawAnnotation.targets);
|
const targetKeys = rawAnnotation.targets.map((target) => target.keyString);
|
||||||
if (targetValues && targetValues.length) {
|
if (targetValues && targetValues.length) {
|
||||||
let boundingBoxPerYAxis = [];
|
let boundingBoxPerYAxis = [];
|
||||||
targetValues.forEach((boundingBox, index) => {
|
targetValues.forEach((boundingBox, index) => {
|
||||||
|
@ -158,24 +158,14 @@ export default {
|
|||||||
},
|
},
|
||||||
fireAnnotationSelection() {
|
fireAnnotationSelection() {
|
||||||
this.openmct.selection.off('change', this.fireAnnotationSelection);
|
this.openmct.selection.off('change', this.fireAnnotationSelection);
|
||||||
|
|
||||||
const targetDetails = {};
|
|
||||||
const targetDomainObjects = {};
|
|
||||||
Object.entries(this.result.targets).forEach(([key, value]) => {
|
|
||||||
targetDetails[key] = value;
|
|
||||||
});
|
|
||||||
this.result.targetModels.forEach((targetModel) => {
|
|
||||||
const keyString = this.openmct.objects.makeKeyString(targetModel.identifier);
|
|
||||||
targetDomainObjects[keyString] = targetModel;
|
|
||||||
});
|
|
||||||
const selection = [
|
const selection = [
|
||||||
{
|
{
|
||||||
element: this.$el,
|
element: this.$el,
|
||||||
context: {
|
context: {
|
||||||
item: this.result.targetModels[0],
|
item: this.result.targetModels[0],
|
||||||
type: 'annotation-search-result',
|
type: 'annotation-search-result',
|
||||||
targetDetails,
|
targetDetails: this.result.targets,
|
||||||
targetDomainObjects,
|
targetDomainObjects: this.result.targetModels,
|
||||||
annotations: [this.result],
|
annotations: [this.result],
|
||||||
annotationType: this.result.annotationType,
|
annotationType: this.result.annotationType,
|
||||||
onAnnotationChange: () => {}
|
onAnnotationChange: () => {}
|
||||||
|
@ -126,11 +126,12 @@ describe('GrandSearch', () => {
|
|||||||
key: 'anAnnotationKey',
|
key: 'anAnnotationKey',
|
||||||
namespace: 'fooNameSpace'
|
namespace: 'fooNameSpace'
|
||||||
},
|
},
|
||||||
targets: {
|
targets: [
|
||||||
'fooNameSpace:some-object': {
|
{
|
||||||
|
keyString: 'fooNameSpace:some-object',
|
||||||
entryId: 'fooBarEntry'
|
entryId: 'fooBarEntry'
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
};
|
};
|
||||||
mockNewObject = {
|
mockNewObject = {
|
||||||
type: 'folder',
|
type: 'folder',
|
||||||
|
Loading…
Reference in New Issue
Block a user