Comparar commits

..

21 Commits

Autor SHA1 Mensagem Data
Brian Broll ad19e0fb57 v0.12.0 2016-08-08 08:30:01 -05:00
Brian Broll 14f222bf6f Merge branch '624-large-files-fail-exec' 2016-08-04 15:23:00 -05:00
Brian Broll 95141d1a42 Added image counter for uploading. Fixes #625 (#632) 2016-08-04 15:14:26 -05:00
Brian Broll 93aaf72372 Added explicit data download on worker. Fixes #624
WIP #624 Missing parens
2016-08-04 15:11:02 -05:00
Brian Broll 47a6612ed0 Added anchor detection on output type click. Fixes #626 (#631) 2016-08-04 14:37:15 -05:00
Brian Broll fe48af8bf4 Created unique worker config and cleanup on close. Fixes #618 (#623) 2016-08-04 10:55:06 -05:00
Brian Broll 78ca4f8762 Added error logs for more complex errors. Fixes #620 (#621)
WIP #620 Added error message to stdout

WIP #620 Added colors and console message
2016-08-04 10:01:36 -05:00
Brian Broll a7e08aa279 Update attributes on cancel job. Fixes #617 (#619)
WIP #617 moved client edits out of promise

WIP #617 Made 'isRunning' more robust

WIP #617 Fixed client calls
2016-08-04 08:37:09 -05:00
Brian Broll e2980d616f Filter out all non-alphanumeric or _ chars in execution. Fixes #612 (#614) 2016-08-03 16:55:02 -05:00
Brian Broll 96f2090d9e Removed long error message on failed exec. Fixes #608 (#611) 2016-08-03 15:55:04 -05:00
Brian Broll 8a86a114db Updated Materialize to explicit require (~global). Fixes #607 (#610) 2016-08-03 15:44:31 -05:00
Brian Broll cb757da118 Added torch config dir check before update. Fixes #606 (#609) 2016-08-03 15:36:43 -05:00
Brian Broll 31711e079a Enabled autoMerge. Fixes #332 Fixes #346 (#605) 2016-08-03 14:02:11 -05:00
Brian Broll 16ebb83ae6 Hiding restart button when running. Fixes #597 (#604) 2016-08-03 13:49:17 -05:00
Brian Broll 65304b2645 Changed SNAPSHOT->DEBUG. Fixes #601 (#603) 2016-08-03 13:18:41 -05:00
Brian Broll b44c6a104b Added execution canceling. Fixes #481 (#602)
WIP #481 Added buttons and jobId, secret setting

WIP #481 Added 'jobId', 'secret' to Job

WIP #481 Canceling job exec support

WIP #481 Added canceling executing pipelines

WIP #481 Fixed canceling pipelines

WIP #481 Improved result messages from executions

WIP #481 Updated decorator and status setting for ExecJob

WIP #481 Updated job colors in lists

WIP #481 Updated pipeline library

WIP #481 Fixed code climate issues
2016-08-03 12:42:55 -05:00
Brian Broll a8e5876f83 Updated fab icon colors. Fixes #588 (#599) 2016-08-03 08:41:24 -05:00
Brian Broll 2d9d1e71c0 Added "complete" notification if exec forked. Fixes #596 (#598)
WIP #596 Added quotes around job, exec, branch for consistency
2016-08-03 08:37:24 -05:00
Brian Broll 475bdfed50 Added notification on fork to ExecuteJob. Fixes #591 (#595)
WIP #591 Added notification on fork to ExecuteJob

WIP #591 Fixed fork name creation
2016-08-03 08:16:04 -05:00
Brian Broll c36f12ccb2 Added Execution dashboard. Fixes #587 (#594)
WIP #587 Added toggling embedded viz

WIP #587 Added ExecutionIndex

WIP #587 Added exec table and rows

WIP #587 Added status colors

WIP #587 Added color coded by status

WIP #587 Retrieved graphs for each execution

WIP #587 Added lineGraph object in right split

WIP #587 Fixed lineGraph resize and added multiple lines

WIP #587 Fixed line updates/removal

WIP #587 Added execution selection

WIP #587 Added execution toggling

WIP #587 Fixed untoggle-able after update

WIP #587 Fixed exec status color updates

WIP #578 Exec name click -> navigate to the given execution

WIP #587 Added pipeline names

WIP #587 Added default 'checking' and pipeline name updates

WIP #587 Fixed the deselection of running execs

WIP #587 Fixed initial pipeline names

WIP #587 Added toggling visualizers

WIP #587 Fixed positioning

WIP #587 Added more logs and fixed pipeline name finding

WIP #587 Fixed project switching and obj changed

WIP #587 Improved perf of chart
2016-08-02 16:42:28 -05:00
Brian Broll 0b8b5b8adf Added decimal to number chars in graph plotting. Fixes #589 (#590) 2016-08-02 11:56:50 -05:00
37 arquivos alterados com 1151 adições e 117 exclusões
+11
Ver Arquivo
@@ -305,6 +305,17 @@ program
if (!justInstalled) {
// Upgrade torch
console.log('Upgrading torch...');
console.log(`Checking for torch in ${config.torch.dir}`);
// Verify that torch is installed in the config's location
if (!exists.sync(path.join(config.torch.dir, 'update.sh'))) {
// config is incorrect!
console.log('Could not find torch installation. Please update the deepforge config with:');
console.log('');
console.log(' deepforge config torch.dir ~/path/to/torch/install');
console.log('');
return;
}
job = spawn('bash', ['./update.sh'], {
cwd: p(config.torch.dir)
});
+10 -2
Ver Arquivo
@@ -4,11 +4,12 @@ var path = require('path'),
fs = require('fs'),
childProcess = require('child_process'),
spawn = childProcess.spawn,
rm_rf = require('rimraf'),
projectConfig = require(__dirname + '/../config'),
executorSrc = path.join(__dirname, '..', 'node_modules', 'webgme', 'src',
'server', 'middleware', 'executor', 'worker'),
workerPath = path.join(__dirname, '..', 'src', 'worker'),
workerConfigPath = path.join(workerPath, 'config.json'),
workerConfigPath = path.join(workerPath, 'config_' + Date.now() + '.json'),
workerTmp = path.join(workerPath, 'tmp'),
address,
config = {};
@@ -22,7 +23,15 @@ if (result.error) {
process.exit(1);
}
var cleanUp = function() {
console.log('removing config ', workerConfigPath);
rm_rf.sync(workerConfigPath);
};
var startExecutor = function() {
process.on('SIGINT', cleanUp);
process.on('uncaughtException', cleanUp);
// Start the executor
var execJob = spawn('node', [
'node_worker.js',
@@ -42,7 +51,6 @@ var createConfigJson = function() {
}
config[address] = {};
// TODO: Check if the config already exists
fs.writeFile(workerConfigPath, JSON.stringify(config), startExecutor);
};
+2
Ver Arquivo
@@ -21,5 +21,7 @@ config.executor.clearOldDataAtStartUp = true;
config.visualization.extraCss.push('deepforge/styles/global.css');
config.storage.autoMerge.enable = true;
validateConfig(config);
module.exports = config;
+1 -1
Ver Arquivo
@@ -12,7 +12,7 @@
"watch-test": "./node_modules/nodemon/bin/nodemon.js --exec 'node ./node_modules/mocha/bin/mocha --recursive test'",
"build-nn": "node ./utils/nn-parser.js"
},
"version": "0.11.0",
"version": "0.12.0",
"dependencies": {
"commander": "^2.9.0",
"dotenv": "^2.0.0",
+3 -1
Ver Arquivo
@@ -1,10 +1,12 @@
/* globals Materialize, WebGMEGlobal, define*/
/* globals WebGMEGlobal, define*/
// This file creates the DeepForge namespace and defines basic actions
define([
'panel/FloatingActionButton/styles/Materialize',
'js/RegistryKeys',
'js/Panels/MetaEditor/MetaEditorConstants',
'js/Constants'
], function(
Materialize,
REGISTRY_KEYS,
META_CONSTANTS,
CONSTANTS
+4
Ver Arquivo
@@ -22,3 +22,7 @@
.create-node text {
font-style: italic;
}
.job-canceled {
background-color: #ffe0b2;
}
+19
Ver Arquivo
@@ -0,0 +1,19 @@
/* globals define*/
define({
getDisplayTime: timestamp => {
var today = new Date().toLocaleDateString(),
date = new Date(timestamp).toLocaleDateString();
if (date === today) {
date = `Today (${new Date(timestamp).toLocaleTimeString()})`;
}
return date;
},
ClassForJobStatus: {
success: 'success',
canceled: 'job-canceled',
failed: 'danger',
pending: '',
running: 'warning'
}
});
@@ -1,10 +1,6 @@
/*globals define, _*/
/*jshint browser: true, camelcase: false*/
/**
* @author brollb / https://github.com/brollb
*/
define([
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
'css!./JobDecorator.EasyDAGWidget.css'
@@ -20,6 +16,7 @@ define([
pending: '#9e9e9e',
queued: '#cfd8dc',
running: '#fff59d',
canceled: '#ffcc80',
success: '#66bb6a',
fail: '#e57373'
};
@@ -35,6 +32,8 @@ define([
status: true,
execFiles: true,
stdout: true,
secret: true,
jobId: true,
debug: true
};
EllipseDecorator.call(this, options);
+6 -3
Ver Arquivo
@@ -118,7 +118,7 @@ define([
this.core.addMember(this.activeNode, 'executions', tgtNode);
return this.project.createTag(
execName.replace(/[ -]/g, '_'),
execName,
this.currentHash
);
})
@@ -153,10 +153,13 @@ define([
};
CreateExecution.prototype.getUniqueExecName = function (basename) {
var name = basename,
taken = {},
var taken = {},
name,
i = 2;
basename = basename.replace(/[^\da-zA-Z_]/g, '_');
name = basename;
// Get a unique name wrt the tags and the other executions
return this.project.getTags()
.then(tags => {
+126 -31
Ver Arquivo
@@ -2,6 +2,7 @@
/*jshint node:true, browser:true*/
define([
'common/storage/constants',
'text!./metadata.json',
'executor/ExecutorClient',
'plugin/PluginBase',
@@ -12,6 +13,7 @@ define([
'q',
'underscore'
], function (
STORAGE_CONSTANTS,
pluginMetadata,
ExecutorClient,
PluginBase,
@@ -46,6 +48,7 @@ define([
this._markForDeletion = {}; // id -> node
this._oldMetadataByName = {}; // name -> id
this.lastAppliedCmd = {};
this.canceled = false;
};
/**
@@ -79,10 +82,43 @@ define([
}
this._callback = callback;
this.currentForkName = null;
this.prepare()
.then(() => this.executeJob(this.activeNode));
};
ExecuteJob.prototype.updateForkName = function (basename) {
basename = basename + '_fork';
basename = basename.replace(/[- ]/g, '_');
return this.project.getBranches().then(branches => {
var names = Object.keys(branches),
name = basename,
i = 2;
while (names.indexOf(name) !== -1) {
name = basename + '_' + i;
i++;
}
this.forkName = name;
});
};
// Override 'save' to notify the user on fork
ExecuteJob.prototype.save = function (msg) {
var name = this.core.getAttribute(this.activeNode, 'name');
return this.updateForkName(name)
.then(() => PluginBase.prototype.save.call(this, msg))
.then(result => {
var msg;
if (result.status === STORAGE_CONSTANTS.FORKED) {
msg = `"${name}" execution has forked to "${result.forkName}"`;
this.currentForkName = result.forkName;
this.sendNotification(msg);
}
});
};
ExecuteJob.prototype.getConnections = function (nodes) {
var conns = [];
for (var i = nodes.length; i--;) {
@@ -177,6 +213,24 @@ define([
}
delete this.lastAppliedCmd[nodeId];
delete this._markForDeletion[nodeId];
this.core.delAttribute(job, 'jobId');
this.core.delAttribute(job, 'secret');
};
ExecuteJob.prototype.resultMsg = function(msg) {
this.sendNotification(msg);
this.createMessage(null, msg);
};
ExecuteJob.prototype.onOperationCanceled = function(op) {
var job = this.core.getParent(op),
name = this.core.getAttribute(op, 'name'),
msg = `"${name}" canceled!`;
this.core.setAttribute(job, 'status', 'canceled');
this.resultMsg(msg);
this.onComplete(op, null);
};
ExecuteJob.prototype.onOperationFail =
@@ -186,8 +240,8 @@ define([
exec = this.core.getParent(job),
name = this.core.getAttribute(job, 'name'),
jobId = this.core.getPath(job),
status = err ? 'fail' : 'success',
msg = err ? `${name} execution failed: ${err}` :
status = err ? 'fail' : (this.canceled ? 'canceled' : 'success'),
msg = err ? `${name} execution failed!` :
`${name} executed successfully!`,
promise = Q();
@@ -195,8 +249,16 @@ define([
this.logger.info(`Setting ${name} (${jobId}) status to ${status}`);
this.clearOldMetadata(job);
if (this.currentForkName) {
// notify client that the job has completed
this.sendNotification(`"${name}" execution completed on branch "${this.currentForkName}"`);
}
if (err) {
this.logger.warn(`${name} failed: ${err}`);
this.core.setAttribute(exec, 'status', 'failed');
} else if (this.canceled) {
// Should I set this to 'canceled'?
this.core.setAttribute(exec, 'status', 'canceled');
} else {
// Check if all the other jobs are successful. If so, set the
// execution status to 'success'
@@ -222,6 +284,7 @@ define([
});
}
this.createMessage(null, msg);
promise
.then(() => this.save(msg))
.then(() => {
@@ -277,17 +340,19 @@ define([
);
})
.then(mds => {
// get (input, filename) tuples
// Record the large files
var inputData = {};
mds.forEach((metadata, i) => {
// add the hashes for each input
var input = inputs[i],
name = metadata.name,
hash = files.inputAssets[input];
data['inputs/' + input + '/' + name] = hash;
inputData['inputs/' + input + '/' + name] = hash;
});
delete files.inputAssets;
files['input-data.json'] = JSON.stringify(inputData, null, 2);
// Add pointer assets
Object.keys(files.ptrAssets)
@@ -382,7 +447,13 @@ define([
this.logger.debug(`Making a commit from ${this.currentHash}`);
this.save(`Queued "${name}" operation in ${this.pipelineName}`)
.then(() => executor.createJob({hash}))
.then(() => this.watchOperation(executor, hash, opNode, job))
.then(info => {
this.core.setAttribute(job, 'jobId', info.hash);
if (info.secret) { // o.w. it is a cached job!
this.core.setAttribute(job, 'secret', info.secret);
}
return this.watchOperation(executor, hash, opNode, job);
})
.catch(err => this.logger.error(`Could not execute "${name}": ${err}`));
};
@@ -703,7 +774,19 @@ define([
var jobId = this.core.getPath(job),
opId = this.core.getPath(op),
info,
name;
secret,
name = this.core.getAttribute(job, 'name');
// If canceled, stop the operation
if (this.canceled) {
secret = this.core.getAttribute(job, 'secret');
if (secret) {
executor.cancelJob(hash, secret);
this.core.delAttribute(job, 'secret');
this.canceled = true;
return this.onOperationCanceled(op);
}
}
return executor.getInfo(hash)
.then(_info => { // Update the job's stdout
@@ -717,8 +800,7 @@ define([
return executor.getOutput(hash, currentLine, actualLine+1)
.then(outputLines => {
var stdout = this.core.getAttribute(job, 'stdout'),
output = outputLines.map(o => o.output).join(''),
jobName = this.core.getAttribute(job, 'name');
output = outputLines.map(o => o.output).join('');
// parse deepforge commands
output = this.parseForMetadataCmds(job, output);
@@ -726,7 +808,7 @@ define([
if (output) {
stdout += output;
this.core.setAttribute(job, 'stdout', stdout);
return this.save(`Received stdout for ${jobName}`);
return this.save(`Received stdout for ${name}`);
}
});
}
@@ -736,7 +818,6 @@ define([
if (info.status === 'RUNNING' &&
this.core.getAttribute(job, 'status') !== 'running') {
name = this.core.getAttribute(job, 'name');
this.core.setAttribute(job, 'status', 'running');
this.save(`Started "${name}" operation in ${this.pipelineName}`);
}
@@ -748,26 +829,40 @@ define([
return;
}
name = this.core.getAttribute(job, 'name');
this.core.setAttribute(job, 'execFiles', info.resultHashes[name + '-all-files']);
return this.blobClient.getArtifact(info.resultHashes.stdout)
.then(artifact => {
var stdoutHash = artifact.descriptor.content[STDOUT_FILE].content;
return this.blobClient.getObjectAsString(stdoutHash);
})
.then(stdout => {
// Parse the remaining code
stdout = this.parseForMetadataCmds(job, stdout, true);
this.core.setAttribute(job, 'stdout', stdout);
if (info.status !== 'SUCCESS') {
// Download all files
this.result.addArtifact(info.resultHashes[name + '-all-files']);
// Set the job to failed! Store the error
this.onOperationFail(op, `Operation "${opId}" failed! ${JSON.stringify(info)}`);
} else {
this.onDistOperationComplete(op, info);
}
});
if (info.status === 'CANCELED') {
// If it was cancelled, the pipeline has been stopped
this.logger.debug(`"${name}" has been CANCELED!`);
this.canceled = true;
return this.onOperationCanceled(op);
}
if (info.status === 'SUCCESS' || info.status === 'FAILED_TO_EXECUTE') {
this.core.setAttribute(job, 'execFiles', info.resultHashes[name + '-all-files']);
return this.blobClient.getArtifact(info.resultHashes.stdout)
.then(artifact => {
var stdoutHash = artifact.descriptor.content[STDOUT_FILE].content;
return this.blobClient.getObjectAsString(stdoutHash);
})
.then(stdout => {
// Parse the remaining code
stdout = this.parseForMetadataCmds(job, stdout, true);
this.core.setAttribute(job, 'stdout', stdout);
if (info.status !== 'SUCCESS') {
// Download all files
this.result.addArtifact(info.resultHashes[name + '-all-files']);
// Set the job to failed! Store the error
this.onOperationFail(op, `Operation "${opId}" failed! ${JSON.stringify(info)}`);
} else {
this.onDistOperationComplete(op, info);
}
});
} else { // something bad happened...
var err = `Failed to execute operation "${opId}": ${info.status}`,
consoleErr = `Failed to execute operation: ${info.status}`;
this.core.setAttribute(job, 'stdout', consoleErr);
this.logger.error(err);
this.onOperationFail(op, err);
}
})
.catch(err => this.logger.error(`Could not get op info for ${opId}: ${err}`));
};
@@ -911,7 +1006,7 @@ define([
ExecuteJob.prototype[CONSTANTS.GRAPH_PLOT] = function (job, id, x, y) {
var jobId = this.core.getPath(job),
nonNum = /[^\d]*/g,
nonNum = /[^\d\.]*/g,
graph,
points;
+49 -6
Ver Arquivo
@@ -12,11 +12,15 @@ var spawn = require('child_process').spawn,
// Get the BlobClient...
var COMMAND_PREFIX = '<%= START_CMD %>',
IMAGE = '<%= IMAGE %>',
requirejs = require('webgme').requirejs;
requirejs = require('webgme').requirejs,
remainingImageCount = 0,
exitCode = null;
requirejs([
'q',
'blob/BlobClient'
], function(
Q,
BlobClient
) {
var url = process.env.ORIGIN_URL || 'http://127.0.0.1:8888',
@@ -34,12 +38,20 @@ requirejs([
logger: logger
});
var checkFinished = () => {
if (exitCode !== null && remainingImageCount === 0) {
log('finished!');
process.exit(exitCode);
}
};
var uploadImage = function(line) {
var args = line.split(/\s+/),
name = args.slice(2).join(' ').replace(/\s+$/, ''),
filename = 'metadata/' + name + '.png';
// Upload the image from metadata/
remainingImageCount++;
fs.readFile(filename, (err, content) => {
if (err) {
console.error(`Could not read ${filename}: ${err}`);
@@ -47,10 +59,15 @@ requirejs([
}
// Add hash to the image command
log('about to putFile', filename);
blobClient.putFile(filename, content)
.then(hash => {
args.splice(2, 0, hash);
console.log(args.join(' '));
log('printing cmd:', args.join(' '));
--remainingImageCount;
log('finished uploading ' + filename + ' ' + remainingImageCount + ' remain');
checkFinished();
})
.fail(err => console.error(`${filename} upload failed: ${err}`));
});
@@ -74,9 +91,35 @@ requirejs([
process.stdout.write(result.join('\n'));
};
// Run 'th init.lua' and merge the stdout, stderr
var job = spawn('th', ['init.lua']);
job.stdout.on('data', onStdout);
job.stderr.on('data', data => process.stdout.write(data));
job.on('close', code => process.exit(code));
var getData = function(ipath, hash) {
// Download the data and put it in the given path
var deferred = Q.defer();
return blobClient.getObject(hash)
.then(buffer => fs.writeFile(ipath, buffer, (err, result) => {
if (err) {
console.error('Retrieving ' + ipath + ' failed!');
deferred.reject(err);
}
console.error('Retrieved ' + ipath);
deferred.resolve(err);
}));
};
// Download the large files
var inputData = JSON.parse(fs.readFileSync('./input-data.json')),
inputPaths = Object.keys(inputData);
// Request the data from the blob
Q.all(inputPaths.map(ipath => getData(ipath, inputData[ipath]))).then(() => {
// Run 'th init.lua' and merge the stdout, stderr
var job = spawn('th', ['init.lua']);
job.stdout.on('data', onStdout);
job.stderr.on('data', data => process.stdout.write(data));
job.on('close', code => {
exitCode = code;
log('script finished w/ exit code:', code);
checkFinished();
});
});
});
+34 -22
Ver Arquivo
@@ -74,6 +74,7 @@ define([
// - keep track if the pipeline has errored
// - if so, don't start any more jobs
this.pipelineError = null;
this.canceled = false;
this.runningJobs = 0;
// metadata records
@@ -112,6 +113,7 @@ define([
}
this._callback = callback;
this.currentForkName = null;
startPromise
.then(() => this.core.loadSubTree(this.activeNode))
@@ -129,32 +131,17 @@ define([
.fail(e => this.logger.error(e));
};
ExecutePipeline.prototype.updateForkName = function () {
var basename = this.pipelineName + '_fork';
return this.project.getBranches().then(branches => {
var names = Object.keys(branches),
name = basename,
i = 2;
while (names.indexOf(name) !== -1) {
name = basename + '_' + i;
i++;
}
this.forkName = name;
});
};
// Override 'save' to prevent race conditions while saving
ExecutePipeline.prototype.save = function (msg) {
// When 'save' is called, it should still finish any current save op
// before continuing
this._currentSave = this._currentSave
.then(() => this.updateForkName())
.then(() => this.updateForkName(this.pipelineName))
.then(() => CreateExecution.prototype.save.call(this, msg))
.then(result => {
var msg;
if (result.status === STORAGE_CONSTANTS.FORKED) {
this.currentForkName = result.forkName;
msg = `"${this.pipelineName}" execution has forked to "${result.forkName}"`;
this.sendNotification(msg);
}
@@ -287,8 +274,17 @@ define([
this.onPipelineComplete(err);
};
ExecutePipeline.prototype.onOperationCanceled = function(op) {
var job = this.core.getParent(op);
this.core.setAttribute(job, 'status', 'canceled');
this.runningJobs--;
this.logger.debug(`${this.core.getAttribute(job, 'name')} has been canceled`);
this.onPipelineComplete();
};
ExecutePipeline.prototype.onPipelineComplete = function(err) {
var name = this.core.getAttribute(this.activeNode, 'name');
var name = this.core.getAttribute(this.activeNode, 'name'),
msg = `"${this.pipelineName}" `;
if (err) {
this.runningJobs--;
@@ -296,17 +292,33 @@ define([
this.pipelineError = this.pipelineError || err;
if (this.pipelineError && this.runningJobs > 0) {
this.logger.info('Pipeline errored but is waiting for the running ' +
this.logger.debug(`${this.runningJobs} remaining jobs`);
if ((this.pipelineError || this.canceled) && this.runningJobs > 0) {
var action = this.pipelineError ? 'error' : 'cancel';
this.logger.info(`Pipeline ${action}ed but is waiting for the running ` +
'jobs to finish');
return;
}
if (this.currentForkName) {
// notify client that the job has completed
this.sendNotification(`"${this.pipelineName}" execution completed on branch "${this.currentForkName}"`);
}
if (this.pipelineError) {
msg += 'failed!';
} else if (this.canceled) {
msg += 'canceled!';
} else {
msg += 'finished!';
}
this.logger.debug(`Pipeline "${name}" complete!`);
this.core.setAttribute(this.activeNode, 'status',
(!this.pipelineError ? 'success' : 'failed'));
(this.pipelineError ? 'failed' : (this.canceled ? 'canceled' : 'success')));
this._finished = true;
this.resultMsg(msg);
this.save('Pipeline execution finished')
.then(() => {
this.result.setSuccess(!this.pipelineError);
@@ -323,7 +335,7 @@ define([
this.logger.info(`About to execute ${readyOps.length} operations`);
// If the pipeline has errored don't start any more jobs
if (this.pipelineError) {
if (this.pipelineError || this.canceled) {
if (this.runningJobs === 0) {
this.onPipelineComplete();
}
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
+6
Ver Arquivo
@@ -94,5 +94,11 @@
"title": "ImageViewer",
"panel": "panels/ImageViewer/ImageViewerPanel",
"DEBUG_ONLY": false
},
{
"id": "ExecutionIndex",
"title": "ExecutionIndex",
"panel": "panels/ExecutionIndex/ExecutionIndexPanel",
"DEBUG_ONLY": false
}
]
@@ -0,0 +1,296 @@
/*globals define, WebGMEGlobal*/
/*jshint browser: true*/
define([
'js/Constants'
], function (
CONSTANTS
) {
'use strict';
var ExecutionIndexControl;
ExecutionIndexControl = function (options) {
this._logger = options.logger.fork('Control');
this._client = options.client;
this._embedded = options.embedded;
// Initialize core collections and variables
this._widget = options.widget;
this._currentNodeId = null;
this.displayedExecutions = {};
this._linesForExecution = {};
this._lineToExec = {};
this._pipelineNames = {};
this._initWidgetEventHandlers();
this._logger.debug('ctor finished');
};
ExecutionIndexControl.prototype._initWidgetEventHandlers = function () {
this._widget.setExecutionDisplayed = this.setExecutionDisplayed.bind(this);
};
ExecutionIndexControl.prototype.setExecutionDisplayed = function (id, bool) {
var lines = this._linesForExecution[id] || [],
action = bool ? 'addNode' : 'removeNode';
// If removing, just get the ids
lines = bool ? lines : lines.map(line => line.id);
this._logger.info(`setting execution ${id} to ${bool ? 'displayed' : 'hidden'}`);
this.displayedExecutions[id] = bool;
// update the given lines
for (var i = lines.length; i--;) {
this._widget[action](lines[i]);
}
};
/* * * * * * * * Visualizer content update callbacks * * * * * * * */
ExecutionIndexControl.prototype.selectedObjectChanged = function (nodeId) {
var self = this;
self._logger.debug('activeObject nodeId \'' + nodeId + '\'');
// Remove current territory patterns
if (self._currentNodeId) {
self._client.removeUI(self._territoryId);
}
self._currentNodeId = nodeId;
if (typeof self._currentNodeId === 'string') {
// Create a territory for the executions
self._selfPatterns = {};
self._territoryId = self._client.addUI(self, function (events) {
self._eventCallback(events);
});
// Update the territory
self._selfPatterns[nodeId] = {children: 4};
self._client.updateTerritory(self._territoryId, self._selfPatterns);
}
};
// This next function retrieves the relevant node information for the widget
ExecutionIndexControl.prototype._getObjectDescriptor = function (nodeId) {
var node = this._client.getNode(nodeId),
childIds,
desc,
base,
type;
if (node) {
base = this._client.getNode(node.getBaseId());
type = base.getAttribute('name');
desc = {
id: node.getId(),
type: type,
name: node.getAttribute('name')
};
if (type === 'Execution') {
desc.status = node.getAttribute('status');
desc.originTime = node.getAttribute('createdAt');
desc.originId = node.getPointer('origin').to;
desc.pipelineName = this._pipelineNames[desc.originId];
this._logger.debug(`Looking up pipeline name for ${desc.name}: ${desc.pipelineName}`);
// Create a territory for this origin and update it!
this._selfPatterns[desc.originId] = {children: 0};
setTimeout(() => this._client.updateTerritory(this._territoryId, this._selfPatterns), 0);
} else if (type === 'Line') {
desc = this.getLineDesc(node);
} else if (type === 'Pipeline') {
desc.execs = node.getMemberIds('executions');
this._pipelineNames[desc.id] = desc.name;
} else if (type === 'Graph') {
childIds = node.getChildrenIds();
desc.lines = childIds.map(id => {
var n = this._client.getNode(id);
return this.getLineDesc(n);
});
}
}
return desc;
};
ExecutionIndexControl.prototype.getLineDesc = function (node) {
var id = node.getId(),
graphId = node.getParentId(),
jobId = this._client.getNode(graphId).getParentId(),
execId = this._client.getNode(jobId).getParentId(),
points,
desc;
points = node.getAttribute('points').split(';')
.map(pair => {
var nums = pair.split(',').map(num => parseFloat(num));
return {
x: nums[0],
y: nums[1]
};
});
desc = {
id: id,
//execName: execName,
execId: execId,
lineName: node.getAttribute('name'),
name: node.getAttribute('name'),
type: 'line',
points: points
};
// Update records
if (!this._linesForExecution[execId]) {
this._linesForExecution[execId] = [];
}
this._linesForExecution[execId].push(desc);
this._lineToExec[id] = execId;
return desc;
};
/* * * * * * * * Node Event Handling * * * * * * * */
ExecutionIndexControl.prototype._eventCallback = function (events) {
var event;
events = events.filter(event => event.eid !== this._currentNodeId);
this._logger.debug('received \'' + events.length + '\' events');
for (var i = events.length; i--;) {
event = events[i];
switch (event.etype) {
case CONSTANTS.TERRITORY_EVENT_LOAD:
this._onLoad(event.eid);
break;
case CONSTANTS.TERRITORY_EVENT_UPDATE:
this._onUpdate(event.eid);
break;
case CONSTANTS.TERRITORY_EVENT_UNLOAD:
this._onUnload(event.eid);
break;
default:
break;
}
}
this._logger.debug('finished processing events!');
};
ExecutionIndexControl.prototype._onLoad = function (gmeId) {
var desc = this._getObjectDescriptor(gmeId);
this._logger.debug(`Loading node of type ${desc.type}`);
if (desc.type === 'Execution') {
this._logger.debug('Adding node to widget...');
this._logger.debug('desc:', desc);
this._widget.addNode(desc);
} else if (desc.type === 'line' && this.isLineDisplayed(desc)) {
this._widget.addNode(desc);
} else if (desc.type === 'Pipeline') {
this.updatePipelineNames(desc);
}
};
ExecutionIndexControl.prototype._onUpdate = function (gmeId) {
var desc = this._getObjectDescriptor(gmeId);
if (desc.type === 'Execution') {
this._widget.updateNode(desc);
} else if (desc.type === 'line' && this.isLineDisplayed(desc)) {
this._widget.updateNode(desc);
} else if (desc.type === 'Pipeline') {
this.updatePipelineNames(desc);
}
};
ExecutionIndexControl.prototype.updatePipelineNames = function (desc) {
// Get all associated executions and update their pipeline name
this._logger.debug('updating pipeline name for ' + desc.execs.join(', '));
for (var i = desc.execs.length; i--;) {
this._widget.updatePipelineName(desc.execs[i], desc.name);
}
if (desc.execs.length === 0) {
// Executions have been deleted - no longer relevant
this._logger.debug('pipeline has 0 executions... removing it', desc.id);
delete this._selfPatterns[desc.id];
delete this._pipelineNames[desc.id];
}
};
ExecutionIndexControl.prototype._onUnload = function (id) {
var execId = this._lineToExec[id];
if (execId) { // it is a line
delete this._lineToExec[id];
for (var k = this._linesForExecution[execId].length; k--;) {
if (this._linesForExecution[execId][k].id === id) {
this._linesForExecution[execId].splice(k, 1);
break;
}
}
}
this._widget.removeNode(id);
};
ExecutionIndexControl.prototype.isLineDisplayed = function (line) {
// lines are only displayed if their execution is checked
return this.displayedExecutions[line.execId];
};
ExecutionIndexControl.prototype._stateActiveObjectChanged = function (model, activeObjectId) {
if (this._currentNodeId === activeObjectId) {
// The same node selected as before - do not trigger
} else {
this.selectedObjectChanged(activeObjectId);
}
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
ExecutionIndexControl.prototype.destroy = function () {
this._detachClientEventListeners();
};
ExecutionIndexControl.prototype._attachClientEventListeners = function () {
this._detachClientEventListeners();
if (!this._embedded) {
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
this._stateActiveObjectChanged, this);
}
};
ExecutionIndexControl.prototype._detachClientEventListeners = function () {
if (!this._embedded) {
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
this._stateActiveObjectChanged);
}
};
ExecutionIndexControl.prototype.onActivate = function () {
this._attachClientEventListeners();
if (typeof this._currentNodeId === 'string') {
WebGMEGlobal.State.registerSuppressVisualizerFromNode(true);
WebGMEGlobal.State.registerActiveObject(this._currentNodeId);
WebGMEGlobal.State.registerSuppressVisualizerFromNode(false);
}
};
ExecutionIndexControl.prototype.onDeactivate = function () {
this._detachClientEventListeners();
};
return ExecutionIndexControl;
});
@@ -0,0 +1,93 @@
/*globals define, _, WebGMEGlobal*/
/*jshint browser: true*/
define([
'js/PanelBase/PanelBase',
'js/PanelManager/IActivePanel',
'widgets/ExecutionIndex/ExecutionIndexWidget',
'./ExecutionIndexControl'
], function (
PanelBase,
IActivePanel,
ExecutionIndexWidget,
ExecutionIndexControl
) {
'use strict';
var ExecutionIndexPanel;
ExecutionIndexPanel = function (layoutManager, params) {
var options = {};
//set properties from options
options[PanelBase.OPTIONS.LOGGER_INSTANCE_NAME] = 'ExecutionIndexPanel';
options[PanelBase.OPTIONS.FLOATING_TITLE] = true;
//call parent's constructor
PanelBase.apply(this, [options, layoutManager]);
this._client = params.client;
this._embedded = params.embedded;
//initialize UI
this._initialize();
this.logger.debug('ctor finished');
};
//inherit from PanelBase
_.extend(ExecutionIndexPanel.prototype, PanelBase.prototype);
_.extend(ExecutionIndexPanel.prototype, IActivePanel.prototype);
ExecutionIndexPanel.prototype._initialize = function () {
//set Widget title
this.widget = new ExecutionIndexWidget(this.logger, this.$el);
this.control = new ExecutionIndexControl({
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 */
ExecutionIndexPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
//apply parent's onReadOnlyChanged
PanelBase.prototype.onReadOnlyChanged.call(this, isReadOnly);
};
ExecutionIndexPanel.prototype.onResize = function (width, height) {
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
this.widget.onWidgetContainerResize(width, height);
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
ExecutionIndexPanel.prototype.destroy = function () {
this.control.destroy();
this.widget.destroy();
PanelBase.prototype.destroy.call(this);
WebGMEGlobal.KeyboardManager.setListener(undefined);
WebGMEGlobal.Toolbar.refresh();
};
ExecutionIndexPanel.prototype.onActivate = function () {
this.widget.onActivate();
this.control.onActivate();
WebGMEGlobal.KeyboardManager.setListener(this.widget);
WebGMEGlobal.Toolbar.refresh();
};
ExecutionIndexPanel.prototype.onDeactivate = function () {
this.widget.onDeactivate();
this.control.onDeactivate();
WebGMEGlobal.KeyboardManager.setListener(undefined);
WebGMEGlobal.Toolbar.refresh();
};
return ExecutionIndexPanel;
});
@@ -1,11 +1,13 @@
/*globals DeepForge, define, $, Materialize, WebGMEGlobal*/
/*globals DeepForge, define, $, WebGMEGlobal*/
// These are actions defined for specific meta types. They are evaluated from
// the context of the ForgeActionButton
define([
'panel/FloatingActionButton/styles/Materialize',
'q',
'js/RegistryKeys',
'deepforge/globals'
], function(
Materialize,
Q,
REGISTRY_KEYS
) {
@@ -103,6 +105,7 @@ define([
name: `Return to ${fromType}`,
icon: 'input',
priority: 2,
color: 'teal',
filter: () => {
return DeepForge.last[fromType];
},
@@ -112,6 +115,7 @@ define([
name: `Delete ${type} Definition`,
icon: 'delete',
priority: 1,
color: 'red',
action: function() {
// Delete and go to the last pipeline?
var node = this.client.getNode(this._currentNodeId),
@@ -139,6 +143,11 @@ define([
name: 'Restart ' + name,
icon: 'replay',
priority: 1000,
color: 'red',
filter: function() {
// Only show if stopped!
return !this.isRunning();
},
action: function(event) {
this.runExecutionPlugin(pluginId, event.shiftKey);
}
@@ -215,9 +224,46 @@ define([
icon: 'play_for_work',
priority: 1,
href: download.execFiles
},
// Stop execution button
{
name: 'Stop Current Job',
icon: 'stop',
priority: 1001,
filter: function() {
return this.isRunning();
},
action: function() {
this.stopJob();
}
}
],
Execution: [
makeRestartButton('Execution', 'ExecutePipeline'),
// Stop execution button
{
name: 'Stop Running Execution',
icon: 'stop',
priority: 1001,
filter: function() {
return this.isRunning();
},
action: function() {
// Stop every running job
var execNode = this.client.getNode(this._currentNodeId),
jobIds = execNode.getChildrenIds(),
msg = `Canceling ${execNode.getAttribute('name')} execution`;
this.client.startTransaction(msg);
jobIds.map(id => this.client.getNode(id))
.filter(job => this.isRunning(job)) // get running jobs
.forEach(job => this.stopJob(job)); // stop them
this.client.setAttributes(execNode.getId(), 'status', 'canceled');
this.client.completeTransaction();
}
}
],
Execution: [makeRestartButton('Execution', 'ExecutePipeline')],
Pipeline: [
{
name: 'Create new node',
@@ -1,8 +1,10 @@
/*globals DeepForge, $, Materialize, define, _ */
/*globals DeepForge, $, WebGMEGlobal, window, define, _ */
/*jshint browser: true*/
define([
'panel/FloatingActionButton/styles/Materialize',
'blob/BlobClient',
'executor/ExecutorClient',
'js/Constants',
'panel/FloatingActionButton/FloatingActionButton',
'deepforge/viz/PipelineControl',
@@ -15,7 +17,9 @@ define([
'text!./PluginConfig.json',
'deepforge/globals'
], function (
Materialize,
BlobClient,
ExecutorClient,
CONSTANTS,
PluginButton,
PipelineControl,
@@ -33,6 +37,11 @@ define([
var ForgeActionButton= function (layoutManager, params) {
PluginButton.call(this, layoutManager, params);
this._pluginConfig = JSON.parse(PluginConfig);
this._executor = new ExecutorClient({
logger: this.logger.fork('ExecutorClient'),
serverPort: WebGMEGlobal.gmeConfig.server.port,
httpsecure: window.location.protocol === 'https:'
});
this._client = this.client;
this._actions = [];
this._blobClient = new BlobClient({
@@ -60,7 +69,7 @@ define([
if (!base) { // must be ROOT or FCO
basename = node.getAttribute('name') || 'ROOT_NODE';
actions = (ACTIONS[basename] || [])
.filter(action => !action.filter || action.filter());
.filter(action => !action.filter || action.filter.call(this));
return actions;
}
@@ -69,7 +78,7 @@ define([
base = this.client.getNode(base.getBaseId());
actions = ACTIONS[basename];
if (actions) {
actions = actions.filter(action => !action.filter || action.filter());
actions = actions.filter(action => !action.filter || action.filter.call(this));
}
}
@@ -328,14 +337,67 @@ define([
context.managerConfig.namespace = 'pipeline';
method = useSecondary ? 'runBrowserPlugin' : 'runServerPlugin';
this.client[method](pluginId, context, err => {
if (err) {
return Materialize.toast(`${name} failed!`, 4000);
this.client[method](pluginId, context, (err, result) => {
var msg = err ? `${name} failed!` : `${name} executed successfully!`,
duration = err ? 4000 : 2000;
// Check if it was canceled - if so, show that type of message
if (result) {
msg = result.messages[0].message;
duration = 4000;
}
Materialize.toast(`${name} executed successfully!`, 2000);
Materialize.toast(msg, duration);
});
};
ForgeActionButton.prototype.isRunning = function(node) {
var baseId,
base,
type;
node = node || this.client.getNode(this._currentNodeId);
baseId = node.getBaseId();
base = this.client.getNode(baseId);
type = base.getAttribute('name');
if (type === 'Execution') {
return node.getAttribute('status') === 'running';
} else if (type === 'Job') {
return this.isRunningJob(node);
}
return false;
};
ForgeActionButton.prototype.isRunningJob = function(job) {
var status = job.getAttribute('status');
return (status === 'running' || status === 'pending') &&
job.getAttribute('secret') && job.getAttribute('jobId');
};
ForgeActionButton.prototype.stopJob = function(job) {
var jobHash,
jobId,
secret;
job = job || this.client.getNode(this._currentNodeId);
jobId = job.getId();
jobHash = job.getAttribute('jobId');
secret = job.getAttribute('secret');
if (!jobHash || !secret) {
this.logger.error('Cannot stop job. Missing jobHash or secret');
return;
}
this.client.delAttributes(jobId, 'jobId');
this.client.delAttributes(jobId, 'secret');
this.client.setAttributes(jobId, 'status', 'canceled');
return this._executor.cancelJob(jobHash, secret)
.then(() => this.logger.info(`${jobHash} has been cancelled!`))
.fail(err => this.logger.error(`Job cancel failed: ${err}`));
};
return ForgeActionButton;
});
@@ -5,6 +5,7 @@
},
"ExecutePipeline": {
"icon": "play_arrow",
"color": "green",
"priority": 1
},
"ImportTorch": {
@@ -65,6 +65,8 @@ define([
return null;
}
};
this._widget.toggleEmbeddedPanel = () => this.toggleEmbeddedPanel();
};
/* * * * * * * * Visualizer content update callbacks * * * * * * * */
+43 -10
Ver Arquivo
@@ -9,6 +9,7 @@ define([
'widgets/MainView/MainViewWidget',
'./MainViewControl',
'panels/PipelineIndex/PipelineIndexPanel',
'panels/ExecutionIndex/ExecutionIndexPanel',
'deepforge/globals'
], function (
PanelBaseWithHeader,
@@ -16,6 +17,7 @@ define([
MainViewWidget,
MainViewControl,
PipelineIndexPanel,
ExecutionIndexPanel,
DeepForge
) {
'use strict';
@@ -38,12 +40,14 @@ define([
this.$nav = $('<div>', {id: 'nav-container'});
this.$el.css({padding: 0});
this.embeddedPanel = new PipelineIndexPanel(layoutManager, params);
this.$embedded = this.embeddedPanel.$el;
this.$embedded.addClass('embedded');
this.$el.append(this.$nav, this.$embedded);
this.embeddedPanels = [
PipelineIndexPanel,
ExecutionIndexPanel
];
this.nextPanelIndex = 0;
this._lm = layoutManager;
this._params = params;
this.$el.append(this.$nav);
this._initialize();
this.logger.debug('ctor finished');
@@ -66,15 +70,42 @@ define([
widget: this.widget
});
var controlObjectChanged = this.control.selectedObjectChanged;
this.control.selectedObjectChanged = nodeId => {
this.embeddedPanel.control.selectedObjectChanged(DeepForge.places.MyPipelines);
return controlObjectChanged.call(this.control, nodeId);
this.control.toggleEmbeddedPanel = this.toggleEmbeddedPanel.bind(this);
var selectedObjectChanged = this.control.selectedObjectChanged;
this.control.selectedObjectChanged = id => {
this.embeddedPanel.control.selectedObjectChanged(this.getEmbeddedNode());
selectedObjectChanged.call(this.control, id);
};
this.toggleEmbeddedPanel(true);
this.onActivate();
};
MainViewPanel.prototype.getEmbeddedNode = function() {
return this.nextPanelIndex === 1 ? DeepForge.places.MyPipelines : DeepForge.places.MyExecutions;
};
MainViewPanel.prototype.toggleEmbeddedPanel = function (silent) {
var Panel = this.embeddedPanels[this.nextPanelIndex];
this.nextPanelIndex = (this.nextPanelIndex + 1) % this.embeddedPanels.length;
if (this.embeddedPanel) { // Remove current
this.embeddedPanel.destroy();
this.$embedded.remove();
}
this.embeddedPanel = new Panel(this._lm, this._params);
this.$embedded = this.embeddedPanel.$el;
this.$embedded.addClass('main-view-embedded');
this.$el.append(this.$embedded);
// Call on Resize and selectedObjectChanged
this.onResize(this.width, this.height);
if (!silent) {
this.embeddedPanel.control.selectedObjectChanged(this.getEmbeddedNode());
}
};
/* OVERRIDE FROM WIDGET-WITH-HEADER */
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
MainViewPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
@@ -98,6 +129,8 @@ define([
margin: 'inherit'
});
this.embeddedPanel.onResize(embeddedWidth, height);
this.width = width;
this.height = height;
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
@@ -92,13 +92,18 @@ define([
OutputViewerPanel.prototype.selectOutput = function (element) {
if (this.$selected !== element) {
// Update the panel
var dataId = element.data('id');
while (element.prop('tagName').toLowerCase() !== 'a' && element.length) {
element = element.parent();
}
dataId = element.data('id');
this.$selected.parent().removeClass('active');
element.parent().addClass('active');
this.$selected = element;
// Update the panel
var dataId = element.data('id');
if (dataId) {
this.loadOutputFor(dataId);
} else { // Set the logviewer
@@ -0,0 +1,12 @@
<table class="table">
<thead>
<tr>
<td></td>
<td>Name</td>
<td>Creation Date</td>
<td>Origin Pipeline</td>
</tr>
</thead>
<tbody class="execs-content">
</tbody>
</table>
@@ -0,0 +1,226 @@
/*globals define, WebGMEGlobal, $*/
/*jshint browser: true*/
define([
'deepforge/viz/Utils',
'widgets/LineGraph/LineGraphWidget',
'text!./ExecTable.html',
'css!./styles/ExecutionIndexWidget.css'
], function (
Utils,
LineGraphWidget,
TableHtml
) {
'use strict';
var ExecutionIndexWidget,
WIDGET_CLASS = 'execution-index';
ExecutionIndexWidget = function (logger, container) {
this._logger = logger.fork('Widget');
this.$el = container;
this.nodes = {};
this.graphs = {};
this._initialize();
this._logger.debug('ctor finished');
};
ExecutionIndexWidget.prototype._initialize = function () {
// set widget class
this.$el.addClass(WIDGET_CLASS);
// Create split screen
this.$left = $('<div>', {class: 'left'});
this.$right = $('<div>', {class: 'right'});
this.$el.append(this.$left, this.$right);
// Create the table
this.$table = $(TableHtml);
this.$table.on('click', '.exec-row', event => this.onExecutionClicked(event));
this.$table.on('click', '.node-nav', event => this.navToNode(event));
this.$left.append(this.$table);
this.$execList = this.$table.find('.execs-content');
// Create the graph in the right half
this.lineGraph = new LineGraphWidget(this._logger, this.$right);
this.defaultSelection = null;
this.hasRunning = false;
};
ExecutionIndexWidget.prototype.navToNode = function (event) {
var id = event.target.getAttribute('data-id');
if (typeof id === 'string') {
WebGMEGlobal.State.registerActiveObject(id);
event.stopPropagation();
}
this._logger.warn('No node id found for node-nav!');
};
ExecutionIndexWidget.prototype.onExecutionClicked = function (event) {
var target = event.target,
checked,
id;
while (!target.getAttribute('data-id')) {
if (!target.parentNode) {
this._logger.error('could not find execution id for ' + event);
return;
}
target = target.parentNode;
}
id = target.getAttribute('data-id');
checked = this.nodes[id].$checkbox.checked;
if (event.target.tagName.toLowerCase() !== 'input') {
this.setSelect(id, !checked);
} else {
this.setExecutionDisplayed(id, checked);
}
};
ExecutionIndexWidget.prototype.onWidgetContainerResize = function (width, height) {
this.$left.css({
width: width/2,
height: height
});
this.$right.css({
left: width/2,
width: width/2,
height: height
});
this.lineGraph.onWidgetContainerResize(width/2, height);
this._logger.debug('Widget is resizing...');
};
// Adding/Removing/Updating items
ExecutionIndexWidget.prototype.addNode = function (desc) {
if (desc.type === 'Execution') {
// Add node to a table of nodes
this.addExecLine(desc);
this.updateSelected(desc);
} else if (desc.type === 'line') {
desc.type = 'line';
this.lineGraph.addNode(desc);
}
};
ExecutionIndexWidget.prototype.updatePipelineName = function (execId, name) {
if (this.nodes[execId]) {
this.nodes[execId].$pipeline.text(name);
}
};
ExecutionIndexWidget.prototype.addExecLine = function (desc) {
var row = $('<tr>', {class: 'exec-row', 'data-id': desc.id}),
checkBox = $('<input>', {type: 'checkbox'}),
statusClass = Utils.ClassForJobStatus[desc.status],
fields,
pipeline,
name,
td;
pipeline = $('<a>', {
class: 'node-nav',
'data-id': desc.originId
}).text(desc.pipelineName || 'view pipeline');
name = $('<a>', {class: 'node-nav', 'data-id': desc.id}).text(desc.name);
fields = [
checkBox,
name,
Utils.getDisplayTime(desc.originTime),
pipeline
];
for (var i = 0; i < fields.length; i++) {
td = $('<td>');
if ((typeof fields[i]) === 'string') {
td.text(fields[i]);
} else {
td.append(fields[i]);
}
row.append(td);
}
this._logger.debug(`Adding execution ${desc.name} (${desc.id}) to list`);
this.$execList.append(row);
row.addClass(statusClass);
this.nodes[desc.id] = {
statusClass: statusClass,
$el: row,
$checkbox: checkBox[0],
$pipeline: pipeline,
$name: name
};
};
ExecutionIndexWidget.prototype.removeNode = function (id) {
if (this.nodes[id]) {
this.nodes[id].$el.remove();
} else if (this.graphs[id]) {
delete this.graphs[id];
}
delete this.nodes[id];
this.lineGraph.removeNode(id); // 'nop' if node is not line
};
ExecutionIndexWidget.prototype.updateSelected = function (desc) {
// If the running pipeline has been unselected, don't reselect it!
if (desc.status === 'running') {
this.hasRunning = true;
this.setSelect(desc.id, true);
if (this.defaultSelection) {
this.setSelect(this.defaultSelection, false);
}
} else if (!this.hasRunning && !this.defaultSelection) {
this.defaultSelection = desc.id;
this.setSelect(desc.id, true);
}
};
ExecutionIndexWidget.prototype.setSelect = function (id, checked) {
this.nodes[id].$checkbox.checked = checked;
this.setExecutionDisplayed(id, checked);
};
ExecutionIndexWidget.prototype.updateNode = function (desc) {
var node = this.nodes[desc.id];
if (node) {
node.$name.text(desc.name);
node.$el.removeClass(node.statusClass);
node.$el.addClass(Utils.ClassForJobStatus[desc.status]);
if (Utils.ClassForJobStatus[desc.status] !== node.statusClass) {
// Only update the selection if the status has changed.
// ie, it has started running
this.updateSelected(desc);
}
this._logger.debug(`setting execution ${desc.id} to ${desc.status}`);
node.statusClass = Utils.ClassForJobStatus[desc.status];
} else if (desc.type === 'line') {
this.lineGraph.updateNode(desc);
}
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
ExecutionIndexWidget.prototype.destroy = function () {
};
ExecutionIndexWidget.prototype.onActivate = function () {
this._logger.debug('ExecutionIndexWidget has been activated');
};
ExecutionIndexWidget.prototype.onDeactivate = function () {
this._logger.debug('ExecutionIndexWidget has been deactivated');
};
return ExecutionIndexWidget;
});
@@ -0,0 +1,21 @@
/**
* This file is for any css that you may want for this visualizer.
*
* Ideally, you would use the scss file also provided in this directory
* and then generate this file automatically from that. However, you can
* simply write css if you prefer
*/
.execution-index.panel-body {
padding: 0;
}
.execution-index .left {
position: absolute;
background-color: #eee;
}
.execution-index .right {
position: absolute;
background-color: #eee;
}
@@ -0,0 +1,7 @@
/**
* This file is for any scss that you may want for this visualizer.
*/
.execution-index {
outline: none;
}
@@ -48,8 +48,8 @@ define([
var title = nodeName === undefined ? this._currentTitle : nodeName;
this._currentTitle = title;
if (this.isSnapshot) {
title += ' (SNAPSHOT)';
if (!this.isSnapshot) {
title += ' (DEBUG)';
}
this._setTitle(title);
@@ -1,4 +1,4 @@
/*globals define, d3, nv */
/*globals define, d3, nv, _ */
/*jshint browser: true*/
define([
@@ -35,7 +35,6 @@ define([
this.$chart = d3.select(this.$el[0]).append('svg');
nv.addGraph(() => {
var chart = nv.models.lineChart()
//.margin({left: 100})
.useInteractiveGuideline(true)
.showLegend(true)
.showYAxis(true)
@@ -82,20 +81,20 @@ define([
values: desc.points
};
}
this.updateChartData();
this.refreshChart();
}
};
LineGraphWidget.prototype.removeNode = function (id) {
delete this.lineData[id];
this.updateChartData();
this.refreshChart();
};
LineGraphWidget.prototype.updateNode = function (desc) {
if (desc && this.lineData[desc.id]) {
this.lineData[desc.id].values = desc.points;
this.lineData[desc.id].key = desc.name;
this.updateChartData();
this.refreshChart();
}
};
@@ -115,6 +114,9 @@ define([
}
};
LineGraphWidget.prototype.refreshChart =
_.debounce(LineGraphWidget.prototype.updateChartData, 50);
LineGraphWidget.prototype.updateChart = function () {
if (this.chart) {
this.chart.update();
@@ -17,12 +17,17 @@ define([
var MainViewWidget,
WIDGET_CLASS = 'main-view',
CreateListItem = _.template(ListItem);
CreateListItem = _.template(ListItem),
ToggleLabels = [
'Executions',
'Pipelines'
];
MainViewWidget = function (logger, container) {
this._logger = logger.fork('Widget');
this.$el = container;
this.$el.addClass(WIDGET_CLASS);
this.toggleIndex = 0;
this.initialize();
this._logger.debug('ctor finished');
};
@@ -32,6 +37,19 @@ define([
this.$nav = $(NavBarHTML);
this.$el.append(this.$nav);
// Execution support
this.$toggle = this.$nav.find('#toggle-main');
this.$toggleLabel = this.$nav.find('.toggle-label');
this.$toggle.on('click', () => {
if (this._closed) { // shouldn't be clicked when closed (but it is possible)
return;
}
this.toggleEmbeddedPanel();
// Update the toggle name
this.toggleIndex = (this.toggleIndex + 1) % 2;
this.$toggleLabel.text(ToggleLabels[this.toggleIndex]);
});
this.$archlist = this.$nav.find('#arch-list-content');
this.$artifacts = this.$nav.find('#artifact-list-content');
@@ -1,6 +1,13 @@
<nav class="side-nav fixed closed hide-list">
<li class="pull-right side-nav-control">
<span class="glyphicon glyphicon-menu-hamburger" aria-hidden="true"></span>
</li >
<li class="no-padding" id="toggle-main">
<ul>
<li class="no-padding">
<a class="toggle-label">Executions</a>
</li>
</ul>
</li>
<li class="no-padding">
<ul class="collapsible" data-collapsible="accordion">
@@ -1,3 +1,7 @@
.main-view-embedded {
position: absolute;
}
.main-view .side-nav-control {
padding-right: 1em;
padding-top: 1em;
@@ -1,14 +1,11 @@
/*globals WebGMEGlobal, $, define*/
/*jshint browser: true*/
/**
* Generated by VisualizerGenerator 1.7.0 from webgme on Thu May 19 2016 14:04:47 GMT-0500 (CDT).
*/
define([
'widgets/EasyDAG/AddNodeDialog',
'widgets/EasyDAG/EasyDAGWidget',
'deepforge/viz/PipelineControl',
'deepforge/viz/Utils',
'deepforge/globals',
'./OperationNode',
'./Connection',
@@ -19,6 +16,7 @@ define([
AddNodeDialog,
EasyDAGWidget,
PipelineControl,
Utils,
DeepForge,
OperationNode,
Connection,
@@ -35,12 +33,7 @@ define([
DEFAULT: 'default',
CONNECTING: 'connecting'
},
UPLOAD_ARTIFACT_ID = '__UPLOAD_ARTIFACT__',
STATUS_TO_CLASS = {
running: 'warning',
success: 'success',
failed: 'danger'
};
UPLOAD_ARTIFACT_ID = '__UPLOAD_ARTIFACT__';
PipelineEditorWidget = function (logger, container, execCntr) {
EasyDAGWidget.call(this, logger, container);
@@ -321,14 +314,10 @@ define([
var row = $('<tr>'),
title = $('<td>', {class: 'execution-name'}),
timestamp = $('<td>'),
className = STATUS_TO_CLASS[exec.status] || '',
today = new Date().toLocaleDateString(),
date = new Date(exec.createdAt).toLocaleDateString(),
className = Utils.ClassForJobStatus[exec.status] || '',
date = Utils.getDisplayTime(exec.createdAt),
rmIcon = $(REMOVE_ICON);
if (date === today) {
date = `Today (${new Date(exec.createdAt).toLocaleTimeString()})`;
}
timestamp.text(date);
title.append($('<a>').text(exec.name));
-1
Ver Arquivo
@@ -1 +0,0 @@
{"http://localhost:8888":{}}
+7
Ver Arquivo
@@ -215,6 +215,13 @@
"panel": "src/visualizers/panels/ImageViewer",
"secondary": false,
"widget": "src/visualizers/widgets/ImageViewer"
},
"ExecutionIndex": {
"src": "panels/ExecutionIndex/ExecutionIndexPanel",
"title": "ExecutionIndex",
"panel": "src/visualizers/panels/ExecutionIndex",
"secondary": false,
"widget": "src/visualizers/widgets/ExecutionIndex"
}
},
"addons": {},