mirror of
https://github.com/nasa/openmct.git
synced 2024-12-23 15:02:23 +00:00
Merge remote-tracking branch 'origin/master' into table-export-934
This commit is contained in:
commit
ea676b4368
@ -27,6 +27,7 @@ define([
|
||||
"./src/MenuArrowController",
|
||||
"./src/navigation/NavigationService",
|
||||
"./src/navigation/NavigateAction",
|
||||
"./src/navigation/OrphanNavigationHandler",
|
||||
"./src/windowing/NewTabAction",
|
||||
"./src/windowing/FullscreenAction",
|
||||
"./src/windowing/WindowTitler",
|
||||
@ -47,6 +48,7 @@ define([
|
||||
MenuArrowController,
|
||||
NavigationService,
|
||||
NavigateAction,
|
||||
OrphanNavigationHandler,
|
||||
NewTabAction,
|
||||
FullscreenAction,
|
||||
WindowTitler,
|
||||
@ -253,6 +255,14 @@ define([
|
||||
"$rootScope",
|
||||
"$document"
|
||||
]
|
||||
},
|
||||
{
|
||||
"implementation": OrphanNavigationHandler,
|
||||
"depends": [
|
||||
"throttle",
|
||||
"topic",
|
||||
"navigationService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
|
@ -0,0 +1,75 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
|
||||
/**
|
||||
* Navigates away from orphan objects whenever they are detected.
|
||||
*
|
||||
* An orphan object is an object whose apparent parent does not
|
||||
* actually contain it. This may occur in certain circumstances, such
|
||||
* as when persistence succeeds for a newly-created object but fails
|
||||
* for its parent.
|
||||
*
|
||||
* @param throttle the `throttle` service
|
||||
* @param topic the `topic` service
|
||||
* @param navigationService the `navigationService`
|
||||
* @constructor
|
||||
*/
|
||||
function OrphanNavigationHandler(throttle, topic, navigationService) {
|
||||
var throttledCheckNavigation;
|
||||
|
||||
function getParent(domainObject) {
|
||||
var context = domainObject.getCapability('context');
|
||||
return context.getParent();
|
||||
}
|
||||
|
||||
function isOrphan(domainObject) {
|
||||
var parent = getParent(domainObject),
|
||||
composition = parent.getModel().composition,
|
||||
id = domainObject.getId();
|
||||
return !composition || (composition.indexOf(id) === -1);
|
||||
}
|
||||
|
||||
function navigateToParent(domainObject) {
|
||||
var parent = getParent(domainObject);
|
||||
return parent.getCapability('action').perform('navigate');
|
||||
}
|
||||
|
||||
function checkNavigation() {
|
||||
var navigatedObject = navigationService.getNavigation();
|
||||
if (navigatedObject.hasCapability('context') &&
|
||||
isOrphan(navigatedObject)) {
|
||||
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
|
||||
navigateToParent(navigatedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throttledCheckNavigation = throttle(checkNavigation);
|
||||
|
||||
navigationService.addListener(throttledCheckNavigation);
|
||||
topic('mutation').listen(throttledCheckNavigation);
|
||||
}
|
||||
|
||||
return OrphanNavigationHandler;
|
||||
});
|
@ -0,0 +1,180 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'../../src/navigation/OrphanNavigationHandler'
|
||||
], function (OrphanNavigationHandler) {
|
||||
describe("OrphanNavigationHandler", function () {
|
||||
var mockTopic,
|
||||
mockThrottle,
|
||||
mockMutationTopic,
|
||||
mockNavigationService,
|
||||
mockDomainObject,
|
||||
mockParentObject,
|
||||
mockContext,
|
||||
mockActionCapability,
|
||||
mockEditor,
|
||||
testParentModel,
|
||||
testId,
|
||||
mockThrottledFns;
|
||||
|
||||
beforeEach(function () {
|
||||
testId = 'some-identifier';
|
||||
|
||||
mockThrottledFns = [];
|
||||
testParentModel = {};
|
||||
|
||||
mockTopic = jasmine.createSpy('topic');
|
||||
mockThrottle = jasmine.createSpy('throttle');
|
||||
mockNavigationService = jasmine.createSpyObj('navigationService', [
|
||||
'getNavigation',
|
||||
'addListener'
|
||||
]);
|
||||
mockMutationTopic = jasmine.createSpyObj('mutationTopic', [
|
||||
'listen'
|
||||
]);
|
||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getId',
|
||||
'getCapability',
|
||||
'getModel',
|
||||
'hasCapability'
|
||||
]);
|
||||
mockParentObject = jasmine.createSpyObj('domainObject', [
|
||||
'getId',
|
||||
'getCapability',
|
||||
'getModel',
|
||||
'hasCapability'
|
||||
]);
|
||||
mockContext = jasmine.createSpyObj('context', ['getParent']);
|
||||
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
|
||||
mockEditor = jasmine.createSpyObj('editor', ['isEditContextRoot']);
|
||||
|
||||
mockThrottle.andCallFake(function (fn) {
|
||||
var mockThrottledFn =
|
||||
jasmine.createSpy('throttled-' + mockThrottledFns.length);
|
||||
mockThrottledFn.andCallFake(fn);
|
||||
mockThrottledFns.push(mockThrottledFn);
|
||||
return mockThrottledFn;
|
||||
});
|
||||
mockTopic.andCallFake(function (k) {
|
||||
return k === 'mutation' && mockMutationTopic;
|
||||
});
|
||||
mockDomainObject.getId.andReturn(testId);
|
||||
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||
return {
|
||||
context: mockContext,
|
||||
editor: mockEditor
|
||||
}[c];
|
||||
});
|
||||
mockDomainObject.hasCapability.andCallFake(function (c) {
|
||||
return !!mockDomainObject.getCapability(c);
|
||||
});
|
||||
mockParentObject.getModel.andReturn(testParentModel);
|
||||
mockParentObject.getCapability.andCallFake(function (c) {
|
||||
return {
|
||||
action: mockActionCapability
|
||||
}[c];
|
||||
});
|
||||
mockContext.getParent.andReturn(mockParentObject);
|
||||
mockNavigationService.getNavigation.andReturn(mockDomainObject);
|
||||
mockEditor.isEditContextRoot.andReturn(false);
|
||||
|
||||
return new OrphanNavigationHandler(
|
||||
mockThrottle,
|
||||
mockTopic,
|
||||
mockNavigationService
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it("listens for mutation with a throttled function", function () {
|
||||
expect(mockMutationTopic.listen)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
expect(mockThrottledFns.indexOf(
|
||||
mockMutationTopic.listen.mostRecentCall.args[0]
|
||||
)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("listens for navigation changes with a throttled function", function () {
|
||||
expect(mockNavigationService.addListener)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
expect(mockThrottledFns.indexOf(
|
||||
mockNavigationService.addListener.mostRecentCall.args[0]
|
||||
)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
[false, true].forEach(function (isOrphan) {
|
||||
var prefix = isOrphan ? "" : "non-";
|
||||
describe("for " + prefix + "orphan objects", function () {
|
||||
beforeEach(function () {
|
||||
testParentModel.composition = isOrphan ? [] : [testId];
|
||||
});
|
||||
|
||||
[false, true].forEach(function (isEditRoot) {
|
||||
var caseName = isEditRoot ?
|
||||
"that are being edited" : "that are not being edited";
|
||||
|
||||
function itNavigatesAsExpected() {
|
||||
if (isOrphan && !isEditRoot) {
|
||||
it("navigates to the parent", function () {
|
||||
expect(mockActionCapability.perform)
|
||||
.toHaveBeenCalledWith('navigate');
|
||||
});
|
||||
} else {
|
||||
it("does nothing", function () {
|
||||
expect(mockActionCapability.perform)
|
||||
.not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe(caseName, function () {
|
||||
beforeEach(function () {
|
||||
mockEditor.isEditContextRoot.andReturn(isEditRoot);
|
||||
});
|
||||
|
||||
describe("when navigation changes", function () {
|
||||
beforeEach(function () {
|
||||
mockNavigationService.addListener.mostRecentCall
|
||||
.args[0](mockDomainObject);
|
||||
});
|
||||
|
||||
itNavigatesAsExpected();
|
||||
});
|
||||
|
||||
describe("when mutation occurs", function () {
|
||||
beforeEach(function () {
|
||||
mockMutationTopic.listen.mostRecentCall
|
||||
.args[0](mockParentObject);
|
||||
});
|
||||
|
||||
itNavigatesAsExpected();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -253,7 +253,7 @@ define([
|
||||
},
|
||||
{
|
||||
"category": "navigation",
|
||||
"message": "There are unsaved changes.",
|
||||
"message": "Continuing will cause the loss of any unsaved changes.",
|
||||
"implementation": EditNavigationPolicy
|
||||
},
|
||||
{
|
||||
|
@ -288,8 +288,9 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
||||
|
||||
.left {
|
||||
padding-right: $interiorMarginLg;
|
||||
.l-back:not(.s-status-editing) {
|
||||
.l-back {
|
||||
margin-right: $interiorMarginLg;
|
||||
&.s-status-editing { display: none; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,12 @@ define([
|
||||
"name": "Export Timeline as CSV",
|
||||
"category": "contextual",
|
||||
"implementation": ExportTimelineAsCSVAction,
|
||||
"depends": ["exportService", "notificationService"]
|
||||
"depends": [
|
||||
"$log",
|
||||
"exportService",
|
||||
"notificationService",
|
||||
"resources[]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
|
@ -1,16 +1,22 @@
|
||||
.l-timeline-gantt {
|
||||
min-width: 2px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: $timelineSwimlaneGanttVM; bottom: $timelineSwimlaneGanttVM;
|
||||
|
||||
.bar {
|
||||
@include ellipsize();
|
||||
height: $activityBarH;
|
||||
line-height: $activityBarH + 2;
|
||||
line-height: $activityBarH;
|
||||
padding: 0 $interiorMargin;
|
||||
|
||||
span {
|
||||
display: inline;
|
||||
$iconW: 20px;
|
||||
@include absPosDefault();
|
||||
display: block;
|
||||
&.s-activity-type {
|
||||
right: auto; width: $iconW;
|
||||
text-align: center;
|
||||
&.timeline {
|
||||
&:before {
|
||||
content:"S";
|
||||
@ -23,7 +29,9 @@
|
||||
}
|
||||
}
|
||||
&.s-title {
|
||||
text-shadow: rgba(black, 0.1) 0 1px 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
left: $iconW;
|
||||
}
|
||||
&.duration {
|
||||
left: auto;
|
||||
@ -52,6 +60,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
&.sm .bar span {
|
||||
// Hide icon and label if width is too small
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-mode .s-timeline-gantt,
|
||||
@ -59,7 +71,7 @@
|
||||
.handle {
|
||||
cursor: col-resize;
|
||||
&.mid {
|
||||
cursor: move;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
}
|
||||
}
|
@ -32,20 +32,10 @@
|
||||
}
|
||||
|
||||
.s-timeline-gantt {
|
||||
$br: $controlCr;
|
||||
.bar {
|
||||
color: $colorGanttBarFg;
|
||||
@include activityBg($colorGanttBarBg);
|
||||
border-radius: $br;
|
||||
box-shadow: $shdwGanttBar;
|
||||
&.expanded {
|
||||
@include border-top-radius($br);
|
||||
@include border-bottom-radius(0);
|
||||
}
|
||||
&.leaf {
|
||||
@include border-top-radius(0);
|
||||
@include border-bottom-radius($br);
|
||||
}
|
||||
.s-toggle {
|
||||
color: $colorGanttToggle;
|
||||
}
|
||||
|
@ -52,6 +52,9 @@
|
||||
// Tree area with item title
|
||||
right: auto; // Set this to auto and uncomment width below when additional tabular columns are added
|
||||
width: $timelineTabularTitleW;
|
||||
.l-swimlanes-holder {
|
||||
bottom: $scrollbarTrackSize;
|
||||
}
|
||||
}
|
||||
&.l-tabular-r {
|
||||
// Start, end, duration, activity modes columns
|
||||
@ -67,6 +70,7 @@
|
||||
&.l-timeline-gantt {
|
||||
.l-swimlanes-holder {
|
||||
@include scrollV(scroll);
|
||||
bottom: $scrollbarTrackSize;
|
||||
}
|
||||
}
|
||||
&.l-timeline-resource-legend {
|
||||
|
@ -20,6 +20,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="t-timeline-gantt l-timeline-gantt s-timeline-gantt"
|
||||
ng-class="{ sm: gantt.width(timespan, parameters.scroll, parameters.toPixels) < 25 }"
|
||||
title="{{model.name}}"
|
||||
ng-controller="TimelineGanttController as gantt"
|
||||
ng-style="timespan ? {
|
||||
|
@ -27,11 +27,15 @@ define([], function () {
|
||||
* in a domain object's composition.
|
||||
* @param {number} index the zero-based index of the composition
|
||||
* element associated with this column
|
||||
* @param idMap an object containing key value pairs, where keys
|
||||
* are domain object identifiers and values are whatever
|
||||
* should appear in CSV output in their place
|
||||
* @constructor
|
||||
* @implements {platform/features/timeline.TimelineCSVColumn}
|
||||
*/
|
||||
function CompositionColumn(index) {
|
||||
function CompositionColumn(index, idMap) {
|
||||
this.index = index;
|
||||
this.idMap = idMap;
|
||||
}
|
||||
|
||||
CompositionColumn.prototype.name = function () {
|
||||
@ -41,7 +45,9 @@ define([], function () {
|
||||
CompositionColumn.prototype.value = function (domainObject) {
|
||||
var model = domainObject.getModel(),
|
||||
composition = model.composition || [];
|
||||
return (composition[this.index]) || "";
|
||||
|
||||
return composition.length > this.index ?
|
||||
this.idMap[composition[this.index]] : "";
|
||||
};
|
||||
|
||||
return CompositionColumn;
|
||||
|
@ -27,14 +27,23 @@ define(["./ExportTimelineAsCSVTask"], function (ExportTimelineAsCSVTask) {
|
||||
*
|
||||
* @param exportService the service used to perform the CSV export
|
||||
* @param notificationService the service used to show notifications
|
||||
* @param {Array} resources an array of `resources` extensions
|
||||
* @param context the Action's context
|
||||
* @implements {Action}
|
||||
* @constructor
|
||||
* @memberof {platform/features/timeline}
|
||||
*/
|
||||
function ExportTimelineAsCSVAction(exportService, notificationService, context) {
|
||||
function ExportTimelineAsCSVAction(
|
||||
$log,
|
||||
exportService,
|
||||
notificationService,
|
||||
resources,
|
||||
context
|
||||
) {
|
||||
this.$log = $log;
|
||||
this.task = new ExportTimelineAsCSVTask(
|
||||
exportService,
|
||||
resources,
|
||||
context.domainObject
|
||||
);
|
||||
this.notificationService = notificationService;
|
||||
@ -45,13 +54,15 @@ define(["./ExportTimelineAsCSVTask"], function (ExportTimelineAsCSVTask) {
|
||||
notification = notificationService.notify({
|
||||
title: "Exporting CSV",
|
||||
unknownProgress: true
|
||||
});
|
||||
}),
|
||||
$log = this.$log;
|
||||
|
||||
return this.task.run()
|
||||
.then(function () {
|
||||
notification.dismiss();
|
||||
})
|
||||
.catch(function () {
|
||||
.catch(function (err) {
|
||||
$log.warn(err);
|
||||
notification.dismiss();
|
||||
notificationService.error("Error exporting CSV");
|
||||
});
|
||||
|
@ -35,11 +35,13 @@ define([
|
||||
* @constructor
|
||||
* @memberof {platform/features/timeline}
|
||||
* @param exportService the service used to export as CSV
|
||||
* @param resources the `resources` extension category
|
||||
* @param {DomainObject} domainObject the timeline being exported
|
||||
*/
|
||||
function ExportTimelineAsCSVTask(exportService, domainObject) {
|
||||
function ExportTimelineAsCSVTask(exportService, resources, domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.exportService = exportService;
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,9 +52,10 @@ define([
|
||||
*/
|
||||
ExportTimelineAsCSVTask.prototype.run = function () {
|
||||
var exportService = this.exportService;
|
||||
var resources = this.resources;
|
||||
|
||||
function doExport(objects) {
|
||||
var exporter = new TimelineColumnizer(objects),
|
||||
var exporter = new TimelineColumnizer(objects, resources),
|
||||
options = { headers: exporter.headers() };
|
||||
return exporter.rows().then(function (rows) {
|
||||
return exportService.exportCSV(rows, options);
|
||||
|
@ -23,19 +23,23 @@
|
||||
define([], function () {
|
||||
|
||||
/**
|
||||
* A column showing domain object identifiers.
|
||||
* A column showing identifying domain objects.
|
||||
* @constructor
|
||||
* @param idMap an object containing key value pairs, where keys
|
||||
* are domain object identifiers and values are whatever
|
||||
* should appear in CSV output in their place
|
||||
* @implements {platform/features/timeline.TimelineCSVColumn}
|
||||
*/
|
||||
function IdColumn() {
|
||||
function IdColumn(idMap) {
|
||||
this.idMap = idMap;
|
||||
}
|
||||
|
||||
IdColumn.prototype.name = function () {
|
||||
return "Identifier";
|
||||
return "Index";
|
||||
};
|
||||
|
||||
IdColumn.prototype.value = function (domainObject) {
|
||||
return domainObject.getId();
|
||||
return this.idMap[domainObject.getId()];
|
||||
};
|
||||
|
||||
return IdColumn;
|
||||
|
@ -27,10 +27,14 @@ define([], function () {
|
||||
* @constructor
|
||||
* @param {number} index the zero-based index of the composition
|
||||
* element associated with this column
|
||||
* @param idMap an object containing key value pairs, where keys
|
||||
* are domain object identifiers and values are whatever
|
||||
* should appear in CSV output in their place
|
||||
* @implements {platform/features/timeline.TimelineCSVColumn}
|
||||
*/
|
||||
function ModeColumn(index) {
|
||||
function ModeColumn(index, idMap) {
|
||||
this.index = index;
|
||||
this.idMap = idMap;
|
||||
}
|
||||
|
||||
ModeColumn.prototype.name = function () {
|
||||
@ -39,8 +43,9 @@ define([], function () {
|
||||
|
||||
ModeColumn.prototype.value = function (domainObject) {
|
||||
var model = domainObject.getModel(),
|
||||
composition = (model.relationships || {}).modes || [];
|
||||
return (composition[this.index]) || "";
|
||||
modes = (model.relationships || {}).modes || [];
|
||||
return modes.length > this.index ?
|
||||
this.idMap[modes[this.index]] : "";
|
||||
};
|
||||
|
||||
return ModeColumn;
|
||||
|
@ -25,13 +25,15 @@ define([
|
||||
"./ModeColumn",
|
||||
"./CompositionColumn",
|
||||
"./MetadataColumn",
|
||||
"./TimespanColumn"
|
||||
"./TimespanColumn",
|
||||
"./UtilizationColumn"
|
||||
], function (
|
||||
IdColumn,
|
||||
ModeColumn,
|
||||
CompositionColumn,
|
||||
MetadataColumn,
|
||||
TimespanColumn
|
||||
TimespanColumn,
|
||||
UtilizationColumn
|
||||
) {
|
||||
|
||||
/**
|
||||
@ -63,15 +65,17 @@ define([
|
||||
*
|
||||
* @param {DomainObject[]} domainObjects the objects to include
|
||||
* in the exported data
|
||||
* @param {Array} resources an array of `resources` extensions
|
||||
* @constructor
|
||||
* @memberof {platform/features/timeline}
|
||||
*/
|
||||
function TimelineColumnizer(domainObjects) {
|
||||
function TimelineColumnizer(domainObjects, resources) {
|
||||
var maxComposition = 0,
|
||||
maxRelationships = 0,
|
||||
columnNames = {},
|
||||
columns = [],
|
||||
foundTimespan = false,
|
||||
idMap,
|
||||
i;
|
||||
|
||||
function addMetadataProperty(property) {
|
||||
@ -82,7 +86,12 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
columns.push(new IdColumn());
|
||||
idMap = domainObjects.reduce(function (map, domainObject, index) {
|
||||
map[domainObject.getId()] = index + 1;
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
columns.push(new IdColumn(idMap));
|
||||
|
||||
domainObjects.forEach(function (domainObject) {
|
||||
var model = domainObject.getModel(),
|
||||
@ -113,12 +122,16 @@ define([
|
||||
columns.push(new TimespanColumn(false));
|
||||
}
|
||||
|
||||
resources.forEach(function (resource) {
|
||||
columns.push(new UtilizationColumn(resource));
|
||||
});
|
||||
|
||||
for (i = 0; i < maxComposition; i += 1) {
|
||||
columns.push(new CompositionColumn(i));
|
||||
columns.push(new CompositionColumn(i, idMap));
|
||||
}
|
||||
|
||||
for (i = 0; i < maxRelationships; i += 1) {
|
||||
columns.push(new ModeColumn(i));
|
||||
columns.push(new ModeColumn(i, idMap));
|
||||
}
|
||||
|
||||
this.domainObjects = domainObjects;
|
||||
|
72
platform/features/timeline/src/actions/UtilizationColumn.js
Normal file
72
platform/features/timeline/src/actions/UtilizationColumn.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2009-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
/**
|
||||
* A column showing utilization costs associated with activities.
|
||||
* @constructor
|
||||
* @param {string} key the key for the particular cost
|
||||
* @implements {platform/features/timeline.TimelineCSVColumn}
|
||||
*/
|
||||
function UtilizationColumn(resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
UtilizationColumn.prototype.name = function () {
|
||||
var units = {
|
||||
"Kbps": "Kb",
|
||||
"watts": "watt-seconds"
|
||||
}[this.resource.units] || "unknown units";
|
||||
|
||||
return this.resource.name + " (" + units + ")";
|
||||
};
|
||||
|
||||
UtilizationColumn.prototype.value = function (domainObject) {
|
||||
var resource = this.resource;
|
||||
|
||||
function getCost(utilization) {
|
||||
var seconds = (utilization.end - utilization.start) / 1000;
|
||||
return seconds * utilization.value;
|
||||
}
|
||||
|
||||
function getUtilizationValue(utilizations) {
|
||||
utilizations = utilizations.filter(function (utilization) {
|
||||
return utilization.key === resource.key;
|
||||
});
|
||||
|
||||
if (utilizations.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return utilizations.map(getCost).reduce(function (a, b) {
|
||||
return a + b;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return domainObject.hasCapability('utilization') ?
|
||||
domainObject.getCapability('utilization').internal()
|
||||
.then(getUtilizationValue) :
|
||||
"";
|
||||
};
|
||||
|
||||
return UtilizationColumn;
|
||||
});
|
@ -193,6 +193,13 @@ define(
|
||||
* @returns {Promise.<string[]>} a promise for resource identifiers
|
||||
*/
|
||||
resources: promiseResourceKeys,
|
||||
/**
|
||||
* Get the resource utilization associated with this object
|
||||
* directly, not including any resource utilization associated
|
||||
* with contained objects.
|
||||
* @returns {Promise.<Array>}
|
||||
*/
|
||||
internal: promiseInternalUtilization,
|
||||
/**
|
||||
* Get the resource utilization associated with this
|
||||
* object. Results are not sorted. This requires looking
|
||||
|
@ -23,13 +23,20 @@
|
||||
define(
|
||||
['../../src/actions/CompositionColumn'],
|
||||
function (CompositionColumn) {
|
||||
var TEST_IDS = ['a', 'b', 'c', 'd', 'e', 'f'];
|
||||
|
||||
describe("CompositionColumn", function () {
|
||||
var testIndex,
|
||||
testIdMap,
|
||||
column;
|
||||
|
||||
beforeEach(function () {
|
||||
testIndex = 3;
|
||||
column = new CompositionColumn(testIndex);
|
||||
testIdMap = TEST_IDS.reduce(function (map, id, index) {
|
||||
map[id] = index;
|
||||
return map;
|
||||
}, {});
|
||||
column = new CompositionColumn(testIndex, testIdMap);
|
||||
});
|
||||
|
||||
it("includes a one-based index in its name", function () {
|
||||
@ -46,15 +53,13 @@ define(
|
||||
'domainObject',
|
||||
['getId', 'getModel', 'getCapability']
|
||||
);
|
||||
testModel = {
|
||||
composition: ['a', 'b', 'c', 'd', 'e', 'f']
|
||||
};
|
||||
testModel = { composition: TEST_IDS };
|
||||
mockDomainObject.getModel.andReturn(testModel);
|
||||
});
|
||||
|
||||
it("returns a corresponding identifier", function () {
|
||||
it("returns a corresponding value from the map", function () {
|
||||
expect(column.value(mockDomainObject))
|
||||
.toEqual(testModel.composition[testIndex]);
|
||||
.toEqual(testIdMap[testModel.composition[testIndex]]);
|
||||
});
|
||||
|
||||
it("returns nothing when composition is exceeded", function () {
|
||||
|
@ -24,7 +24,8 @@ define(
|
||||
['../../src/actions/ExportTimelineAsCSVAction'],
|
||||
function (ExportTimelineAsCSVAction) {
|
||||
describe("ExportTimelineAsCSVAction", function () {
|
||||
var mockExportService,
|
||||
var mockLog,
|
||||
mockExportService,
|
||||
mockNotificationService,
|
||||
mockNotification,
|
||||
mockDomainObject,
|
||||
@ -39,6 +40,13 @@ define(
|
||||
['getId', 'getModel', 'getCapability', 'hasCapability']
|
||||
);
|
||||
mockType = jasmine.createSpyObj('type', ['instanceOf']);
|
||||
|
||||
mockLog = jasmine.createSpyObj('$log', [
|
||||
'warn',
|
||||
'error',
|
||||
'info',
|
||||
'debug'
|
||||
]);
|
||||
mockExportService = jasmine.createSpyObj(
|
||||
'exportService',
|
||||
['exportCSV']
|
||||
@ -63,8 +71,10 @@ define(
|
||||
testContext = { domainObject: mockDomainObject };
|
||||
|
||||
action = new ExportTimelineAsCSVAction(
|
||||
mockLog,
|
||||
mockExportService,
|
||||
mockNotificationService,
|
||||
[],
|
||||
testContext
|
||||
);
|
||||
});
|
||||
@ -129,8 +139,11 @@ define(
|
||||
});
|
||||
|
||||
describe("and an error occurs", function () {
|
||||
var testError;
|
||||
|
||||
beforeEach(function () {
|
||||
testPromise.reject();
|
||||
testError = { someProperty: "some value" };
|
||||
testPromise.reject(testError);
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
});
|
||||
@ -145,6 +158,10 @@ define(
|
||||
expect(mockNotificationService.error)
|
||||
.toHaveBeenCalledWith(jasmine.any(String));
|
||||
});
|
||||
|
||||
it("logs the root cause", function () {
|
||||
expect(mockLog.warn).toHaveBeenCalledWith(testError);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -52,6 +52,7 @@ define(
|
||||
|
||||
task = new ExportTimelineAsCSVTask(
|
||||
mockExportService,
|
||||
[],
|
||||
mockDomainObject
|
||||
);
|
||||
});
|
||||
|
@ -24,10 +24,12 @@ define(
|
||||
['../../src/actions/IdColumn'],
|
||||
function (IdColumn) {
|
||||
describe("IdColumn", function () {
|
||||
var column;
|
||||
var testIdMap,
|
||||
column;
|
||||
|
||||
beforeEach(function () {
|
||||
column = new IdColumn();
|
||||
testIdMap = { "foo": "bar" };
|
||||
column = new IdColumn(testIdMap);
|
||||
});
|
||||
|
||||
it("has a name", function () {
|
||||
@ -47,9 +49,9 @@ define(
|
||||
mockDomainObject.getId.andReturn(testId);
|
||||
});
|
||||
|
||||
it("provides a domain object's identifier", function () {
|
||||
it("provides a value mapped from domain object's identifier", function () {
|
||||
expect(column.value(mockDomainObject))
|
||||
.toEqual(testId);
|
||||
.toEqual(testIdMap[testId]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -23,13 +23,20 @@
|
||||
define(
|
||||
['../../src/actions/ModeColumn'],
|
||||
function (ModeColumn) {
|
||||
var TEST_IDS = ['a', 'b', 'c', 'd', 'e', 'f'];
|
||||
|
||||
describe("ModeColumn", function () {
|
||||
var testIndex,
|
||||
testIdMap,
|
||||
column;
|
||||
|
||||
beforeEach(function () {
|
||||
testIndex = 3;
|
||||
column = new ModeColumn(testIndex);
|
||||
testIdMap = TEST_IDS.reduce(function (map, id, index) {
|
||||
map[id] = index;
|
||||
return map;
|
||||
}, {});
|
||||
column = new ModeColumn(testIndex, testIdMap);
|
||||
});
|
||||
|
||||
it("includes a one-based index in its name", function () {
|
||||
@ -48,15 +55,15 @@ define(
|
||||
);
|
||||
testModel = {
|
||||
relationships: {
|
||||
modes: ['a', 'b', 'c', 'd', 'e', 'f']
|
||||
modes: TEST_IDS
|
||||
}
|
||||
};
|
||||
mockDomainObject.getModel.andReturn(testModel);
|
||||
});
|
||||
|
||||
it("returns a corresponding identifier", function () {
|
||||
it("returns a corresponding value from the map", function () {
|
||||
expect(column.value(mockDomainObject))
|
||||
.toEqual(testModel.relationships.modes[testIndex]);
|
||||
.toEqual(testIdMap[testModel.relationships.modes[testIndex]]);
|
||||
});
|
||||
|
||||
it("returns nothing when relationships are exceeded", function () {
|
||||
|
@ -75,7 +75,7 @@ define(
|
||||
return c === 'metadata' && testMetadata;
|
||||
});
|
||||
|
||||
exporter = new TimelineColumnizer(mockDomainObjects);
|
||||
exporter = new TimelineColumnizer(mockDomainObjects, []);
|
||||
});
|
||||
|
||||
describe("rows", function () {
|
||||
@ -94,13 +94,6 @@ define(
|
||||
it("include one row per domain object", function () {
|
||||
expect(rows.length).toEqual(mockDomainObjects.length);
|
||||
});
|
||||
|
||||
it("includes identifiers for each domain object", function () {
|
||||
rows.forEach(function (row, index) {
|
||||
var id = mockDomainObjects[index].getId();
|
||||
expect(row.indexOf(id)).not.toEqual(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("headers", function () {
|
||||
|
Loading…
Reference in New Issue
Block a user