-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* WIP Add initial commit of DatasetExplorer * WIP use artifact name in DataExplorer * WIP Add plot editor to dataset explorer * WIP add PlotEditor and css * Add UI elements for adding/removing data * Fix UI issue with editing plotted data * WIP Add utility for parsing python slices * Add slice string validation * UI support for validating data slice * Update figure updates given the figure data * Add explorer_helpers.py for DatasetExplorer * Add explorer helpers to session for getting metadata * dynamically create variable names for dropdown * Add tests for variable name creation from metadata * Plot actual data selected from "Add Data" button * Fix plot height * Add basic 3D plot support to DataExplorer * Fix async issue with multiple lines * Fix x-axis, y-axis labels * Add validation for plotting data * Remove .only from PythonSlice test suite * Add basic artifact loader to DatasetExplorer * Only show artifacts with data in artifact loader * Update metadata on artifact load into session * Removed hardcoded examples from html * Add some support for colors (uniform only) * Add color support for individual points * Fix selection of keys from artifacts * WIP Use session with queue in dataset explorer * WIP code cleanup dataset explorer * Add compute creation (and shield) for DatasetExplorer * Don't show slice syntax errors until change event * Fix artifacts with extensions. minor code cleanup * Increase territory for access to initialization code (custom serializer support) * Rename DatasetVisualizer -> TensorPlotter * Rename scss,css files * Add "save" action to floating action button * Only load jscolor in the browser * Skip jscolor library when linting * Add InteractiveEditor base class * Update to use InteractiveEditor base classes * Add getSnapshot to tensor plotter * Fix setting the data dialog on open * WIP working on operation code... * Include all artifacts in TensorPlotter * Fixed python slice parsing * Add InteractiveExplorer base class * Use inform dialog w/ auth errors * Update TensorPlotter to inherit from InteractiveExplorer * fix css linting issue * Remove TensorPlotter * Remove tensor plotter from registries * Remove more tensorplotter things * Remove TensorPlotter tests * Remove old comments and minor fixes * remove old comment
- Loading branch information
Showing
11 changed files
with
595 additions
and
0 deletions.
There are no files selected for viewing
218 changes: 218 additions & 0 deletions
218
src/visualizers/panels/InteractiveEditor/InteractiveEditorControl.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
/*globals define, WebGMEGlobal*/ | ||
|
||
define([ | ||
'deepforge/viz/ConfigDialog', | ||
'js/Constants', | ||
], function ( | ||
ConfigDialog, | ||
CONSTANTS, | ||
) { | ||
|
||
'use strict'; | ||
|
||
class InteractiveEditorControl { | ||
constructor(options) { | ||
this._logger = options.logger.fork('Control'); | ||
this.client = options.client; | ||
this._embedded = options.embedded; | ||
this._widget = options.widget; | ||
this.initializeWidgetHandlers(this._widget); | ||
this.territoryEventFilters = []; | ||
|
||
this._currentNodeId = null; | ||
|
||
this._logger.debug('ctor finished'); | ||
} | ||
|
||
initializeWidgetHandlers (widget) { | ||
const features = widget.getCapabilities(); | ||
if (features.save) { | ||
widget.save = () => this.save(); | ||
} | ||
widget.getConfigDialog = () => new ConfigDialog(this.client); | ||
} | ||
|
||
selectedObjectChanged (nodeId) { | ||
const desc = this.getObjectDescriptor(nodeId); | ||
|
||
this._logger.debug('activeObject nodeId \'' + nodeId + '\''); | ||
|
||
if (this._currentNodeId) { | ||
this.client.removeUI(this._territoryId); | ||
} | ||
|
||
this._currentNodeId = nodeId; | ||
|
||
if (typeof this._currentNodeId === 'string') { | ||
const territory = this.getTerritory(nodeId); | ||
this._widget.setTitle(desc.name.toUpperCase()); | ||
|
||
this._territoryId = this.client | ||
.addUI(this, events => this._eventCallback(events)); | ||
|
||
this.client.updateTerritory(this._territoryId, territory); | ||
} | ||
} | ||
|
||
getTerritory(nodeId) { | ||
const territory = {}; | ||
territory[nodeId] = {children: 0}; | ||
return territory; | ||
} | ||
|
||
getMetaNode(name) { | ||
const metanodes = this.client.getAllMetaNodes(); | ||
return metanodes | ||
.find(node => { | ||
const namespace = node.getNamespace(); | ||
const fullName = namespace ? namespace + '.' + node.getAttribute('name') : | ||
node.getAttribute('name'); | ||
|
||
return fullName === name; | ||
}); | ||
} | ||
|
||
createNode(desc, parentId) { | ||
if (!parentId) { | ||
parentId = this._currentNodeId; | ||
} | ||
desc.pointers = desc.pointers || {}; | ||
desc.attributes = desc.attributes || {}; | ||
|
||
const base = this.getMetaNode(desc.type) || this.client.getNode(desc.pointers.base); | ||
const nodeId = this.client.createNode({ | ||
parentId: parentId, | ||
baseId: base.getId() | ||
}); | ||
|
||
const attributes = Object.entries(desc.attributes); | ||
attributes.forEach(entry => { | ||
const [name, value] = entry; | ||
this.client.setAttribute(nodeId, name, value); | ||
}); | ||
|
||
const pointers = Object.entries(desc.pointers); | ||
pointers.forEach(entry => { | ||
const [name, id] = entry; | ||
this.client.setPointer(nodeId, name, id); | ||
}); | ||
|
||
return nodeId; | ||
} | ||
|
||
save() { | ||
this.client.startTransaction(); | ||
const dataId = this.createNode(this._widget.getSnapshot()); | ||
const implicitOpId = this.createNode(this._widget.getEditorState(), dataId); | ||
this.client.setPointer(dataId, 'provenance', implicitOpId); | ||
const operationId = this.createNode(this._widget.getOperation(), implicitOpId); | ||
this.client.setPointer(implicitOpId, 'operation', operationId); | ||
this.client.completeTransaction(); | ||
} | ||
|
||
getObjectDescriptor (nodeId) { | ||
const node = this.client.getNode(nodeId); | ||
|
||
if (node) { | ||
return { | ||
id: node.getId(), | ||
name: node.getAttribute('name'), | ||
childrenIds: node.getChildrenIds(), | ||
parentId: node.getParentId(), | ||
}; | ||
} | ||
} | ||
|
||
/* * * * * * * * Node Event Handling * * * * * * * */ | ||
_eventCallback (events=[]) { | ||
this._logger.debug('_eventCallback \'' + events.length + '\' items'); | ||
|
||
events | ||
.filter(event => this.isRelevantEvent(event)) | ||
.forEach(event => { | ||
switch (event.etype) { | ||
|
||
case CONSTANTS.TERRITORY_EVENT_LOAD: | ||
this.onNodeLoad(event.eid); | ||
break; | ||
case CONSTANTS.TERRITORY_EVENT_UPDATE: | ||
this.onNodeUpdate(event.eid); | ||
break; | ||
case CONSTANTS.TERRITORY_EVENT_UNLOAD: | ||
this.onNodeUnload(event.eid); | ||
break; | ||
default: | ||
break; | ||
} | ||
}); | ||
|
||
this._logger.debug('_eventCallback \'' + events.length + '\' items - DONE'); | ||
} | ||
|
||
onNodeLoad (gmeId) { | ||
const description = this.getObjectDescriptor(gmeId); | ||
this._widget.addNode(description); | ||
} | ||
|
||
onNodeUpdate (gmeId) { | ||
const description = this.getObjectDescriptor(gmeId); | ||
this._widget.updateNode(description); | ||
} | ||
|
||
onNodeUnload (gmeId) { | ||
this._widget.removeNode(gmeId); | ||
} | ||
|
||
isRelevantEvent (event) { | ||
return this.territoryEventFilters | ||
.reduce((keep, fn) => keep && fn(event), true); | ||
} | ||
|
||
_stateActiveObjectChanged (model, activeObjectId) { | ||
if (this._currentNodeId === activeObjectId) { | ||
// The same node selected as before - do not trigger | ||
} else { | ||
this.selectedObjectChanged(activeObjectId); | ||
} | ||
} | ||
|
||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */ | ||
destroy () { | ||
this._detachClientEventListeners(); | ||
} | ||
|
||
_attachClientEventListeners () { | ||
this._detachClientEventListeners(); | ||
if (!this._embedded) { | ||
WebGMEGlobal.State.on( | ||
'change:' + CONSTANTS.STATE_ACTIVE_OBJECT, | ||
this._stateActiveObjectChanged, | ||
this | ||
); | ||
} | ||
} | ||
|
||
_detachClientEventListeners () { | ||
if (!this._embedded) { | ||
WebGMEGlobal.State.off( | ||
'change:' + CONSTANTS.STATE_ACTIVE_OBJECT, | ||
this._stateActiveObjectChanged | ||
); | ||
} | ||
} | ||
|
||
onActivate () { | ||
this._attachClientEventListeners(); | ||
|
||
if (typeof this._currentNodeId === 'string') { | ||
WebGMEGlobal.State.registerActiveObject(this._currentNodeId, {suppressVisualizerFromNode: true}); | ||
} | ||
} | ||
|
||
onDeactivate () { | ||
this._detachClientEventListeners(); | ||
} | ||
} | ||
|
||
return InteractiveEditorControl; | ||
}); |
97 changes: 97 additions & 0 deletions
97
src/visualizers/panels/InteractiveEditor/InteractiveEditorPanel.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/*globals define, _, WebGMEGlobal*/ | ||
|
||
define([ | ||
'js/PanelBase/PanelBaseWithHeader', | ||
'js/PanelManager/IActivePanel', | ||
'widgets/InteractiveEditor/InteractiveEditorWidget', | ||
'./InteractiveEditorControl' | ||
], function ( | ||
PanelBaseWithHeader, | ||
IActivePanel, | ||
InteractiveEditorWidget, | ||
InteractiveEditorControl | ||
) { | ||
'use strict'; | ||
|
||
function InteractiveEditorPanel(layoutManager, params) { | ||
var options = {}; | ||
//set properties from options | ||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'InteractiveEditorPanel'; | ||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true; | ||
|
||
//call parent's constructor | ||
PanelBaseWithHeader.apply(this, [options, layoutManager]); | ||
|
||
this._client = params.client; | ||
this._embedded = params.embedded; | ||
|
||
this.initialize(); | ||
|
||
this.logger.debug('ctor finished'); | ||
} | ||
|
||
//inherit from PanelBaseWithHeader | ||
_.extend(InteractiveEditorPanel.prototype, PanelBaseWithHeader.prototype); | ||
_.extend(InteractiveEditorPanel.prototype, IActivePanel.prototype); | ||
|
||
InteractiveEditorPanel.prototype.initialize = function () { | ||
var self = this; | ||
|
||
//set Widget title | ||
this.setTitle(''); | ||
|
||
this.widget = new InteractiveEditorWidget(this.logger, this.$el); | ||
|
||
this.widget.setTitle = function (title) { | ||
self.setTitle(title); | ||
}; | ||
|
||
this.control = new InteractiveEditorControl({ | ||
logger: this.logger, | ||
client: this._client, | ||
embedded: this._embedded, | ||
widget: this.widget | ||
}); | ||
|
||
this.onActivate(); | ||
}; | ||
|
||
/* OVERRIDE FROM WIDGET-WITH-HEADER */ | ||
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */ | ||
InteractiveEditorPanel.prototype.onReadOnlyChanged = function (isReadOnly) { | ||
//apply parent's onReadOnlyChanged | ||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly); | ||
|
||
}; | ||
|
||
InteractiveEditorPanel.prototype.onResize = function (width, height) { | ||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height); | ||
this.widget.onWidgetContainerResize(width, height); | ||
}; | ||
|
||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */ | ||
InteractiveEditorPanel.prototype.destroy = function () { | ||
this.control.destroy(); | ||
this.widget.destroy(); | ||
|
||
PanelBaseWithHeader.prototype.destroy.call(this); | ||
WebGMEGlobal.KeyboardManager.setListener(undefined); | ||
WebGMEGlobal.Toolbar.refresh(); | ||
}; | ||
|
||
InteractiveEditorPanel.prototype.onActivate = function () { | ||
this.widget.onActivate(); | ||
this.control.onActivate(); | ||
WebGMEGlobal.KeyboardManager.setListener(this.widget); | ||
WebGMEGlobal.Toolbar.refresh(); | ||
}; | ||
|
||
InteractiveEditorPanel.prototype.onDeactivate = function () { | ||
this.widget.onDeactivate(); | ||
this.control.onDeactivate(); | ||
WebGMEGlobal.KeyboardManager.setListener(undefined); | ||
WebGMEGlobal.Toolbar.refresh(); | ||
}; | ||
|
||
return InteractiveEditorPanel; | ||
}); |
44 changes: 44 additions & 0 deletions
44
src/visualizers/panels/InteractiveExplorer/InteractiveExplorerControl.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/*globals define */ | ||
|
||
define([ | ||
'panels/InteractiveEditor/InteractiveEditorControl', | ||
], function ( | ||
InteractiveEditorControl, | ||
) { | ||
|
||
'use strict'; | ||
|
||
class InteractiveExplorerControl extends InteractiveEditorControl { | ||
ensureValidSnapshot(desc) { | ||
const metadata = this.getMetaNode('pipeline.Metadata'); | ||
const type = this.getMetaNode(desc.type); | ||
|
||
if (!type) { | ||
throw new Error(`Invalid metadata type: ${type}`); | ||
} | ||
|
||
if (!type.isTypeOf(metadata.getId())) { | ||
throw new Error('Explorer can only create artifact metadata'); | ||
} | ||
} | ||
|
||
save() { | ||
const snapshotDesc = this._widget.getSnapshot(); | ||
this.ensureValidSnapshot(snapshotDesc); | ||
|
||
const features = this._widget.getCapabilities(); | ||
this.client.startTransaction(); | ||
const data = this.createNode(snapshotDesc); | ||
if (features.provenance) { | ||
const implicitOp = this.createNode(this._widget.getEditorState(), data); | ||
this.client.setPointer(data.getId(), 'provenance', implicitOp.getId()); | ||
const operation = this.createNode(this._widget.getOperation(), implicitOp); | ||
this.client.setPointer(implicitOp.getId(), 'operation', operation.getId()); | ||
} | ||
this.client.completeTransaction(); | ||
} | ||
|
||
} | ||
|
||
return InteractiveExplorerControl; | ||
}); |
33 changes: 33 additions & 0 deletions
33
src/visualizers/panels/InteractiveExplorer/InteractiveExplorerPanel.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/*globals define */ | ||
|
||
define([ | ||
'panels/InteractiveEditor/InteractiveEditorPanel', | ||
'widgets/InteractiveExplorer/InteractiveExplorerWidget', | ||
'./InteractiveExplorerControl', | ||
], function ( | ||
InteractiveEditorPanel, | ||
InteractiveExplorerWidget, | ||
InteractiveExplorerControl, | ||
) { | ||
'use strict'; | ||
|
||
class InteractiveExplorerPanel extends InteractiveEditorPanel { | ||
|
||
initialize() { | ||
this.setTitle(''); | ||
this.widget = new InteractiveExplorerWidget(this.logger, this.$el); | ||
this.widget.setTitle = title => this.setTitle(title); | ||
|
||
this.control = new InteractiveExplorerControl({ | ||
logger: this.logger, | ||
client: this._client, | ||
embedded: this._embedded, | ||
widget: this.widget | ||
}); | ||
|
||
this.onActivate(); | ||
} | ||
} | ||
|
||
return InteractiveExplorerPanel; | ||
}); |
Oops, something went wrong.