Comparar commits
21 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ad19e0fb57 | |||
| 14f222bf6f | |||
| 95141d1a42 | |||
| 93aaf72372 | |||
| 47a6612ed0 | |||
| fe48af8bf4 | |||
| 78ca4f8762 | |||
| a7e08aa279 | |||
| e2980d616f | |||
| 96f2090d9e | |||
| 8a86a114db | |||
| cb757da118 | |||
| 31711e079a | |||
| 16ebb83ae6 | |||
| 65304b2645 | |||
| b44c6a104b | |||
| a8e5876f83 | |||
| 2d9d1e71c0 | |||
| 475bdfed50 | |||
| c36f12ccb2 | |||
| 0b8b5b8adf |
@@ -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
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,3 +22,7 @@
|
||||
.create-node text {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.job-canceled {
|
||||
background-color: #ffe0b2;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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 = `[0;31mFailed to execute operation: ${info.status}[0m`;
|
||||
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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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.
@@ -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 * * * * * * * */
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
{"http://localhost:8888":{}}
|
||||
@@ -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": {},
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário