Comparar commits
185 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| d88b758f43 | |||
| 0e6ae8f9b6 | |||
| 3426a07096 | |||
| e95f611b79 | |||
| 5d0f13c5ec | |||
| 88e1f3828e | |||
| 4de9cf29fe | |||
| 448616be12 | |||
| 93a2905534 | |||
| 0bd0bd70f2 | |||
| 1720fc869a | |||
| 41e7750dd1 | |||
| 81afc81887 | |||
| 3e9931fa3b | |||
| 5135ccef09 | |||
| 6314e00a8d | |||
| 779429e24d | |||
| a7f4eac09d | |||
| 7c645e9b23 | |||
| fc3e14644f | |||
| fd48c1b480 | |||
| 8928d0f105 | |||
| 6857da5ac7 | |||
| fee584cfde | |||
| 45fcd81739 | |||
| d831f5e032 | |||
| 65e938f489 | |||
| b9b5cea5a1 | |||
| 6c639376d1 | |||
| 58ed3048f8 | |||
| f64492061d | |||
| 552e71687d | |||
| 448de23945 | |||
| dbd00e5946 | |||
| 4d0b4ae017 | |||
| d6c201cce6 | |||
| b8bc3af524 | |||
| 72de1d44da | |||
| da6f186b52 | |||
| 5f1c50beb6 | |||
| 3a79afbc41 | |||
| ba210e4b27 | |||
| bdb21a5262 | |||
| 4b6471efbc | |||
| beeb1deb3c | |||
| 0bc6846c32 | |||
| 8a40cb95fb | |||
| 92ea4b2ff5 | |||
| e0a83b4d6c | |||
| 49268b9554 | |||
| 65164bcb76 | |||
| 1afcaf9238 | |||
| 92021a06ba | |||
| be1e4fa388 | |||
| 09505c9989 | |||
| b96f2a845a | |||
| 14c0af86c9 | |||
| 56a0b788ee | |||
| 873cbc2145 | |||
| 3e3326688c | |||
| 6e32394e01 | |||
| afed957af8 | |||
| 4c100ac001 | |||
| 29724551f0 | |||
| 1f5c8d7423 | |||
| a613a1e8f7 | |||
| 7fce6e98dd | |||
| ea572e8f6c | |||
| 36290d8dc7 | |||
| dadc09e0e1 | |||
| be3d278d13 | |||
| 32a96fbf2b | |||
| ebe391c948 | |||
| e30ebf3a45 | |||
| 9e0a780ed2 | |||
| 841637e804 | |||
| 75241262c4 | |||
| b39ac022b6 | |||
| 8486b86a05 | |||
| 322af8a4f5 | |||
| 5e1930f096 | |||
| c0f1348a14 | |||
| 383f5f9fa0 | |||
| 5cf592e103 | |||
| a14e750b6f | |||
| 73e165197f | |||
| 9dd0815625 | |||
| 8e3ac1d203 | |||
| 65f0902dd3 | |||
| 825209970c | |||
| 2453aa8f05 | |||
| 9fca9783a0 | |||
| 62616d28db | |||
| db03f3f361 | |||
| 68e4e06f3b | |||
| 4e03997af6 | |||
| 9357503b49 | |||
| 24b7471d28 | |||
| 9e16aba11b | |||
| 60f7cba7e7 | |||
| 7ec0c98d5c | |||
| 03339bb667 | |||
| a77ca29bbb | |||
| 7fe29c705c | |||
| 2d20746808 | |||
| d1b43bf50c | |||
| 80f87b1068 | |||
| a3ff6cf40d | |||
| ec8fcb25f0 | |||
| d9899af233 | |||
| 823e038edc | |||
| 7eb2c1a404 | |||
| 69cfc4dd77 | |||
| e88500df33 | |||
| 64ae1da03e | |||
| e24bf3171d | |||
| f301e9312f | |||
| 0b95219c2a | |||
| b05f96e77a | |||
| 063aac8730 | |||
| edeb783547 | |||
| b65c0b740a | |||
| 55d8b72e05 | |||
| 1d4469d2b9 | |||
| fda5be793d | |||
| a8666959c0 | |||
| f95125ab04 | |||
| a5cbfb2c89 | |||
| b43a580683 | |||
| 1f0f615fc4 | |||
| 3842439112 | |||
| 8229495b34 | |||
| b0135100ed | |||
| a628256682 | |||
| ddd6431d55 | |||
| a63287df92 | |||
| 3721257559 | |||
| dba9c4a25b | |||
| 11f7751843 | |||
| c05d7bb433 | |||
| 8e6bdfbeff | |||
| b4829adbc3 | |||
| a3cfa9f8e3 | |||
| 62a80d1f9f | |||
| b0ad46c66b | |||
| 7e2b83b5cc | |||
| 5193bbd931 | |||
| b513bcbec4 | |||
| 408ee8e0f4 | |||
| dd92726044 | |||
| 5d2098e6e1 | |||
| 341497323c | |||
| 1f71b254b5 | |||
| db97a492e1 | |||
| f863fc2638 | |||
| 3d6c654028 | |||
| 64f64ecd2a | |||
| 09b33792d9 | |||
| ab932c1a52 | |||
| 206f4ce6fc | |||
| 1618460984 | |||
| 41cfdfb007 | |||
| 57a60d6919 | |||
| ab3ba50886 | |||
| cc21727a7b | |||
| 1dbbf8a302 | |||
| 6f6ab1af1e | |||
| 09a51dd518 | |||
| 3e0b1c0e50 | |||
| 628c636129 | |||
| d2173bd9f3 | |||
| 2baf4a72bf | |||
| de1ca7a2d2 | |||
| 2b75f16b9a | |||
| 20618ddb40 | |||
| be97f4806e | |||
| cb97a14e59 | |||
| 6faa7d444a | |||
| 806780bd1f | |||
| 2e0ddfaeb9 | |||
| c83d4ecbc5 | |||
| a467e67524 | |||
| 271f237eac | |||
| 857be35efa | |||
| 87f73359ab |
@@ -0,0 +1,36 @@
|
||||
---
|
||||
engines:
|
||||
csslint:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
exclude_fingerprints:
|
||||
- 1e004cf4e49528a58a0ac3858112601c
|
||||
config:
|
||||
languages:
|
||||
- ruby
|
||||
- javascript
|
||||
- python
|
||||
- php
|
||||
eslint:
|
||||
enabled: true
|
||||
fixme:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.css"
|
||||
- "**.inc"
|
||||
- "**.js"
|
||||
- "**.jsx"
|
||||
- "**.module"
|
||||
- "**.php"
|
||||
- "**.py"
|
||||
- "**.rb"
|
||||
exclude_paths:
|
||||
- config/
|
||||
- test/
|
||||
- src/common/lua.js
|
||||
- src/common/js-yaml.min.js
|
||||
- src/visualizers/widgets/TextEditor/lib/
|
||||
- src/visualizers/widgets/PipelineIndex/styles/PipelineIndex.css
|
||||
- src/visualizers/widgets/LineGraph/lib/
|
||||
@@ -0,0 +1,3 @@
|
||||
--exclude-exts=.min.css
|
||||
--exclude-list=src/visualizers/widgets/PipelineIndex/styles/PipelineIndex.css
|
||||
--ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes
|
||||
@@ -2,3 +2,4 @@ src/common/lua.js
|
||||
src/visualizers/widgets/TextEditor/lib/*
|
||||
src/common/js-yaml.min.js
|
||||
src/visualizers/Visualizers.json
|
||||
config/config.webgme.js
|
||||
|
||||
+5
-4
@@ -1,18 +1,19 @@
|
||||
env:
|
||||
browser: true
|
||||
node: true
|
||||
mocha: true
|
||||
es6: true
|
||||
extends: 'eslint:recommended'
|
||||
rules:
|
||||
indent:
|
||||
- error
|
||||
- 2
|
||||
- 4
|
||||
linebreak-style:
|
||||
- error
|
||||
- 2
|
||||
- unix
|
||||
quotes:
|
||||
- error
|
||||
- 2
|
||||
- single
|
||||
semi:
|
||||
- error
|
||||
- 2
|
||||
- always
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
*.webgmex filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
|
||||
+12
-7
@@ -1,22 +1,27 @@
|
||||
[](https://img.shields.io/badge/state-pre--alpha-red.svg)
|
||||
[](https://img.shields.io/badge/state-beta-yellow.svg)
|
||||
[](./LICENSE)
|
||||
[](https://travis-ci.org/dfst/deepforge)
|
||||
[](https://gitter.im/dfst/deepforge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://waffle.io/dfst/deepforge)
|
||||
|
||||
**Notice**: DeepForge is still a work in progress and is also lacking significant documentation! That being said, any contributions and/or feedback is greatly appreciated (and feel free to always ask any questions on the gitter)!
|
||||
|
||||
# DeepForge
|
||||
DeepForge is an open-source visual development environment for deep learning. Currently, it supports Convolutional Neural Networks but we are planning on supporting additional deep learning classifiers such as RNNs and LSTMs. Additional features include real-time collaborative editing and version control.
|
||||
|
||||
## Quick Setup
|
||||
After cloning the repo, run
|
||||
## Quick Start
|
||||
Simply run the following command to install deepforge with its dependencies:
|
||||
|
||||
```
|
||||
npm install && npm start
|
||||
curl -o- https://raw.githubusercontent.com/dfst/deepforge/master/install.sh | bash
|
||||
```
|
||||
|
||||
Now, navigate to `localhost:8888` in a browser and create a new project. Select `nn` as the seed and start creating your neural nets!
|
||||
Next, follow the postinstall instructions to start MongoDB and DeepForge!
|
||||
|
||||
Finally, navigate to [http://localhost:8888](http://localhost:8888) to start using DeepForge! For more, detailed instructions,check out our [wiki](https://github.com/dfst/deepforge/wiki/Installation-Guide).
|
||||
|
||||
## Caffe Support?
|
||||
DeepForge uses Torch to perform the actual training and testing of the models. If you are interested in DeepForge using Caffe for actual training and testing, check out [DeepForge-Caffe](https://github.com/dfst/deepforge-caffe).
|
||||
|
||||
## Interested in contributing?
|
||||
Contributions are welcome! Either fork the project and submit some PR's or shoot me an email about getting more involved!
|
||||
|
||||
Sponsored by [Digital Reasoning](http://www.digitalreasoning.com/)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
env:
|
||||
browser: true
|
||||
node: true
|
||||
mocha: true
|
||||
es6: true
|
||||
extends: 'eslint:recommended'
|
||||
rules:
|
||||
no-console:
|
||||
- 0
|
||||
indent:
|
||||
- 2
|
||||
- 4
|
||||
linebreak-style:
|
||||
- 2
|
||||
- unix
|
||||
quotes:
|
||||
- 2
|
||||
- single
|
||||
semi:
|
||||
- 2
|
||||
- always
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"torch": {
|
||||
"dir": "~/.deepforge/torch"
|
||||
},
|
||||
"mongo": {
|
||||
"dir": "~/.deepforge/data"
|
||||
}
|
||||
}
|
||||
Arquivo executável
+393
@@ -0,0 +1,393 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var Command = require('commander').Command,
|
||||
program = new Command(),
|
||||
childProcess = require('child_process'),
|
||||
spawn = childProcess.spawn,
|
||||
execSync = childProcess.execSync,
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
version = require('../package.json').version,
|
||||
exists = require('exists-file'),
|
||||
forever = require('forever-monitor'),
|
||||
DEFAULT_CONFIG = require('./config.json'),
|
||||
assign = require('lodash.assign'),
|
||||
config,
|
||||
|
||||
configDir = path.join(process.env.HOME, '.deepforge'),
|
||||
configPath = path.join(configDir, 'config.json'),
|
||||
dataPath = path.join(configDir, 'data'),
|
||||
|
||||
localConfig,
|
||||
p = dir => dir.replace(/^~/, process.env.HOME); // resolve '~' to '$HOME'
|
||||
|
||||
// Check for any commands
|
||||
if (process.argv.length === 2) {
|
||||
process.argv.push('--help');
|
||||
}
|
||||
|
||||
// Create the config if it doesn't exist
|
||||
if (!exists.sync(configDir)) {
|
||||
fs.mkdirSync(configDir);
|
||||
}
|
||||
if (!exists.sync(dataPath)) {
|
||||
fs.mkdirSync(dataPath);
|
||||
}
|
||||
if (!exists.sync(configPath)) {
|
||||
fs.writeFileSync(configPath, '{\n}');
|
||||
}
|
||||
|
||||
localConfig = require(configPath);
|
||||
config = assign(DEFAULT_CONFIG, require(configPath));
|
||||
|
||||
var getConfigValue = function(id) {
|
||||
var keys = id.split('.'),
|
||||
value = config;
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
value = value[keys[i]];
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
var storeConfig = function(id, value) {
|
||||
// load the config
|
||||
var keys = id.split('.').filter(k => k),
|
||||
lastKey = keys.pop(),
|
||||
currentObj = localConfig,
|
||||
current = getConfigValue(id);
|
||||
|
||||
// Check if it is a valid key
|
||||
if (current === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (!currentObj[keys[i]]) {
|
||||
currentObj[keys[i]] = {};
|
||||
}
|
||||
currentObj = currentObj[keys[i]];
|
||||
}
|
||||
|
||||
currentObj[lastKey] = value;
|
||||
fs.writeFileSync(configPath, JSON.stringify(localConfig, null, 2));
|
||||
return true;
|
||||
};
|
||||
|
||||
program
|
||||
.version('v' + version)
|
||||
.description('Command line interface for managing deepforge');
|
||||
|
||||
// start
|
||||
var start = function(main, opts) {
|
||||
var child = new forever.Monitor(main, opts);
|
||||
child.on('exit', function () {
|
||||
console.log('Exited after 3 failed restarts');
|
||||
});
|
||||
|
||||
child.start();
|
||||
};
|
||||
|
||||
var isLocalUri = function(protocol, uri) {
|
||||
return uri.indexOf(protocol + '://localhost') === 0 ||
|
||||
uri.indexOf(protocol + '://127.0.0.1') === 0;
|
||||
};
|
||||
|
||||
var checkMongo = function(args) {
|
||||
// check the webgme config
|
||||
var gmeConfig = require('../config'),
|
||||
mongoUri = gmeConfig.mongo.uri;
|
||||
|
||||
if (isLocalUri('mongodb', mongoUri)) {
|
||||
// Make sure mongo is running locally (using pgrep)
|
||||
try {
|
||||
execSync('pgrep mongod').toString();
|
||||
console.log('MongoDB is already running!');
|
||||
} catch (e) { // no pIds
|
||||
console.log('Starting MongoDB...');
|
||||
startMongo(args, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var startMongo = function(args, silent) {
|
||||
var job = spawn('mongod', ['--dbpath', p(config.mongo.dir)], {
|
||||
cwd: process.env.HOME
|
||||
});
|
||||
if (!silent) {
|
||||
job.stdout.on('data',
|
||||
data => process.stdout.write(data.toString()));
|
||||
}
|
||||
job.on('error', err => {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.log('Could not find MongoDB. Is it installed?');
|
||||
if (!args.mongo) {
|
||||
console.log('Otherwise, set MONGO_URI to the desired mongo uri and try again:');
|
||||
console.log('');
|
||||
console.log(' MONGO_URI=mongodb://some.other.ip:27017' +
|
||||
`/deepforge deepforge ${process.argv.slice(2).join(' ')}`);
|
||||
console.log('');
|
||||
}
|
||||
} else {
|
||||
console.log('Error encountered while starting MongoDB');
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
job.stderr.on('data', data => {
|
||||
var msg = 'mongodb: ' + data;
|
||||
process.stdout.write(msg);
|
||||
});
|
||||
job.on('exit', code => {
|
||||
if (code) {
|
||||
console.log('MongoDB closed w/ error code: ' + code);
|
||||
}
|
||||
});
|
||||
};
|
||||
var checkTorch = function() {
|
||||
return new Promise(_checkTorch)
|
||||
.catch(() => 'Torch installation failed');
|
||||
};
|
||||
|
||||
var _checkTorch = function(resolve, reject) {
|
||||
var result = childProcess.spawnSync('th', ['--help']),
|
||||
tgtDir = p(config.torch.dir),
|
||||
cmds;
|
||||
|
||||
if (result.error) {
|
||||
// Try to install torch
|
||||
console.log(`Torch7 not found. Installing to ${tgtDir}...`);
|
||||
|
||||
cmds = [
|
||||
`git clone https://github.com/torch/distro.git ${tgtDir} --recursive`,
|
||||
`cd ${tgtDir}`,
|
||||
'bash install-deps',
|
||||
'./install.sh'
|
||||
];
|
||||
|
||||
spawnMany(cmds,
|
||||
() => {
|
||||
storeConfig('torch.dir', tgtDir);
|
||||
resolve(true);
|
||||
},
|
||||
reject
|
||||
);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
};
|
||||
|
||||
var spawnMany = function(cmds, succ, err) {
|
||||
var rawCmd,
|
||||
cmd,
|
||||
args,
|
||||
job;
|
||||
|
||||
if (cmds.length === 0) {
|
||||
return succ();
|
||||
}
|
||||
|
||||
rawCmd = cmds.shift();
|
||||
args = rawCmd.split(' ');
|
||||
cmd = args.shift();
|
||||
job = spawn(cmd, args);
|
||||
job.stdout.on('data', data => process.stdout.write(data));
|
||||
job.stderr.on('data', data => process.stderr.write(data));
|
||||
job.on('close', code => {
|
||||
if (code) {
|
||||
console.log(`${rawCmd} failed w/ error code ${code}`);
|
||||
err(code, rawCmd);
|
||||
} else {
|
||||
spawnMany(cmds, succ, err);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
program.command('start')
|
||||
.description('start deepforge locally (default) or specific components')
|
||||
.option('-p, --port <port>', 'specify the port to use')
|
||||
.option('-s, --server', 'start the server')
|
||||
.option('-w, --worker [url]', 'start a worker and connect to given url. Defaults to local deepforge')
|
||||
.option('-m, --mongo', 'start MongoDB')
|
||||
.action(args => {
|
||||
var main = path.join(__dirname, 'start-local.js'),
|
||||
opts;
|
||||
|
||||
opts = {
|
||||
max: 3,
|
||||
args: []
|
||||
};
|
||||
|
||||
if (args.port) {
|
||||
opts.env = {
|
||||
PORT: args.port
|
||||
};
|
||||
}
|
||||
|
||||
if (args.server) {
|
||||
checkMongo(args);
|
||||
main = path.join(__dirname, '..', 'app.js');
|
||||
start(main, opts);
|
||||
}
|
||||
|
||||
if (args.worker) {
|
||||
checkTorch().then(() => {
|
||||
main = path.join(__dirname, 'start-worker.js');
|
||||
if (args.worker !== true) {
|
||||
opts.args.push(args.worker);
|
||||
}
|
||||
start(main, opts);
|
||||
});
|
||||
}
|
||||
|
||||
if (args.mongo) {
|
||||
startMongo(args);
|
||||
}
|
||||
|
||||
if (!args.server && !args.worker && !args.mongo) {
|
||||
// Starting everything
|
||||
checkMongo(args);
|
||||
checkTorch().then(() => start(main, opts));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// update
|
||||
program
|
||||
.command('update')
|
||||
.description('upgrade deepforge to latest version')
|
||||
.option('-g, --git', 'update tracking the git repo')
|
||||
.option('-t, --torch', 'update torch installation')
|
||||
.option('-s, --server', 'update deepforge')
|
||||
.action(args => {
|
||||
var pkg = 'deepforge',
|
||||
job,
|
||||
latestVersion;
|
||||
|
||||
// Install the project
|
||||
if (!args.torch || args.server) {
|
||||
|
||||
if (args.git) {
|
||||
pkg = 'dfst/deepforge';
|
||||
} else {
|
||||
// Check the version
|
||||
try {
|
||||
latestVersion = execSync('npm show deepforge version')
|
||||
.toString().replace(/\s+$/, '');
|
||||
|
||||
if (latestVersion === version) {
|
||||
console.log('Already up-to-date');
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Could not retrieve the latest deepforge version');
|
||||
}
|
||||
}
|
||||
|
||||
job = spawn('npm', ['install', '-g', pkg]);
|
||||
job.stdout.on('data', data => process.stdout.write(data.toString()));
|
||||
job.stderr.on('data', data => process.stderr.write(data.toString()));
|
||||
job.on('close', code => {
|
||||
if (!code) {
|
||||
console.log('Upgrade successful!');
|
||||
} else {
|
||||
console.log('Upgrade failed w/ error code: ' + code);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (args.torch || !args.server) {
|
||||
// Update torch
|
||||
checkTorch().then(justInstalled => {
|
||||
if (!justInstalled) {
|
||||
// Upgrade torch
|
||||
console.log('Upgrading torch...');
|
||||
job = spawn('bash', ['./update.sh'], {
|
||||
cwd: p(config.torch.dir)
|
||||
});
|
||||
job.stdout.on('data', data => process.stdout.write(data.toString()));
|
||||
job.stderr.on('data', data => process.stderr.write(data.toString()));
|
||||
job.on('close', code => {
|
||||
if (!code) {
|
||||
console.log('Upgrade successful!');
|
||||
} else {
|
||||
console.log('Upgrade failed w/ error code: ' + code);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// uninstall command
|
||||
program
|
||||
.command('uninstall')
|
||||
.description('uninstall deepforge from the system')
|
||||
.option('-t, --torch', 'uninstall torch')
|
||||
.option('-c, --clean', 'uninstall deepforge, torch and all associated data/config')
|
||||
.action(opts => {
|
||||
if (opts.torch || opts.clean) {
|
||||
if (opts.torch) {
|
||||
console.log(`uninstalling torch at ${p(config.torch.dir)}`);
|
||||
}
|
||||
fs.unlinkSync(p(config.torch.dir));
|
||||
}
|
||||
|
||||
if (opts.clean) { // remove the .deepforge directory
|
||||
console.log('removing config and data files...');
|
||||
fs.unlinkSync(p(config.mongo.dir));
|
||||
fs.unlinkSync(p(configDir));
|
||||
}
|
||||
|
||||
if (!opts.torch || opts.clean) { // uninstall deepforge
|
||||
spawnMany(
|
||||
['npm uninstall -g deepforge'],
|
||||
() => console.log('deepforge has been uninstalled!'),
|
||||
() => console.log('uninstall failed')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// config
|
||||
program
|
||||
.command('config [key] [value]')
|
||||
.description('read or edit config options (omit "value" to see current value)')
|
||||
.action(key => {
|
||||
var value = program.args[1],
|
||||
success;
|
||||
|
||||
if (value) { // write a value
|
||||
success = storeConfig(key, value);
|
||||
if (success) {
|
||||
console.log('Config has been updated!');
|
||||
}
|
||||
} else if (key) { // read a single value
|
||||
value = getConfigValue(key);
|
||||
if (value === null) {
|
||||
console.log(`Invalid config value: "${key}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
value = JSON.stringify(value, null, 2);
|
||||
}
|
||||
|
||||
console.log(value);
|
||||
} else { // print entire config
|
||||
console.log(`Current config:\n${JSON.stringify(config, null, 2)}`);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(cmd) {
|
||||
var cmds = cmd.split(/\s+/).filter(w => !!w);
|
||||
cmds.unshift('node');
|
||||
cmds.unshift('./bin/deepforge');
|
||||
program.parse(cmds);
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
program.parse(process.argv);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ var spawn = require('child_process').spawn,
|
||||
execJob,
|
||||
workerJob = null;
|
||||
|
||||
process.env.NODE_ENV = 'local';
|
||||
execJob = spawn('npm', [
|
||||
'start'
|
||||
]);
|
||||
|
||||
+13
-3
@@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
/*globals process, __dirname, require*/
|
||||
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
spawn = require('child_process').spawn,
|
||||
childProcess = require('child_process'),
|
||||
spawn = childProcess.spawn,
|
||||
projectConfig = require(__dirname + '/../config'),
|
||||
executorSrc = path.join(__dirname, '..', 'node_modules', 'webgme', 'src',
|
||||
'server', 'middleware', 'executor', 'worker'),
|
||||
@@ -12,13 +13,22 @@ var path = require('path'),
|
||||
address,
|
||||
config = {};
|
||||
|
||||
// Check torch support
|
||||
var result = childProcess.spawnSync('th', ['--help']);
|
||||
if (result.error) {
|
||||
console.error('Checking Torch7 dependency failed. Do you have Torch7 installed ' +
|
||||
'and in your PATH?\n\nFor Torch7 installation instructions, check out ' +
|
||||
'http://torch.ch/docs/getting-started.html');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var startExecutor = function() {
|
||||
// Start the executor
|
||||
var execJob = spawn('node', [
|
||||
'node_worker.js',
|
||||
workerConfigPath,
|
||||
workerTmp
|
||||
]);
|
||||
]);
|
||||
execJob.stdout.pipe(process.stdout);
|
||||
execJob.stderr.pipe(process.stderr);
|
||||
};
|
||||
|
||||
+31
-14
@@ -1,8 +1,26 @@
|
||||
{
|
||||
"AutoViz": {
|
||||
"preloadIds": [
|
||||
"ArchEditor",
|
||||
"PipelineEditor",
|
||||
"OperationEditor",
|
||||
"ExecutionView"
|
||||
]
|
||||
},
|
||||
"ArchEditor": {
|
||||
"hotkeys": "none",
|
||||
"LayerColors": {}
|
||||
},
|
||||
"BreadcrumbHeader": {
|
||||
"pathRule": "history"
|
||||
},
|
||||
"FloatingActionButton": {
|
||||
"hideOnEmpty": true
|
||||
},
|
||||
"GenericUIProjectNavigatorController": {
|
||||
"rootMenuClass": "deepforge-logo",
|
||||
"rootDisplayName": "DeepForge"
|
||||
},
|
||||
"CHFLayout": {
|
||||
"panels": [
|
||||
{
|
||||
@@ -13,7 +31,7 @@
|
||||
},
|
||||
{
|
||||
"id": "Footer",
|
||||
"panel": "FooterControls/FooterControlsPanel",
|
||||
"panel": "Footer/FooterPanel",
|
||||
"container": "footer",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
@@ -36,27 +54,26 @@
|
||||
"nodes": [
|
||||
{
|
||||
"nodeName": "MyArchitectures",
|
||||
"title": "Architectures",
|
||||
"icon": "shuffle",
|
||||
"rank": 1,
|
||||
"description": "Neural network architectures are stored here and can be used in pipelines."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyExecutions",
|
||||
"description": "Executions are read-only snapshots of pipelines that have been executed. Past and current executing pipelines are stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyPipelines",
|
||||
"description": "Pipelines are used for training, testing and ensembling models."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyOperations",
|
||||
"description": "Operations are the building blocks of pipelines. Custom operations can be created and stored here."
|
||||
"title": "Pipelines",
|
||||
"color": "blue-grey",
|
||||
"icon": "input",
|
||||
"rank": 3,
|
||||
"description": "Pipelines compose operations together to effectively train, test and/or ensemble models."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyArtifacts",
|
||||
"title": "Artifacts",
|
||||
"icon": "view_quilt",
|
||||
"color": "blue-grey",
|
||||
"rank": 5,
|
||||
"description": "Artifacts from pipeline executions are stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyDataTypes",
|
||||
"description": "Custom defined data types are stored here."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
var config = require('./config.webgme'),
|
||||
validateConfig = require('webgme/config/validator');
|
||||
|
||||
require('dotenv').load();
|
||||
require('dotenv').load({silent: true});
|
||||
|
||||
// Add/overwrite any additional settings here
|
||||
config.server.port = process.env.PORT || config.server.port;
|
||||
config.server.port = +process.env.PORT || config.server.port;
|
||||
config.mongo.uri = process.env.MONGO_URI || config.mongo.uri;
|
||||
config.requirejsPaths.deepforge = './src/common';
|
||||
config.seedProjects.defaultProject = 'project';
|
||||
@@ -15,6 +16,9 @@ config.plugin.allowBrowserExecution = true;
|
||||
config.plugin.allowServerExecution = true;
|
||||
|
||||
config.executor.enable = true;
|
||||
config.executor.clearOldDataAtStartUp = true;
|
||||
|
||||
config.visualization.extraCss.push('deepforge/styles/global.css');
|
||||
|
||||
validateConfig(config);
|
||||
module.exports = config;
|
||||
|
||||
@@ -9,8 +9,7 @@ var config = require('./config.base'),
|
||||
// config.mongo.uri = 'mongodb://127.0.0.1:27017/webgme_my_app';
|
||||
|
||||
// Seeds for development are prefixed with 'dev'
|
||||
config.seedProjects.basePaths = config.seedProjects.basePaths
|
||||
.filter(path => path.indexOf('dev') === -1);
|
||||
config.seedProjects.basePaths = ['src/seeds/project', 'src/seeds/cifar10'];
|
||||
|
||||
validateConfig(config);
|
||||
module.exports = config;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Config for running deepforge w/ one local worker
|
||||
// jshint node: true
|
||||
'use strict';
|
||||
|
||||
var config = require('./config.default'),
|
||||
validateConfig = require('webgme/config/validator');
|
||||
|
||||
// Turn up the worker polling rate
|
||||
config.executor.workerRefreshInterval = 150;
|
||||
config.executor.clearOldDataAtStartUp = true,
|
||||
|
||||
validateConfig(config);
|
||||
module.exports = config;
|
||||
@@ -15,12 +15,11 @@ config.visualization.decoratorPaths.push('src/decorators');
|
||||
config.visualization.decoratorPaths.push('node_modules/webgme-easydag/src/decorators');
|
||||
config.seedProjects.basePaths.push('src/seeds/nn');
|
||||
config.seedProjects.basePaths.push('src/seeds/devTests');
|
||||
config.seedProjects.basePaths.push('src/seeds/devMinimal');
|
||||
config.seedProjects.basePaths.push('src/seeds/devUtilTests');
|
||||
config.seedProjects.basePaths.push('src/seeds/pipeline');
|
||||
config.seedProjects.basePaths.push('src/seeds/devPipelineTests');
|
||||
config.seedProjects.basePaths.push('src/seeds/demo');
|
||||
config.seedProjects.basePaths.push('src/seeds/project');
|
||||
config.seedProjects.basePaths.push('src/seeds/cifar10');
|
||||
|
||||
|
||||
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
# Things to install:
|
||||
# - nvm
|
||||
|
||||
command -v git >/dev/null 2>&1 || { echo >&2 "I require git but it's not installed. Aborting."; exit 1; }
|
||||
|
||||
echo >&2 "Checking DeepForge dependencies...";
|
||||
command -v th >/dev/null 2>&1 || {
|
||||
# No torch!
|
||||
echo >&2 "Torch is not found. Installing...";
|
||||
git clone https://github.com/torch/distro.git ~/torch --recursive;
|
||||
cd ~/torch; bash install-deps;
|
||||
./install.sh;
|
||||
}
|
||||
|
||||
# profile (bash, zsh, profile, etc) borrowed from nvm's installer
|
||||
detect_profile() {
|
||||
if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
|
||||
echo "$PROFILE"
|
||||
return
|
||||
fi
|
||||
|
||||
DETECTED_PROFILE=''
|
||||
local SHELLTYPE
|
||||
SHELLTYPE="$(basename "/$SHELL")"
|
||||
|
||||
if [ "$SHELLTYPE" = "bash" ]; then
|
||||
if [ -f "$HOME/.bashrc" ]; then
|
||||
DETECTED_PROFILE="$HOME/.bashrc"
|
||||
elif [ -f "$HOME/.bash_profile" ]; then
|
||||
DETECTED_PROFILE="$HOME/.bash_profile"
|
||||
fi
|
||||
elif [ "$SHELLTYPE" = "zsh" ]; then
|
||||
DETECTED_PROFILE="$HOME/.zshrc"
|
||||
fi
|
||||
|
||||
if [ -z "$DETECTED_PROFILE" ]; then
|
||||
if [ -f "$HOME/.profile" ]; then
|
||||
DETECTED_PROFILE="$HOME/.profile"
|
||||
elif [ -f "$HOME/.bashrc" ]; then
|
||||
DETECTED_PROFILE="$HOME/.bashrc"
|
||||
elif [ -f "$HOME/.bash_profile" ]; then
|
||||
DETECTED_PROFILE="$HOME/.bash_profile"
|
||||
elif [ -f "$HOME/.zshrc" ]; then
|
||||
DETECTED_PROFILE="$HOME/.zshrc"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
detect_profile
|
||||
|
||||
command -v node >/dev/null 2>&1 || {
|
||||
# No node! Install nvm
|
||||
echo >&2 "NodeJS is not found. Installing (using nvm)...";
|
||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash;
|
||||
source $DETECTED_PROFILE
|
||||
. $NVM_DIR/nvm.sh
|
||||
|
||||
# Install nodejs v6.2.0
|
||||
echo "Installing nodejs v6.2.0"
|
||||
nvm install v6.2.0
|
||||
nvm alias default v6.2.0
|
||||
|
||||
# Install npm@2
|
||||
npm install npm@2 -g
|
||||
}
|
||||
|
||||
command -v node >/dev/null 2>&1 || {
|
||||
# No mongod!
|
||||
echo >&2 "MongoDB is not found. Installing...";
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
brew install mongodb
|
||||
elif [[ "$(uname)" == 'Linux' ]]; then
|
||||
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
# this will get the required information without dirtying any env state
|
||||
DIST_VERS="$( ( . /etc/os-release &>/dev/null
|
||||
echo "$ID $VERSION_ID") )"
|
||||
DISTRO="${DIST_VERS%% *}" # get our distro name
|
||||
VERSION="${DIST_VERS##* }" # get our version number
|
||||
elif [[ -r /etc/lsb-release ]]; then
|
||||
DIST_VERS="$( ( . /etc/lsb-release &>/dev/null
|
||||
echo "${DISTRIB_ID,,} $DISTRIB_RELEASE") )"
|
||||
DISTRO="${DIST_VERS%% *}" # get our distro name
|
||||
VERSION="${DIST_VERS##* }" # get our version number
|
||||
else # well, I'm out of ideas for now
|
||||
echo '==> Failed to determine distro and version.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect archlinux
|
||||
if [[ "$DISTRO" = "arch" ]]; then
|
||||
distribution="archlinux"
|
||||
sudo pacman -S mongodb
|
||||
# Detect Ubuntu
|
||||
elif [[ "$DISTRO" = "ubuntu" ]]; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get install mongodb
|
||||
else
|
||||
NEEDS_MONGO=true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
echo >&2 "Installing DeepForge...";
|
||||
|
||||
# Clone deepforge into ~/deepforge
|
||||
git clone https://github.com/dfst/deepforge ~/deepforge
|
||||
cd ~/deepforge
|
||||
npm install
|
||||
|
||||
mkdir ~/deepforge/data 2> /dev/null
|
||||
|
||||
echo "Final Installation steps:"
|
||||
echo " 1) Close and re-open your terminal"
|
||||
echo " (or run \"source $DETECTED_PROFILE\")"
|
||||
|
||||
if [[ $NEEDS_MONGO ]]; then
|
||||
echo " 2) Install MongoDB for your OS"
|
||||
echo " (available at https://www.mongodb.com/download-center)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Then run DeepForge!"
|
||||
echo " 1) make sure MongoDB is running locally"
|
||||
echo " (start mongo w/ \"mongod --dbpath ~/deepforge/data\")"
|
||||
echo " 2) Run \"npm run local\" from ~/deepforge"
|
||||
+19
-8
@@ -1,28 +1,39 @@
|
||||
{
|
||||
"name": "deepforge",
|
||||
"bin": {
|
||||
"deepforge": "./bin/deepforge"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"start-dev": "NODE_ENV=dev node app.js",
|
||||
"worker": "node ./bin/start-worker.js",
|
||||
"local": "node ./bin/start-local.js",
|
||||
"test": "node ./node_modules/mocha/bin/mocha --recursive test"
|
||||
"worker": "node ./bin/start-worker.js",
|
||||
"test": "node ./node_modules/mocha/bin/mocha --recursive test",
|
||||
"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.4.0",
|
||||
"version": "0.10.0",
|
||||
"dependencies": {
|
||||
"commander": "^2.9.0",
|
||||
"dotenv": "^2.0.0",
|
||||
"exists-file": "^2.1.0",
|
||||
"forever-monitor": "^1.7.0",
|
||||
"lodash.assign": "^4.0.9",
|
||||
"lodash.difference": "^4.1.2",
|
||||
"nodemon": "^1.9.2",
|
||||
"webgme": "^2.0.0",
|
||||
"webgme-autoviz": "^2.0.3",
|
||||
"webgme-breadcrumbheader": "^2.0.0",
|
||||
"webgme-autoviz": "dfst/webgme-autoviz",
|
||||
"webgme-breadcrumbheader": "^2.1.0",
|
||||
"webgme-chflayout": "^2.0.0",
|
||||
"webgme-easydag": "dfst/webgme-easydag",
|
||||
"webgme-fab": "^2.0.2",
|
||||
"webgme-fab": "dfst/webgme-fab",
|
||||
"webgme-simple-nodes": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.0.0",
|
||||
"jszip": "^2.5.0",
|
||||
"mocha": "^2.2.5",
|
||||
"rimraf": "^2.4.0",
|
||||
"chai": "^3.0.0"
|
||||
"mockery": "^1.7.0",
|
||||
"rimraf": "^2.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/* globals define */
|
||||
define({
|
||||
LINE_OFFSET: 'lineOffset',
|
||||
|
||||
// DeepForge metadata creation in dist execution
|
||||
START_CMD: 'deepforge-cmd',
|
||||
GRAPH_CREATE: 'GRAPH',
|
||||
GRAPH_PLOT: 'PLOT',
|
||||
GRAPH_CREATE_LINE: 'LINE'
|
||||
});
|
||||
@@ -0,0 +1,294 @@
|
||||
/* globals Materialize, WebGMEGlobal, define*/
|
||||
// This file creates the DeepForge namespace and defines basic actions
|
||||
define([
|
||||
'js/RegistryKeys',
|
||||
'js/Panels/MetaEditor/MetaEditorConstants',
|
||||
'js/Constants'
|
||||
], function(
|
||||
REGISTRY_KEYS,
|
||||
META_CONSTANTS,
|
||||
CONSTANTS
|
||||
) {
|
||||
var DeepForge = {},
|
||||
placesTerritoryId,
|
||||
client = WebGMEGlobal.Client,
|
||||
PLACE_NAMES;
|
||||
|
||||
// Helper functions
|
||||
var addToMetaSheet = function(nodeId, metasheetName) {
|
||||
var root = client.getNode(CONSTANTS.PROJECT_ROOT_ID),
|
||||
metatabs = root.getRegistry(REGISTRY_KEYS.META_SHEETS),
|
||||
metatab = metatabs.find(tab => tab.title === metasheetName) || metatabs[0],
|
||||
metatabId = metatab.SetID;
|
||||
|
||||
// Add to the general meta
|
||||
client.addMember(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
nodeId,
|
||||
META_CONSTANTS.META_ASPECT_SET_NAME
|
||||
);
|
||||
client.setMemberRegistry(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
nodeId,
|
||||
META_CONSTANTS.META_ASPECT_SET_NAME,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
{
|
||||
x: 100,
|
||||
y: 100
|
||||
}
|
||||
);
|
||||
|
||||
// Add to the specific sheet
|
||||
client.addMember(CONSTANTS.PROJECT_ROOT_ID, nodeId, metatabId);
|
||||
client.setMemberRegistry(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
nodeId,
|
||||
metatabId,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
{
|
||||
x: 100,
|
||||
y: 100
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var createNamedNode = function(baseId, parentId, isMeta) {
|
||||
var newId = client.createChild({parentId, baseId}),
|
||||
baseNode = client.getNode(baseId),
|
||||
basename = 'New' + baseNode.getAttribute('name'),
|
||||
newName = getUniqueName(parentId, basename);
|
||||
|
||||
// If instance, make the first char lowercase
|
||||
if (!isMeta) {
|
||||
newName = newName.substring(0, 1).toLowerCase() + newName.substring(1);
|
||||
}
|
||||
|
||||
// Set isAbstract false, if needed
|
||||
if (baseNode.getRegistry('isAbstract')) {
|
||||
client.setRegistry(newId, 'isAbstract', false);
|
||||
}
|
||||
|
||||
client.setAttributes(newId, 'name', newName);
|
||||
return newId;
|
||||
};
|
||||
|
||||
var getUniqueName = function(parentId, basename) {
|
||||
var pNode = client.getNode(parentId),
|
||||
children = pNode.getChildrenIds().map(id => client.getNode(id)),
|
||||
name = basename,
|
||||
exists = {},
|
||||
i = 2;
|
||||
|
||||
children.forEach(child => exists[child.getAttribute('name')] = true);
|
||||
|
||||
while (exists[name]) {
|
||||
name = basename + '_' + i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
//////////////////// DeepForge places detection ////////////////////
|
||||
var TYPE_TO_CONTAINER = {
|
||||
|
||||
Architecture: 'MyArchitectures',
|
||||
Pipeline: 'MyPipelines',
|
||||
Execution: 'MyExecutions',
|
||||
Layer: 'MyLayers',
|
||||
Artifact: 'MyArtifacts',
|
||||
Operation: 'MyOperations',
|
||||
Primitive: 'MyDataTypes',
|
||||
Complex: 'MyDataTypes'
|
||||
};
|
||||
|
||||
PLACE_NAMES = Object.keys(TYPE_TO_CONTAINER).map(key => TYPE_TO_CONTAINER[key]);
|
||||
|
||||
// Add DeepForge directories
|
||||
var updateDeepForgeNamespace = function() {
|
||||
var territory = {};
|
||||
|
||||
DeepForge.places = {};
|
||||
|
||||
// Create a territory
|
||||
if (placesTerritoryId) {
|
||||
client.removeUI(placesTerritoryId);
|
||||
}
|
||||
|
||||
territory[CONSTANTS.PROJECT_ROOT_ID] = {children: 1};
|
||||
placesTerritoryId = client.addUI(null, updateDeepForgePlaces);
|
||||
|
||||
// Update the territory (load the main places)
|
||||
client.updateTerritory(placesTerritoryId, territory);
|
||||
};
|
||||
|
||||
var updateDeepForgePlaces = function(events) {
|
||||
var nodeIdsByName = {},
|
||||
nodes;
|
||||
|
||||
nodes = events
|
||||
// Remove root node, complete event and update/unload events
|
||||
.filter(event => event.eid && event.eid !== CONSTANTS.PROJECT_ROOT_ID)
|
||||
.filter(event => event.etype === CONSTANTS.TERRITORY_EVENT_LOAD)
|
||||
.map(event => client.getNode(event.eid));
|
||||
|
||||
nodes.forEach(node =>
|
||||
nodeIdsByName[node.getAttribute('name')] = node.getId());
|
||||
|
||||
PLACE_NAMES.forEach(name => DeepForge.places[name] = nodeIdsByName[name]);
|
||||
|
||||
// Remove the territory
|
||||
client.removeUI(placesTerritoryId);
|
||||
placesTerritoryId = null;
|
||||
};
|
||||
|
||||
//////////////////// DeepForge creation actions ////////////////////
|
||||
var instances = [
|
||||
'Architecture',
|
||||
'Pipeline'
|
||||
],
|
||||
metaNodes = [
|
||||
'Operation',
|
||||
'Primitive',
|
||||
'Complex'
|
||||
];
|
||||
|
||||
var createNew = function(type, metasheetName) {
|
||||
var parentId,
|
||||
placeName = TYPE_TO_CONTAINER[type],
|
||||
newId,
|
||||
baseId,
|
||||
msg = `Created new ${type + (metasheetName ? ' prototype' : '')}`;
|
||||
|
||||
baseId = client.getAllMetaNodes()
|
||||
.find(node => node.getAttribute('name') === type)
|
||||
.getId();
|
||||
|
||||
// Look up the parent container
|
||||
parentId = DeepForge.places[placeName];
|
||||
|
||||
client.startTransaction(msg);
|
||||
newId = createNamedNode(baseId, parentId, !!metasheetName);
|
||||
|
||||
if (metasheetName) {
|
||||
addToMetaSheet(newId, metasheetName);
|
||||
}
|
||||
|
||||
client.completeTransaction();
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(newId);
|
||||
return newId;
|
||||
};
|
||||
|
||||
var createCustomLayer = function(typeName) {
|
||||
var metanodes = client.getAllMetaNodes(),
|
||||
msg = `Created new custom ${typeName} layer`,
|
||||
newId,
|
||||
customLayerId,
|
||||
baseId,
|
||||
name,
|
||||
i = metanodes.length;
|
||||
|
||||
while (i-- && !(baseId && customLayerId)) {
|
||||
name = metanodes[i].getAttribute('name');
|
||||
if (name === 'CustomLayer') {
|
||||
customLayerId = metanodes[i].getId();
|
||||
} else if (name === typeName) {
|
||||
baseId = metanodes[i].getId();
|
||||
}
|
||||
}
|
||||
|
||||
client.startTransaction(msg);
|
||||
|
||||
newId = createNamedNode(baseId, DeepForge.places.MyLayers, true);
|
||||
addToMetaSheet(newId, 'CustomLayers');
|
||||
client.addMixin(newId, customLayerId);
|
||||
client.setRegistry(newId, REGISTRY_KEYS.IS_ABSTRACT, false);
|
||||
|
||||
client.completeTransaction();
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(newId);
|
||||
};
|
||||
|
||||
// Creating Artifacts
|
||||
var UPLOAD_PLUGIN = 'ImportArtifact',
|
||||
DATA_TYPE_CONFIG = {
|
||||
name: 'dataTypeId',
|
||||
displayName: 'Data Type Id',
|
||||
valueType: 'string',
|
||||
valueItems: []
|
||||
};
|
||||
|
||||
var uploadArtifact = function() {
|
||||
// Get the data types
|
||||
var dataBase,
|
||||
dataBaseId,
|
||||
metanodes = client.getAllMetaNodes(),
|
||||
dataTypes = [];
|
||||
|
||||
dataBase = metanodes.find(n => n.getAttribute('name') === 'Data');
|
||||
|
||||
if (!dataBase) {
|
||||
this.logger.error('Could not find the base Data node!');
|
||||
return;
|
||||
}
|
||||
|
||||
dataBaseId = dataBase.getId();
|
||||
dataTypes = metanodes.filter(n => client.isTypeOf(n.getId(), dataBaseId))
|
||||
.filter(n => !n.getRegistry('isAbstract'))
|
||||
.map(node => node.getAttribute('name'));
|
||||
|
||||
//this.logger.info(`Found ${dataTypes.length} data types`);
|
||||
|
||||
// Add the target type to the pluginMetadata... hacky :/
|
||||
var metadata = WebGMEGlobal.allPluginsMetadata[UPLOAD_PLUGIN],
|
||||
config = metadata.configStructure
|
||||
.find(opt => opt.name === DATA_TYPE_CONFIG.name);
|
||||
|
||||
if (!config) {
|
||||
config = DATA_TYPE_CONFIG;
|
||||
WebGMEGlobal.allPluginsMetadata[UPLOAD_PLUGIN].configStructure.push(config);
|
||||
}
|
||||
|
||||
config.valueItems = dataTypes;
|
||||
config.value = dataTypes[0];
|
||||
|
||||
WebGMEGlobal.InterpreterManager.configureAndRun(metadata, (result) => {
|
||||
var msg = 'Artifact upload complete!';
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
if (!result.success) {
|
||||
msg = `Artifact upload failed: ${result.error}`;
|
||||
}
|
||||
Materialize.toast(msg, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
DeepForge.last = {};
|
||||
DeepForge.create = {};
|
||||
instances.forEach(type => {
|
||||
DeepForge.create[type] = function() {
|
||||
return createNew.call(null, type);
|
||||
};
|
||||
});
|
||||
|
||||
metaNodes.forEach(type => {
|
||||
DeepForge.create[type] = function() {
|
||||
return createNew.call(null, type, type);
|
||||
};
|
||||
});
|
||||
|
||||
DeepForge.create.Layer = createCustomLayer;
|
||||
DeepForge.create.Artifact = uploadArtifact;
|
||||
|
||||
//////////////////// DeepForge prev locations ////////////////////
|
||||
// Update DeepForge on project changed
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_PROJECT_NAME,
|
||||
updateDeepForgeNamespace, null);
|
||||
|
||||
// define DeepForge globally
|
||||
window.DeepForge = DeepForge;
|
||||
|
||||
return DeepForge;
|
||||
});
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -2687,6 +2687,7 @@ function LuaContext(){
|
||||
}
|
||||
exports.stdlib(_G, helpers)();
|
||||
}
|
||||
this.__helpers = helpers;
|
||||
}
|
||||
|
||||
LuaContext.prototype = {}
|
||||
|
||||
+83
-10
@@ -9,9 +9,24 @@ define([
|
||||
};
|
||||
|
||||
// Should these be in lua?
|
||||
LocalExecutor.prototype.BlobLoader = function(node) {
|
||||
var hash = this.core.getAttribute(node, 'data');
|
||||
return this.getOutputs(node)
|
||||
LocalExecutor.prototype.ArtifactLoader = function(node) {
|
||||
// Get the hash from the output node
|
||||
var hash;
|
||||
return this.core.loadChildren(node)
|
||||
.then(cntrs => {
|
||||
// Get the output container and load it's children
|
||||
var output = cntrs
|
||||
.find(cntr => {
|
||||
var metaNode = this.core.getMetaType(cntr),
|
||||
metaName = this.core.getAttribute(metaNode, 'name');
|
||||
return metaName === 'Outputs';
|
||||
});
|
||||
return this.core.loadChildren(output);
|
||||
})
|
||||
.then(dataNodes => {
|
||||
hash = this.core.getAttribute(dataNodes[0], 'data');
|
||||
return this.getOutputs(node);
|
||||
})
|
||||
.then(outputTuples => {
|
||||
var outputs = outputTuples.map(tuple => tuple[2]),
|
||||
paths;
|
||||
@@ -21,12 +36,52 @@ define([
|
||||
this.logger.info(`Loading blob data (${hash}) to ${paths.map(p => `"${p}"`)}`);
|
||||
outputs.forEach(output => this.core.setAttribute(output, 'data', hash));
|
||||
|
||||
// Set the metadata as appropriate
|
||||
// TODO
|
||||
this.onOperationComplete(node);
|
||||
});
|
||||
};
|
||||
|
||||
LocalExecutor.prototype.ArtifactFinder = function(node) {
|
||||
// Check the save dir for a node with the given name
|
||||
// that has the given type
|
||||
var hash,
|
||||
typeId = this.core.getPointerPath(node, 'type'),
|
||||
type,
|
||||
artifactName = this.core.getAttribute(node, 'artifactName');
|
||||
|
||||
return this.core.loadByPath(this.rootNode, typeId)
|
||||
.then(_type => {
|
||||
type = _type;
|
||||
return this._getSaveDir();
|
||||
})
|
||||
.then(saveDir => this.core.loadChildren(saveDir))
|
||||
.then(artifacts => {
|
||||
return artifacts.find(artifact =>
|
||||
this.core.getAttribute(artifact, 'name') === artifactName &&
|
||||
this.isMetaTypeOf(artifact, type));
|
||||
})
|
||||
.then(matchingArtifact => {
|
||||
hash = matchingArtifact && this.core.getAttribute(matchingArtifact, 'data');
|
||||
// If no hash, just continue (the subsequent ops will receive 'nil')
|
||||
if (!hash) {
|
||||
return this.onOperationComplete(node);
|
||||
} else {
|
||||
return this.getOutputs(node)
|
||||
.then(outputPairs => {
|
||||
var outputs = outputPairs.map(pair => pair[2]),
|
||||
paths;
|
||||
|
||||
paths = outputs.map(output => this.core.getPath(output));
|
||||
// Get the 'data' hash and store it in the output data ports
|
||||
this.logger.info(`Loading blob data (${hash}) to ${paths.map(p => `"${p}"`)}`);
|
||||
|
||||
outputs.forEach(output => this.core.setAttribute(output, 'data', hash));
|
||||
|
||||
this.onOperationComplete(node);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
LocalExecutor.prototype._getSaveDir = function () {
|
||||
return this.core.loadChildren(this.rootNode)
|
||||
.then(children => {
|
||||
@@ -90,13 +145,31 @@ define([
|
||||
this.logger.info(`saving hashes: ${hashes.map(h => `"${h}"`)}`);
|
||||
this.onOperationComplete(node);
|
||||
});
|
||||
|
||||
// Overwrite existing node w/ this name?
|
||||
// TODO
|
||||
};
|
||||
|
||||
LocalExecutor.TYPES = Object.keys(LocalExecutor.prototype)
|
||||
.filter(name => name.indexOf('_') !== 0);
|
||||
// Helper methods
|
||||
LocalExecutor.prototype.getLocalOperationType = function(node) {
|
||||
var type;
|
||||
for (var i = LocalExecutor.OPERATIONS.length; i--;) {
|
||||
type = LocalExecutor.OPERATIONS[i];
|
||||
if (!this.META[type]) {
|
||||
this.logger.warn(`Missing local operation: ${type}`);
|
||||
continue;
|
||||
}
|
||||
if (this.isMetaTypeOf(node, this.META[type])) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
LocalExecutor.prototype.isLocalOperation = function(node) {
|
||||
return !!this.getLocalOperationType(node);
|
||||
};
|
||||
|
||||
LocalExecutor.OPERATIONS = Object.keys(LocalExecutor.prototype)
|
||||
.filter(name => name.indexOf('_') !== 0)
|
||||
.filter(name => name !== 'isLocalOperation' && name !== 'getLocalOperationType');
|
||||
|
||||
return LocalExecutor;
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
define([
|
||||
'q'
|
||||
], function(
|
||||
Q
|
||||
) {
|
||||
var PtrCodeGen = function() {
|
||||
};
|
||||
|
||||
PtrCodeGen.prototype.getPtrCodeHash = function(ptrId) {
|
||||
return this.core.loadByPath(this.rootNode, ptrId)
|
||||
.then(ptrNode => {
|
||||
// Look up the plugin to use
|
||||
var metanode = this.core.getMetaType(ptrNode),
|
||||
pluginId;
|
||||
|
||||
pluginId = this.core.getRegistry(ptrNode, 'validPlugins').split(' ').shift();
|
||||
this.logger.info(`generating code for ${this.core.getAttribute(ptrNode, 'name')} using ${pluginId}`);
|
||||
|
||||
var context = WebGMEGlobal.Client.getCurrentPluginContext(pluginId);
|
||||
|
||||
context.managerConfig.namespace = this.core.getNamespace(metanode);
|
||||
context.managerConfig.activeNode = this.core.getPath(ptrNode);
|
||||
|
||||
// Load and run the plugin
|
||||
return Q.nfcall(this.executePlugin.bind(this), pluginId, context);
|
||||
})
|
||||
.then(hashes => hashes[0]); // Grab the first asset for now
|
||||
};
|
||||
|
||||
PtrCodeGen.prototype.executePlugin = function(pluginId, config, callback) {
|
||||
// Call the Interpreter manager in a Q.ninvoke friendly way
|
||||
// I need to create a custom context for the given plugin:
|
||||
// - Set the activeNode to the given referenced node
|
||||
// - If the activeNode is namespaced, set META to the given namespace
|
||||
//
|
||||
// FIXME: Check if it is running in the browser or on the server
|
||||
WebGMEGlobal.Client.runBrowserPlugin(pluginId, config, (err, result) => {
|
||||
if (!result.success) {
|
||||
return callback(result.getError());
|
||||
}
|
||||
this.logger.info('Finished calling ' + pluginId);
|
||||
callback(null, result.artifacts);
|
||||
});
|
||||
};
|
||||
|
||||
return PtrCodeGen;
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Audiowide';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Audiowide'), local('Audiowide-Regular'), url(https://fonts.gstatic.com/s/audiowide/v4/7pSgz2MbVvTCvvm7vukSHxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Audiowide';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Audiowide'), local('Audiowide-Regular'), url(https://fonts.gstatic.com/s/audiowide/v4/8XtYtNKEyyZh481XVWfVOltXRa8TVwTICgirnJhmVJw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
|
||||
.deepforge-logo .item-label {
|
||||
font-family: 'Audiowide', cursive;
|
||||
}
|
||||
|
||||
.create-node text {
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
define([
|
||||
'widgets/EasyDAG/Buttons',
|
||||
'widgets/EasyDAG/Icons'
|
||||
], function(
|
||||
EasyDAGButtons,
|
||||
Icons
|
||||
) {
|
||||
|
||||
// Create a GoToBase button
|
||||
var client = WebGMEGlobal.Client;
|
||||
|
||||
var GoToBase = function(params) {
|
||||
// Check if it should be disabled
|
||||
var baseId = this._getBaseId(params.item),
|
||||
base = baseId && client.getNode(baseId);
|
||||
|
||||
if (!params.disabled) {
|
||||
params.disabled = base ? base.isLibraryElement() : true;
|
||||
}
|
||||
EasyDAGButtons.ButtonBase.call(this, params);
|
||||
};
|
||||
|
||||
GoToBase.SIZE = 10;
|
||||
GoToBase.BORDER = 1;
|
||||
GoToBase.prototype.BTN_CLASS = 'go-to-base';
|
||||
GoToBase.prototype = new EasyDAGButtons.ButtonBase();
|
||||
|
||||
GoToBase.prototype._render = function() {
|
||||
var lineRadius = GoToBase.SIZE - GoToBase.BORDER,
|
||||
btnColor = '#90caf9';
|
||||
|
||||
if (this.disabled) {
|
||||
btnColor = '#e0e0e0';
|
||||
}
|
||||
|
||||
this.$el
|
||||
.append('circle')
|
||||
.attr('r', GoToBase.SIZE)
|
||||
.attr('fill', btnColor);
|
||||
|
||||
// Show the 'code' icon
|
||||
Icons.addIcon('code', this.$el, {
|
||||
radius: lineRadius
|
||||
});
|
||||
};
|
||||
|
||||
GoToBase.prototype._onClick = function(item) {
|
||||
var node = client.getNode(item.id),
|
||||
baseId = node.getBaseId();
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(baseId);
|
||||
};
|
||||
|
||||
GoToBase.prototype._getBaseId = function(item) {
|
||||
var n = client.getNode(item.id);
|
||||
return n && n.getBaseId();
|
||||
};
|
||||
|
||||
return {
|
||||
DeleteOne: EasyDAGButtons.DeleteOne,
|
||||
GoToBase: GoToBase
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.node-prompter .scrollbar {
|
||||
fill: #bfbfbf;
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*globals define, d3 */
|
||||
// Given a container and a set of nodes, this will prompt the user
|
||||
// to select one of the set of nodes. This will also need to support
|
||||
// adding a "plus" button for creating new objects in line
|
||||
|
||||
define([
|
||||
'q',
|
||||
'css!./NodePrompter.css'
|
||||
], function(
|
||||
Q
|
||||
) {
|
||||
|
||||
var MARGIN = 15,
|
||||
CLOSING_GRACE = 400,
|
||||
TRANSITION_DURATION = 400;
|
||||
|
||||
var NodePrompter = function(rect, opts) {
|
||||
opts = opts || {};
|
||||
// default options
|
||||
opts.padding = opts.padding || 0;
|
||||
|
||||
this.left = rect.left-opts.padding;
|
||||
this.top = rect.top-opts.padding;
|
||||
this.width = rect.width + 2*opts.padding;
|
||||
this.actualHeight = rect.height + 2*opts.padding;
|
||||
this.height = this.actualHeight; // scroll height
|
||||
this.cx = opts.cx || rect.left + rect.width/2;
|
||||
this.cy = opts.cy || rect.top + rect.height/2;
|
||||
this.active = true;
|
||||
this.onNode = false;
|
||||
this.scrollbar = null;
|
||||
this.scrollPosition = 0;
|
||||
|
||||
var container = document.createElement('div');
|
||||
container.setAttribute('class', 'node-prompter');
|
||||
this.container = container;
|
||||
container.style.width = this.width + 'px';
|
||||
container.style.height = this.height+'px';
|
||||
container.style.position = 'absolute';
|
||||
|
||||
};
|
||||
|
||||
NodePrompter.prototype.prompt = function(nodes, selectFn) {
|
||||
var deferred = Q.defer(),
|
||||
size,
|
||||
cornerRadius = 10;
|
||||
|
||||
this.selectHandler = selectFn;
|
||||
this.svg = d3.select(this.container).append('svg')
|
||||
.attr('width', this.width)
|
||||
.attr('height', this.height)
|
||||
.attr('overflow', 'hidden');
|
||||
|
||||
document.body.appendChild(this.container);
|
||||
|
||||
// Expand the panel
|
||||
this.panel = this.svg.append('rect');
|
||||
this.nodeContainer = this.svg.append('g');
|
||||
|
||||
size = this.initNodes(nodes);
|
||||
this.resize(size.width, size.height);
|
||||
|
||||
// Create the panel
|
||||
this.panel
|
||||
.attr('x', this.cx)
|
||||
.attr('y', this.cy)
|
||||
.attr('rx', 1)
|
||||
.attr('ry', 1)
|
||||
.attr('height', 1)
|
||||
.attr('width', 1)
|
||||
.attr('fill', '#f44336');
|
||||
|
||||
|
||||
this.panel.transition()
|
||||
.delay(50)
|
||||
.duration(TRANSITION_DURATION)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('rx', cornerRadius)
|
||||
.attr('ry', cornerRadius)
|
||||
.attr('height', this.actualHeight)
|
||||
.attr('width', this.width)
|
||||
.attr('fill', '#e0e0e0')
|
||||
.each('end', () => {
|
||||
// Add the given nodes to the panel
|
||||
this.showNodes(nodes, deferred.resolve);
|
||||
// Add scrollbar if height is too large
|
||||
if (this.height > this.actualHeight) {
|
||||
this.createScrollbar(cornerRadius);
|
||||
}
|
||||
});
|
||||
|
||||
// Event handling
|
||||
this.svg.on('mouseout', () => {
|
||||
this.active = false;
|
||||
setTimeout(this.destroyIfInactive.bind(this), CLOSING_GRACE);
|
||||
});
|
||||
this.svg.on('mouseover', () => this.active = true);
|
||||
|
||||
// Return a promise called on 'selected'
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
NodePrompter.prototype.resize = function(width, height) {
|
||||
var dx = this.width - width,
|
||||
maxHeight = this.height,
|
||||
dy;
|
||||
|
||||
this.actualHeight = Math.min(maxHeight, height);
|
||||
dy = this.height - this.actualHeight;
|
||||
|
||||
this.nodes.forEach(node => node.moveBy(-dx/2, 0));
|
||||
this.left += dx;
|
||||
this.top += dy;
|
||||
this.cx -= dx;
|
||||
this.cy -= dy;
|
||||
|
||||
this.container.style.left = this.left + 'px';
|
||||
this.container.style.top = this.top + 'px';
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
};
|
||||
|
||||
NodePrompter.prototype.createScrollbar = function(yMargin) {
|
||||
var width = 4,
|
||||
actualHeight = this.actualHeight-2*yMargin,
|
||||
updateScroll = this.updateScroll.bind(this);
|
||||
|
||||
// Create the scrollbar
|
||||
this.scrollBarHeight = actualHeight/this.height*actualHeight;
|
||||
this.scrollHeight = actualHeight;
|
||||
this.scrollbar = this.svg.append('rect')
|
||||
.attr('class', 'scrollbar')
|
||||
.attr('x', this.width - width)
|
||||
.attr('y', yMargin)
|
||||
.attr('rx', 2)
|
||||
.attr('ry', 2)
|
||||
.attr('height', this.scrollBarHeight)
|
||||
.attr('width', width);
|
||||
|
||||
// Attach scroll handler to the 'panel' rect
|
||||
this.svg
|
||||
.on('zoom', updateScroll)
|
||||
.on('wheel.zoom', updateScroll)
|
||||
.on('mousewheel.zoom', updateScroll)
|
||||
.on('DOMMouseScroll.zoom', updateScroll);
|
||||
};
|
||||
|
||||
NodePrompter.prototype.updateScroll = function() {
|
||||
var delta = d3.event.deltaY,
|
||||
sensitivity = 1,
|
||||
maxScroll = this.scrollHeight - this.scrollBarHeight,
|
||||
containerY,
|
||||
relView;
|
||||
|
||||
this.scrollPosition += delta*sensitivity;
|
||||
this.scrollPosition = Math.max(this.scrollPosition, 0);
|
||||
this.scrollPosition = Math.min(this.scrollPosition, maxScroll);
|
||||
|
||||
this.scrollbar
|
||||
.attr('transform', `translate(0, ${this.scrollPosition})`);
|
||||
|
||||
// Update the translation on the nodeContainer
|
||||
relView = this.scrollPosition/maxScroll;
|
||||
containerY = relView * (this.height-this.actualHeight);
|
||||
this.nodeContainer
|
||||
.attr('transform', `translate(0, -${containerY})`);
|
||||
};
|
||||
|
||||
NodePrompter.prototype.destroyIfInactive = function() {
|
||||
// Verify that is not over any of the displayed nodes
|
||||
if (!this.active && !this.onNode) {
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
NodePrompter.prototype.destroy = function() {
|
||||
this.hideNodes();
|
||||
if (this.scrollbar) {
|
||||
this.scrollbar.remove();
|
||||
}
|
||||
this.panel.transition()
|
||||
.duration(TRANSITION_DURATION)
|
||||
.attr('x', this.cx)
|
||||
.attr('y', this.cy)
|
||||
.attr('rx', 1)
|
||||
.attr('ry', 1)
|
||||
.attr('height', 1)
|
||||
.attr('width', 1)
|
||||
.attr('fill', '#f44336')
|
||||
.each('end', () => {
|
||||
this.container.remove();
|
||||
});
|
||||
};
|
||||
|
||||
NodePrompter.prototype.onSelected = function(container, callback) {
|
||||
// Return the id
|
||||
if (this.selectHandler) {
|
||||
this.selectHandler(container.node, this);
|
||||
} else {
|
||||
this.destroy();
|
||||
return callback(container.node);
|
||||
}
|
||||
};
|
||||
|
||||
NodePrompter.prototype.initNodes = function(nodes) {
|
||||
// For each node, create the containers and position them
|
||||
var decorators = nodes.map(node => new Container(this.nodeContainer, node)),
|
||||
lineGroup,
|
||||
maxLineWidth = this.width - 2*MARGIN,
|
||||
totalWidth = 0,
|
||||
lineWidth,
|
||||
lineStartHeight = MARGIN,
|
||||
lineHeight,
|
||||
cntr,
|
||||
x,y,
|
||||
i = 0;
|
||||
|
||||
// Position the nodes. while we can fit the node on the given line, add it
|
||||
decorators.forEach(d => {
|
||||
d.computeSize(0.25);
|
||||
});
|
||||
|
||||
while (i < decorators.length) {
|
||||
lineGroup = [decorators[i]];
|
||||
lineWidth = decorators[i].width() + MARGIN;
|
||||
lineHeight = decorators[i].height();
|
||||
i++;
|
||||
while (i < decorators.length &&
|
||||
lineWidth + decorators[i].width() + MARGIN < maxLineWidth) {
|
||||
|
||||
lineGroup.push(decorators[i]);
|
||||
lineWidth += decorators[i].width() + MARGIN;
|
||||
lineHeight = Math.max(lineHeight, decorators[i].height());
|
||||
i++;
|
||||
}
|
||||
|
||||
// Get the positions for each
|
||||
lineWidth += MARGIN;
|
||||
totalWidth = Math.max(lineWidth, totalWidth);
|
||||
x = (this.width-lineWidth)/2 + MARGIN;
|
||||
for (var g = 0; g < lineGroup.length; g++) {
|
||||
cntr = lineGroup[g];
|
||||
y = (lineHeight - cntr.height())/2 + lineStartHeight;
|
||||
cntr.goTo(x, y);
|
||||
x += cntr.width() + MARGIN;
|
||||
}
|
||||
|
||||
lineStartHeight += lineHeight + MARGIN;
|
||||
}
|
||||
|
||||
this.nodes = decorators;
|
||||
return {
|
||||
height: lineStartHeight,
|
||||
width: totalWidth
|
||||
};
|
||||
};
|
||||
|
||||
NodePrompter.prototype.showNodes = function(nodes, callback) {
|
||||
this.nodes.forEach(d => {
|
||||
d.render(0.25);
|
||||
d.$el.on('mouseover', () => this.onNode = true);
|
||||
d.$el.on('mouseout', () => this.onNode = false);
|
||||
d.$el.on('click', () => this.onSelected(d, callback));
|
||||
});
|
||||
};
|
||||
|
||||
NodePrompter.prototype.hideNodes = function() {
|
||||
this.nodes.forEach(node => node.$el.remove());
|
||||
};
|
||||
|
||||
var Container = function(svg, node) { // used for positioning
|
||||
this.$el = svg.append('g');
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.node = node;
|
||||
this.decorator = new node.Decorator({
|
||||
node: node,
|
||||
parentEl: this.$el
|
||||
});
|
||||
};
|
||||
|
||||
Container.prototype.moveBy = function(dx, dy) {
|
||||
dx = dx || 0;
|
||||
dy = dy || 0;
|
||||
this.x += dx;
|
||||
this.y += dy;
|
||||
};
|
||||
|
||||
Container.prototype.goTo = function(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
};
|
||||
|
||||
Container.prototype.computeSize = function(zoom) {
|
||||
this.$el.attr('opacity', 0);
|
||||
this.decorator.render(zoom);
|
||||
};
|
||||
|
||||
Container.prototype.render = function(zoom) {
|
||||
this.$el.attr('transform', `translate(${this.x}, ${this.y})`);
|
||||
this.$el
|
||||
.transition()
|
||||
.attr('opacity', 1);
|
||||
this.decorator.render(zoom);
|
||||
};
|
||||
|
||||
Container.prototype.width = function() {
|
||||
return this.decorator.width;
|
||||
};
|
||||
|
||||
Container.prototype.height = function() {
|
||||
return this.decorator.height;
|
||||
};
|
||||
|
||||
return NodePrompter;
|
||||
});
|
||||
@@ -7,9 +7,9 @@ define([
|
||||
var OperationControl = function() {
|
||||
};
|
||||
|
||||
OperationControl.prototype.hasMetaName = function(id, name) {
|
||||
OperationControl.prototype.hasMetaName = function(id, name, inclusive) {
|
||||
var node = this._client.getNode(id),
|
||||
bId = node.getBaseId(),
|
||||
bId = inclusive ? id : node.getBaseId(),
|
||||
baseName;
|
||||
|
||||
while (bId) {
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
define([
|
||||
'panels/EasyDAG/EasyDAGControl',
|
||||
'deepforge/viz/OperationControl',
|
||||
'deepforge/Constants',
|
||||
'widgets/EasyDAG/AddNodeDialog',
|
||||
'underscore'
|
||||
], function(
|
||||
EasyDAGControl,
|
||||
OperationControl,
|
||||
CONSTANTS,
|
||||
AddNodeDialog,
|
||||
_
|
||||
) {
|
||||
@@ -72,6 +74,8 @@ define([
|
||||
var desc = EasyDAGControl.prototype._getObjectDescriptor.call(this, id),
|
||||
node = this._client.getNode(id);
|
||||
|
||||
desc.inputs = [];
|
||||
desc.outputs = [];
|
||||
if (this.hasMetaName(id, 'Operation')) {
|
||||
// Only decorate operations in the currently active node
|
||||
if (this._currentNodeId !== desc.parentId) {
|
||||
@@ -95,6 +99,7 @@ define([
|
||||
|
||||
// Remove the 'code' attribute
|
||||
if (desc.attributes.code) {
|
||||
delete desc.attributes[CONSTANTS.LINE_OFFSET];
|
||||
delete desc.attributes.code;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'underscore'
|
||||
], function(
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
_
|
||||
) {
|
||||
|
||||
var RenameablePanel = function() {
|
||||
PanelBaseWithHeader.apply(this, arguments);
|
||||
};
|
||||
|
||||
_.extend(
|
||||
RenameablePanel.prototype,
|
||||
PanelBaseWithHeader.prototype,
|
||||
IActivePanel.prototype
|
||||
);
|
||||
|
||||
RenameablePanel.OPTIONS = PanelBaseWithHeader.OPTIONS;
|
||||
RenameablePanel.prototype.initializeRenameable = function () {
|
||||
this.$panelHeaderTitle.on('dblclick', this.editTitle.bind(this));
|
||||
};
|
||||
|
||||
RenameablePanel.prototype.currentNodeId = function () {
|
||||
return this.control._currentNodeId;
|
||||
};
|
||||
|
||||
RenameablePanel.prototype.currentBaseName = function () {
|
||||
var currentId = this.currentNodeId(),
|
||||
node = this._client.getNode(currentId),
|
||||
baseId = node.getBaseId(),
|
||||
base = this._client.getNode(baseId);
|
||||
|
||||
return base.getAttribute('name');
|
||||
};
|
||||
|
||||
RenameablePanel.prototype.editTitle = function () {
|
||||
this.$panelHeaderTitle.editInPlace({
|
||||
css: {
|
||||
'z-index': 1000
|
||||
},
|
||||
onChange: (oldValue, newValue) => {
|
||||
var nodeId = this.currentNodeId(),
|
||||
type = this.currentBaseName(),
|
||||
msg = `Renamed ${type}: ${oldValue} -> ${newValue}`;
|
||||
|
||||
if (!/^\s*$/.test(newValue)) {
|
||||
this._client.startTransaction(msg);
|
||||
this._client.setAttributes(nodeId, 'name', newValue);
|
||||
this._client.completeTransaction();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return RenameablePanel;
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/Decorators/DecoratorBase',
|
||||
'./EasyDAG/ArtifactOpDecorator.EasyDAGWidget'
|
||||
], function (
|
||||
DecoratorBase,
|
||||
ArtifactOpDecoratorEasyDAGWidget
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var ArtifactOpDecorator,
|
||||
__parent__ = DecoratorBase,
|
||||
__parent_proto__ = DecoratorBase.prototype,
|
||||
DECORATOR_ID = 'ArtifactOpDecorator';
|
||||
|
||||
ArtifactOpDecorator = function (params) {
|
||||
var opts = _.extend({loggerName: this.DECORATORID}, params);
|
||||
|
||||
__parent__.apply(this, [opts]);
|
||||
|
||||
this.logger.debug('ArtifactOpDecorator ctor');
|
||||
};
|
||||
|
||||
_.extend(ArtifactOpDecorator.prototype, __parent_proto__);
|
||||
ArtifactOpDecorator.prototype.DECORATORID = DECORATOR_ID;
|
||||
|
||||
/*********************** OVERRIDE DecoratorBase MEMBERS **************************/
|
||||
|
||||
ArtifactOpDecorator.prototype.initializeSupportedWidgetMap = function () {
|
||||
this.supportedWidgetMap = {
|
||||
EasyDAG: ArtifactOpDecoratorEasyDAGWidget
|
||||
};
|
||||
};
|
||||
|
||||
return ArtifactOpDecorator;
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
.artifactop-decorator {
|
||||
min-width: 65px;
|
||||
height: 40px;
|
||||
border: 1px solid black;
|
||||
background-color: #dedede;
|
||||
padding: 3px;
|
||||
text-align: center; }
|
||||
.artifactop-decorator .attr-title {
|
||||
font-style: italic;
|
||||
}
|
||||
.artifactop-decorator .name {
|
||||
margin-top: 10px;
|
||||
white-space: nowrap;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
text-align: center; }
|
||||
.artifactop-decorator .connector {
|
||||
background-color: #fefefe;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
border: 1px solid blue;
|
||||
z-index: 10;
|
||||
margin-left: -6px;
|
||||
left: 50%; }
|
||||
.artifactop-decorator .connector:hover {
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); }
|
||||
.artifactop-decorator .connector.top {
|
||||
top: -6px; }
|
||||
.artifactop-decorator .connector.bottom {
|
||||
bottom: -6px; }
|
||||
|
||||
.selected .artifactop-decorator {
|
||||
border: 1px solid #52a8ec;
|
||||
background-color: #dbeafc; }
|
||||
@@ -0,0 +1,112 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/Constants',
|
||||
'decorators/DcOpDecorator/EasyDAG/DcOpDecorator.EasyDAGWidget',
|
||||
'css!./ArtifactOpDecorator.EasyDAGWidget.css'
|
||||
], function (
|
||||
CONSTANTS,
|
||||
DecoratorBase
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var ArtifactOpDecorator,
|
||||
DECORATOR_ID = 'ArtifactOpDecorator',
|
||||
CAST_OPTS = {
|
||||
ArtifactLoader: {
|
||||
ptr: 'artifact',
|
||||
metaTgt: false
|
||||
},
|
||||
ArtifactFinder: {
|
||||
ptr: 'type',
|
||||
metaTgt: true
|
||||
}
|
||||
};
|
||||
|
||||
// ArtifactOp nodes need to be able to...
|
||||
// - dynamically change their outputs (downcast)
|
||||
ArtifactOpDecorator = function (options) {
|
||||
options.color = options.color || '#b0bec5';
|
||||
DecoratorBase.call(this, options);
|
||||
// set the opts...
|
||||
this.castOpts = CAST_OPTS[this._node.baseName];
|
||||
};
|
||||
|
||||
_.extend(ArtifactOpDecorator.prototype, DecoratorBase.prototype);
|
||||
|
||||
ArtifactOpDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
|
||||
|
||||
ArtifactOpDecorator.prototype.getTargetFilterFnFor = function() {
|
||||
return id => {
|
||||
var node = this.client.getNode(id),
|
||||
isMetaTgt = node.getId() === node.getMetaTypeId();
|
||||
return isMetaTgt === this.castOpts.metaTgt;
|
||||
};
|
||||
};
|
||||
|
||||
ArtifactOpDecorator.prototype.savePointer = function(name, to) {
|
||||
// When the 'artifact' pointer is changed, we should change the base
|
||||
// of the data output node to the target type
|
||||
if (typeof to !== 'string') {
|
||||
var outputId = this._node.outputs[0] && this._node.outputs[0].id;
|
||||
|
||||
// Clear the data handle of the output
|
||||
this.client.startTransaction(`Removing output of ${this.name}`);
|
||||
this.client.delPointer(this._node.id, name);
|
||||
if (outputId) {
|
||||
this.client.delAttributes(outputId, 'data');
|
||||
}
|
||||
this.client.completeTransaction();
|
||||
} else if (name === this.castOpts.ptr) { // set the casted value
|
||||
this.client.startTransaction(`Setting output of ${this.name} to ${to}`);
|
||||
this.castOutputType(to);
|
||||
this.client.makePointer(this._node.id, name, to);
|
||||
this.client.completeTransaction();
|
||||
} else {
|
||||
DecoratorBase.prototype.savePointer.call(this, name, to);
|
||||
}
|
||||
};
|
||||
|
||||
ArtifactOpDecorator.prototype.getDisplayName = function() {
|
||||
var ptrName = this._node.baseName === 'ArtifactLoader' ? 'artifact' : 'type',
|
||||
id = this._node.pointers[ptrName],
|
||||
name = this.nameFor[id] || this._node.name;
|
||||
return name;
|
||||
};
|
||||
|
||||
ArtifactOpDecorator.prototype.updateDisplayName = function() {
|
||||
var newName = this.getDisplayName();
|
||||
if (this.name !== newName) {
|
||||
this.name = newName;
|
||||
this.nameWidth = null;
|
||||
}
|
||||
};
|
||||
|
||||
ArtifactOpDecorator.prototype.updateTargetName = function(id, name) {
|
||||
DecoratorBase.prototype.updateTargetName.apply(this, arguments);
|
||||
// Update name
|
||||
var ptrName = this._node.baseName === 'ArtifactLoader' ? 'artifact' : 'type';
|
||||
if (this._node.pointers[ptrName] === id) {
|
||||
this._name = name;
|
||||
this.onResize();
|
||||
}
|
||||
};
|
||||
|
||||
ArtifactOpDecorator.prototype.expand = function() {
|
||||
this.updateDisplayName();
|
||||
DecoratorBase.prototype.expand.call(this);
|
||||
};
|
||||
|
||||
ArtifactOpDecorator.prototype.condense = function() {
|
||||
this.updateDisplayName();
|
||||
DecoratorBase.prototype.condense.call(this);
|
||||
};
|
||||
|
||||
return ArtifactOpDecorator;
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/Decorators/DecoratorBase',
|
||||
'./EasyDAG/DcOpDecorator.EasyDAGWidget'
|
||||
], function (
|
||||
DecoratorBase,
|
||||
DcOpDecoratorEasyDAGWidget
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var DcOpDecorator,
|
||||
__parent__ = DecoratorBase,
|
||||
__parent_proto__ = DecoratorBase.prototype,
|
||||
DECORATOR_ID = 'DcOpDecorator';
|
||||
|
||||
DcOpDecorator = function (params) {
|
||||
var opts = _.extend({loggerName: this.DECORATORID}, params);
|
||||
|
||||
__parent__.apply(this, [opts]);
|
||||
|
||||
this.logger.debug('DcOpDecorator ctor');
|
||||
};
|
||||
|
||||
_.extend(DcOpDecorator.prototype, __parent_proto__);
|
||||
DcOpDecorator.prototype.DECORATORID = DECORATOR_ID;
|
||||
|
||||
/*********************** OVERRIDE DecoratorBase MEMBERS **************************/
|
||||
|
||||
DcOpDecorator.prototype.initializeSupportedWidgetMap = function () {
|
||||
this.supportedWidgetMap = {
|
||||
EasyDAG: DcOpDecoratorEasyDAGWidget
|
||||
};
|
||||
};
|
||||
|
||||
return DcOpDecorator;
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
.dcop-decorator {
|
||||
min-width: 65px;
|
||||
height: 40px;
|
||||
border: 1px solid black;
|
||||
background-color: #dedede;
|
||||
padding: 3px;
|
||||
text-align: center; }
|
||||
.dcop-decorator .attr-title {
|
||||
font-style: italic;
|
||||
}
|
||||
.dcop-decorator .name {
|
||||
margin-top: 10px;
|
||||
white-space: nowrap;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
text-align: center; }
|
||||
.dcop-decorator .connector {
|
||||
background-color: #fefefe;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
border: 1px solid blue;
|
||||
z-index: 10;
|
||||
margin-left: -6px;
|
||||
left: 50%; }
|
||||
.dcop-decorator .connector:hover {
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); }
|
||||
.dcop-decorator .connector.top {
|
||||
top: -6px; }
|
||||
.dcop-decorator .connector.bottom {
|
||||
bottom: -6px; }
|
||||
|
||||
.selected .dcop-decorator {
|
||||
border: 1px solid #52a8ec;
|
||||
background-color: #dbeafc; }
|
||||
@@ -0,0 +1,79 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/Constants',
|
||||
'decorators/OperationDecorator/EasyDAG/OperationDecorator.EasyDAGWidget',
|
||||
'css!./DcOpDecorator.EasyDAGWidget.css'
|
||||
], function (
|
||||
CONSTANTS,
|
||||
DecoratorBase
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var DcOpDecorator,
|
||||
DECORATOR_ID = 'DcOpDecorator';
|
||||
|
||||
// DcOp nodes need to be able to...
|
||||
// - dynamically change their outputs (downcast)
|
||||
DcOpDecorator = function (options) {
|
||||
options.color = options.color || '#78909c';
|
||||
DecoratorBase.call(this, options);
|
||||
};
|
||||
|
||||
_.extend(DcOpDecorator.prototype, DecoratorBase.prototype);
|
||||
|
||||
DcOpDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
|
||||
|
||||
DcOpDecorator.prototype.getTargetFilterFnFor = function() {
|
||||
return id => {
|
||||
var node = this.client.getNode(id);
|
||||
return node.getId() !== node.getMetaTypeId(); // not meta node
|
||||
};
|
||||
};
|
||||
|
||||
DcOpDecorator.prototype.castOutputType = function(targetId) {
|
||||
var target = this.client.getNode(targetId),
|
||||
baseId = target.getBaseId(),
|
||||
outputId = this._node.outputs[0] && this._node.outputs[0].id,
|
||||
hash;
|
||||
|
||||
if (!outputId) {
|
||||
// create the outputId node
|
||||
outputId = this._createOutputNode(baseId);
|
||||
} else {
|
||||
this.client.makePointer(outputId, CONSTANTS.POINTER_BASE, baseId);
|
||||
}
|
||||
// Copy the data content to the output node
|
||||
hash = target.getAttribute('data');
|
||||
this.client.setAttributes(outputId, 'data', hash);
|
||||
};
|
||||
|
||||
DcOpDecorator.prototype._createOutputNode = function(baseId) {
|
||||
var n = this.client.getNode(this._node.id),
|
||||
outputCntrId;
|
||||
|
||||
outputCntrId = n.getChildrenIds().find(id => {
|
||||
var metaTypeId = this.client.getNode(id).getMetaTypeId(),
|
||||
metaType = this.client.getNode(metaTypeId);
|
||||
|
||||
if (!metaType) {
|
||||
this.logger.error(`Could not check the type of ${id}!`);
|
||||
return false;
|
||||
}
|
||||
return metaType.getAttribute('name') === 'Outputs';
|
||||
});
|
||||
|
||||
return this.client.createChild({
|
||||
baseId: baseId,
|
||||
parentId: outputCntrId
|
||||
});
|
||||
};
|
||||
|
||||
return DcOpDecorator;
|
||||
});
|
||||
@@ -18,6 +18,7 @@ define([
|
||||
DECORATOR_ID = 'JobDecorator',
|
||||
COLORS = {
|
||||
pending: '#9e9e9e',
|
||||
queued: '#cfd8dc',
|
||||
running: '#fff59d',
|
||||
success: '#66bb6a',
|
||||
fail: '#e57373'
|
||||
@@ -29,6 +30,13 @@ define([
|
||||
// - unhighlight ports
|
||||
// - report the location of specific ports
|
||||
JobDecorator = function (options) {
|
||||
options.skipAttributes = {
|
||||
name: true,
|
||||
status: true,
|
||||
execFiles: true,
|
||||
stdout: true,
|
||||
debug: true
|
||||
};
|
||||
EllipseDecorator.call(this, options);
|
||||
};
|
||||
|
||||
@@ -42,7 +50,8 @@ define([
|
||||
|
||||
JobDecorator.prototype.setAttributes = function() {
|
||||
EllipseDecorator.prototype.setAttributes.call(this);
|
||||
var status = this._attributes.status && this._attributes.status.value;
|
||||
var attrs = this._node.attributes,
|
||||
status = attrs.status && attrs.status.value;
|
||||
|
||||
// Update the color based on the 'status' attr
|
||||
this.color = COLORS[status] || COLORS.fail;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/* globals define, _*/
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/AttributeField'
|
||||
], function(
|
||||
AttributeFieldBase
|
||||
) {
|
||||
// Attribute field in which the label is clickable and the attribute meta is editable
|
||||
var AttributeField = function() {
|
||||
AttributeFieldBase.apply(this, arguments);
|
||||
this.$label.on('click', () => this.onLabelClick());
|
||||
};
|
||||
|
||||
_.extend(AttributeField.prototype, AttributeFieldBase.prototype);
|
||||
|
||||
return AttributeField;
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
/* globals define */
|
||||
define([
|
||||
], function(
|
||||
) {
|
||||
|
||||
var CreateAttrField = function(logger, pEl, y) {
|
||||
this.$el = pEl.append('text')
|
||||
.attr('y', y)
|
||||
.attr('class', 'create-attr-field')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('font-weight', 'bold')
|
||||
.attr('font-style', 'italic')
|
||||
.text('New Attribute')
|
||||
.on('click', () => this.onClick());
|
||||
};
|
||||
|
||||
CreateAttrField.prototype.render =
|
||||
CreateAttrField.prototype.destroy = function() {};
|
||||
|
||||
CreateAttrField.prototype.width = function() {
|
||||
return this.$el[0][0].getBoundingClientRect().width;
|
||||
};
|
||||
|
||||
return CreateAttrField;
|
||||
});
|
||||
@@ -1,15 +1,17 @@
|
||||
/*globals define, $,_*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
|
||||
'./AttributeField',
|
||||
'./CreateAttributeField',
|
||||
'decorators/MetaDecorator/DiagramDesigner/AttributeDetailsDialog',
|
||||
'css!./OpIntDecorator.EasyDAGWidget.css'
|
||||
], function (
|
||||
DecoratorBase
|
||||
DecoratorBase,
|
||||
AttributeField,
|
||||
CreateAttributeField,
|
||||
AttributeDetailsDialog
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
@@ -17,13 +19,7 @@ define([
|
||||
var OpIntDecorator,
|
||||
DECORATOR_ID = 'OpIntDecorator';
|
||||
|
||||
// OpInt nodes need to be able to...
|
||||
// - show their ports
|
||||
// - highlight ports
|
||||
// - unhighlight ports
|
||||
// - report the location of specific ports
|
||||
OpIntDecorator = function (options) {
|
||||
this.color = this.color || '#78909c';
|
||||
DecoratorBase.call(this, options);
|
||||
};
|
||||
|
||||
@@ -31,9 +27,9 @@ define([
|
||||
|
||||
OpIntDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
|
||||
OpIntDecorator.prototype.initialize = function() {
|
||||
if (this._node.baseName === 'Operation') {
|
||||
if (this.isOperation()) {
|
||||
this.color = '#2196f3';
|
||||
} else {
|
||||
} else if (this._node.baseName) {
|
||||
// On hover, show the type
|
||||
this.enableTooltip(this._node.baseName, 'dark');
|
||||
}
|
||||
@@ -41,6 +37,109 @@ define([
|
||||
this.$name.on('dblclick', this.editName.bind(this));
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.AttributeField = AttributeField;
|
||||
OpIntDecorator.prototype.isOperation = function() {
|
||||
return this._node.baseName === 'Operation';
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.createAttributeFields = function(y, width) {
|
||||
var field,
|
||||
initialY = y;
|
||||
|
||||
if (!this.isOperation()) {
|
||||
return y;
|
||||
}
|
||||
|
||||
y = DecoratorBase.prototype.createAttributeFields.call(this, y, width);
|
||||
// Change attribute field so clicking allows user to edit/delete the field
|
||||
this.fields.forEach(field =>
|
||||
field.onLabelClick = this.editAttributeMeta.bind(this, field.name));
|
||||
|
||||
// Add the 'create new attribute' field
|
||||
y += this.ROW_HEIGHT + (y === initialY ? 0 : 10);
|
||||
field = new CreateAttributeField(this.logger, this.$attributes, y, width);
|
||||
field.onClick = this.newAttribute.bind(this);
|
||||
this.fields.push(field);
|
||||
return y;
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.newAttribute = function() {
|
||||
var defSchema = {
|
||||
type: 'string'
|
||||
};
|
||||
|
||||
this.editAttributeMeta(null, defSchema);
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.expand = function() {
|
||||
DecoratorBase.prototype.expand.call(this, this.isOperation());
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.editAttributeMeta = function(name, defSchema) {
|
||||
var dialog = new AttributeDetailsDialog(),
|
||||
node = this.client.getNode(this._node.id),
|
||||
attrNames = node.getValidAttributeNames(),
|
||||
attrInfo = this._node.attributes[name] || defSchema,
|
||||
schema,
|
||||
i;
|
||||
|
||||
// Open the dialog for editing the attribute
|
||||
schema = _.extend({defaultValue: attrInfo.value}, attrInfo);
|
||||
|
||||
// Remove the current name
|
||||
i = attrNames.indexOf(name);
|
||||
if (i !== -1) {
|
||||
attrNames.splice(i, 1);
|
||||
}
|
||||
|
||||
dialog.show(schema, attrNames,
|
||||
desc => this.setAttributeMeta(name, desc),
|
||||
() => this.deleteAttribute(name));
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.deleteAttribute = function(name) {
|
||||
var opName = this._node.attributes.name.value,
|
||||
msg = `Deleting "${name}" attribute from "${opName}" operation`;
|
||||
|
||||
this.client.startTransaction(msg);
|
||||
this.client.removeAttributeSchema(this._node.id, name);
|
||||
this.client.delAttributes(this._node.id, name);
|
||||
this.client.completeTransaction();
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.setAttributeMeta = function(name, desc) {
|
||||
var schema,
|
||||
opName = this._node.attributes.name.value,
|
||||
msg = `Updating "${name}" attribute in "${opName}" operation`;
|
||||
|
||||
// Create the schema from the desc
|
||||
schema = {
|
||||
type: desc.type,
|
||||
min: desc.min,
|
||||
max: desc.max,
|
||||
regexp: desc.regexp
|
||||
};
|
||||
|
||||
if (desc.isEnum) {
|
||||
schema.enum = desc.enumValues;
|
||||
}
|
||||
|
||||
// Update the operation's attribute
|
||||
this.client.startTransaction(msg);
|
||||
|
||||
if (name !== desc.name) { // Renaming attribute
|
||||
if (name) {
|
||||
this.client.removeAttributeSchema(this._node.id, name);
|
||||
this.client.delAttributes(this._node.id, name);
|
||||
}
|
||||
name = desc.name;
|
||||
}
|
||||
|
||||
this.client.setAttributeSchema(this._node.id, name, schema);
|
||||
this.client.setAttributes(this._node.id, name, desc.defaultValue);
|
||||
this.client.completeTransaction();
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.editName = function() {
|
||||
var html = this.$name[0][0],
|
||||
position = html.getBoundingClientRect(),
|
||||
@@ -76,6 +175,13 @@ define([
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.onNameChanged = function(oldVal, newValue) {
|
||||
var whitespace = /^\s*$/;
|
||||
if (newValue !== oldVal && !whitespace.test(newValue)) {
|
||||
this.onValidNameChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.onValidNameChange = function(newValue) {
|
||||
this.saveAttribute('name', newValue);
|
||||
};
|
||||
|
||||
@@ -83,8 +189,5 @@ define([
|
||||
return this._node.name;
|
||||
};
|
||||
|
||||
// clicking on the name should allow the user to edit it in place
|
||||
// TODO
|
||||
|
||||
return OpIntDecorator;
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ define([
|
||||
|
||||
OpIntPtrDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
|
||||
|
||||
OpIntPtrDecorator.prototype.onNameChanged = function(old, newValue) {
|
||||
OpIntPtrDecorator.prototype.onValidNameChange = function(newValue) {
|
||||
return this.changePtrName(this.name, newValue);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
/*globals define, _*/
|
||||
/*globals define, _, Opentip*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
|
||||
'css!./OperationDecorator.EasyDAGWidget.css'
|
||||
@@ -16,7 +12,12 @@ define([
|
||||
|
||||
var OperationDecorator,
|
||||
NAME_MARGIN = 25,
|
||||
DECORATOR_ID = 'OperationDecorator';
|
||||
DECORATOR_ID = 'OperationDecorator',
|
||||
PORT_TOOLTIP_OPTS = {
|
||||
tipJoint: 'left',
|
||||
removeElementsOnHide: true,
|
||||
style: 'dark'
|
||||
};
|
||||
|
||||
// Operation nodes need to be able to...
|
||||
// - show their ports
|
||||
@@ -30,6 +31,7 @@ define([
|
||||
this.id = this._node.id;
|
||||
this.$ports = this.$el.append('g')
|
||||
.attr('id', 'ports');
|
||||
this.$portTooltips = {};
|
||||
};
|
||||
|
||||
_.extend(OperationDecorator.prototype, DecoratorBase.prototype);
|
||||
@@ -41,29 +43,22 @@ define([
|
||||
};
|
||||
|
||||
OperationDecorator.prototype.condense = function() {
|
||||
var path,
|
||||
width,
|
||||
rx;
|
||||
|
||||
width = Math.max(this.nameWidth + 2 * NAME_MARGIN, this.dense.width);
|
||||
rx = width/2;
|
||||
|
||||
path = [
|
||||
`M${-rx},0`,
|
||||
`l ${width} 0`,
|
||||
`l 0 ${this.dense.height}`,
|
||||
`l -${width} 0`,
|
||||
`l 0 -${this.dense.height}`
|
||||
].join(' ');
|
||||
|
||||
var width = Math.max(this.nameWidth + 2 * NAME_MARGIN, this.dense.width);
|
||||
|
||||
this.$body
|
||||
.attr('d', path);
|
||||
.transition()
|
||||
.attr('x', -width/2)
|
||||
.attr('y', 0)
|
||||
.attr('width', width)
|
||||
.attr('height', this.dense.height);
|
||||
|
||||
// Clear the attributes
|
||||
this.$attributes.remove();
|
||||
this.clearFields();
|
||||
this.$attributes = this.$el.append('g')
|
||||
.attr('fill', '#222222');
|
||||
.attr('fill', 'none');
|
||||
|
||||
this.createAttributeFields(0, width);
|
||||
this.createPointerFields(0, width, true);
|
||||
|
||||
this.height = this.dense.height;
|
||||
this.width = width;
|
||||
@@ -91,7 +86,8 @@ define([
|
||||
|
||||
OperationDecorator.prototype.renderPort = function(port, x, y, isInput) {
|
||||
var color = this.PORT_COLOR.OPEN,
|
||||
portIcon = this.$ports.append('g');
|
||||
portIcon = this.$ports.append('g'),
|
||||
tooltip;
|
||||
|
||||
// If the port is incoming and occupied, render it differently
|
||||
if (isInput && port.connection) {
|
||||
@@ -115,14 +111,26 @@ define([
|
||||
portIcon.on('click', this.onPortClick.bind(this, this.id, port.id, !isInput));
|
||||
|
||||
// Add tooltip with whole name
|
||||
// TODO
|
||||
if (this.$portTooltips[port.id]) {
|
||||
this.$portTooltips[port.id].hide();
|
||||
}
|
||||
tooltip = new Opentip(portIcon[0][0], PORT_TOOLTIP_OPTS);
|
||||
tooltip.setContent(port.name);
|
||||
portIcon.on('mouseenter', () => tooltip.show());
|
||||
portIcon.on('mouseout', () => tooltip.hide());
|
||||
this.$portTooltips[port.id] = tooltip;
|
||||
};
|
||||
|
||||
OperationDecorator.prototype.hidePorts = function() {
|
||||
var visiblePortIds = Object.keys(this.$portTooltips);
|
||||
this.logger.info(`hiding ports for ${this.name} (${this.id})`);
|
||||
this.$ports.remove();
|
||||
this.$ports = this.$el.append('g')
|
||||
.attr('id', 'ports');
|
||||
|
||||
for (var i = visiblePortIds.length; i--;) {
|
||||
this.$portTooltips[visiblePortIds[i]].hide();
|
||||
}
|
||||
};
|
||||
|
||||
OperationDecorator.prototype.getPortLocation = function(id, isInput) {
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
/**
|
||||
* Generated by PluginGenerator 1.7.0 from webgme on Mon May 23 2016 14:23:16 GMT-0500 (CDT).
|
||||
* A plugin that inherits from the PluginBase. To see source code documentation about available
|
||||
* properties and methods visit %host%/docs/source/PluginBase.html.
|
||||
*/
|
||||
|
||||
define([
|
||||
'q',
|
||||
'deepforge/plugin/LocalExecutor',
|
||||
'text!./metadata.json',
|
||||
'underscore',
|
||||
'plugin/PluginBase'
|
||||
], function (
|
||||
Q,
|
||||
LocalExecutor,
|
||||
pluginMetadata,
|
||||
_,
|
||||
PluginBase
|
||||
) {
|
||||
'use strict';
|
||||
@@ -97,19 +95,44 @@ define([
|
||||
|
||||
return this.getExecutionDir()
|
||||
.then(execDir => {
|
||||
|
||||
tgtNode = this.core.createNode({
|
||||
base: this.META.Execution,
|
||||
parent: execDir
|
||||
});
|
||||
this.core.setAttribute(tgtNode, 'name', `${name} Execution`);
|
||||
return this.core.loadChildren(node);
|
||||
|
||||
// Get a unique name
|
||||
return this.getUniqueExecName(name + '_execution');
|
||||
})
|
||||
.then(execName => {
|
||||
var isSnapshot = this.getCurrentConfig().snapshot;
|
||||
|
||||
this.logger.debug(`Creating execution ${execName}`);
|
||||
|
||||
// Set all the metadata for the new execution
|
||||
this.core.setAttribute(tgtNode, 'name', execName);
|
||||
this.core.setAttribute(tgtNode, 'snapshot', isSnapshot);
|
||||
this.core.setAttribute(tgtNode, 'tagname', execName);
|
||||
this.core.setAttribute(tgtNode, 'createdAt', Date.now());
|
||||
this.core.setPointer(tgtNode, 'origin', this.activeNode);
|
||||
this.core.addMember(this.activeNode, 'executions', tgtNode);
|
||||
|
||||
return this.project.createTag(
|
||||
execName.replace(/[ -]/g, '_'),
|
||||
this.currentHash
|
||||
);
|
||||
})
|
||||
.then(() => this.core.loadChildren(node))
|
||||
.then(children => {
|
||||
if (!children.length) {
|
||||
this.logger.warn(`No children in pipeline. Will proceed anyway`);
|
||||
this.logger.warn('No children in pipeline. Will proceed anyway');
|
||||
}
|
||||
|
||||
copies = children.length ? this.core.copyNodes(children, tgtNode) : [];
|
||||
return this.copyOperations(children, tgtNode);
|
||||
})
|
||||
.then(copiedPairs => {
|
||||
var originals = copiedPairs.map(pair => pair[0]);
|
||||
copies = copiedPairs.map(pair => pair[1]);
|
||||
opTuples = copies
|
||||
.map((copy, i) => [copy, i]) // zip w/ index
|
||||
.filter(pair => this.core.isTypeOf(pair[0], this.META.Operation));
|
||||
@@ -117,20 +140,147 @@ define([
|
||||
// Create a mapping of old names to new names
|
||||
return Q.all(opTuples.map(pair =>
|
||||
// Add the input/output mappings to the dataMapping
|
||||
this.addDataToMap(children[pair[1]], pair[0], dataMapping)
|
||||
this.addDataToMap(originals[pair[1]], pair[0], dataMapping)
|
||||
)
|
||||
);
|
||||
})
|
||||
.then(() => { // datamapping is set!
|
||||
this.updateReferences(copies, dataMapping);
|
||||
this.boxOperations(opTuples.map(o => o[0]), tgtNode);
|
||||
return this.save(`Created execution of ${name}`);
|
||||
return this.save(`Created execution from ${name}`);
|
||||
})
|
||||
.then(() => tgtNode); // return tgtNode
|
||||
};
|
||||
|
||||
CreateExecution.prototype.getExecutionsDir = function () {
|
||||
return this.rootNode;
|
||||
CreateExecution.prototype.getUniqueExecName = function (basename) {
|
||||
var name = basename,
|
||||
taken = {},
|
||||
i = 2;
|
||||
|
||||
// Get a unique name wrt the tags and the other executions
|
||||
return this.project.getTags()
|
||||
.then(tags => {
|
||||
Object.keys(tags).forEach(name => taken[name] = true);
|
||||
|
||||
// Get the other executions
|
||||
return this.getExecutionDir();
|
||||
})
|
||||
.then(execDir => {
|
||||
var cIds = this.core.getChildrenPaths(execDir);
|
||||
return Q.all(cIds.map(id => this.core.loadByPath(this.rootNode, id)));
|
||||
})
|
||||
.then(execs => {
|
||||
var names = execs.map(exec => this.core.getAttribute(exec, 'name'));
|
||||
names.forEach(name => taken[name] = true);
|
||||
|
||||
while (taken[name]) {
|
||||
name = basename + '_' + (i++);
|
||||
}
|
||||
return name;
|
||||
});
|
||||
};
|
||||
|
||||
CreateExecution.prototype.copyOperations = function (nodes, dst) {
|
||||
var snapshot = this.getCurrentConfig().snapshot;
|
||||
|
||||
if (snapshot) {
|
||||
return Q.all(nodes.map(node => {
|
||||
if (this.isLocalOperation(node) ||
|
||||
this.isMetaTypeOf(node, this.META.Transporter)) {
|
||||
|
||||
return [[node, this.core.copyNode(node, dst)]];
|
||||
} else if (this.isMetaTypeOf(node, this.META.Operation)) {
|
||||
return this.snapshotNode(node, dst);
|
||||
}
|
||||
}))
|
||||
.then(pairs => pairs.filter(pair => !!pair)
|
||||
.reduce((l1, l2) => l1.concat(l2))
|
||||
);
|
||||
|
||||
} else if (nodes.length) {
|
||||
var copies = this.core.copyNodes(nodes, dst);
|
||||
return nodes.map((node, i) => [node, copies[i]]);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
CreateExecution.prototype.snapshotNode = function (op, dst) {
|
||||
// If we are making a snapshot, we should copy the base operation
|
||||
// and set the attributes, add the child nodes, etc
|
||||
var base = this.core.getBase(this.core.getBase(op)),
|
||||
names,
|
||||
values,
|
||||
snapshot = this.core.createNode({
|
||||
base: base,
|
||||
parent: dst
|
||||
});
|
||||
|
||||
// Copy over the attributes
|
||||
names = this.core.getValidAttributeNames(op);
|
||||
values = names.map(name => this.core.getAttribute(op, name));
|
||||
names.forEach((name, i) =>
|
||||
this.core.setAttribute(snapshot, name, values[i]));
|
||||
|
||||
// Copy the pointers
|
||||
names = this.core.getValidPointerNames(op);
|
||||
return Q.all(names
|
||||
.map(name => this.core.getPointerPath(op, name))
|
||||
.map(id => this.core.loadByPath(this.rootNode, id)))
|
||||
.then(values => {
|
||||
|
||||
names.forEach((name, i) =>
|
||||
this.core.setPointer(snapshot, name, values[i]));
|
||||
|
||||
// Copy the data I/O
|
||||
var srcCntrs = this.core.getChildrenPaths(op),
|
||||
dstCntrs = this.core.getChildrenPaths(snapshot);
|
||||
|
||||
return Q.all([srcCntrs, dstCntrs].map(ids =>
|
||||
Q.all(ids.map(id => this.core.loadByPath(this.rootNode, id)))));
|
||||
})
|
||||
.then(cntrs => {
|
||||
var srcCntrs,
|
||||
dstCntrs;
|
||||
|
||||
// Sort all containers by metatype id
|
||||
cntrs.map(l => l.sort((a, b) => {
|
||||
var aId = this.core.getPath(this.core.getMetaType(a)),
|
||||
bId = this.core.getPath(this.core.getMetaType(b));
|
||||
|
||||
return aId < bId ? -1 : 1;
|
||||
}));
|
||||
|
||||
srcCntrs = cntrs[0];
|
||||
dstCntrs = cntrs[1];
|
||||
return Q.all(srcCntrs.map(ctr => Q.all(this.core.getChildrenPaths(ctr)
|
||||
.map(id => this.core.loadByPath(this.rootNode, id)))))
|
||||
.then(cntrs =>
|
||||
cntrs.map((nodes, i) =>
|
||||
nodes.map(n => [n, this.copyDataNode(n, dstCntrs[i])]))
|
||||
);
|
||||
})
|
||||
.then(nodes => {
|
||||
nodes = nodes.reduce((l1, l2) => l1.concat(l2), []);
|
||||
nodes.push([op, snapshot]);
|
||||
return nodes;
|
||||
});
|
||||
};
|
||||
|
||||
CreateExecution.prototype.copyDataNode = function (original, dst) {
|
||||
// Create new node of the given type
|
||||
var attrNames = this.core.getAttributeNames(original),
|
||||
values,
|
||||
copy = this.core.createNode({
|
||||
base: this.core.getMetaType(original),
|
||||
parent: dst
|
||||
});
|
||||
|
||||
// Set the 'name', 'data' attributes
|
||||
values = attrNames.map(name => this.core.getAttribute(original, name));
|
||||
attrNames.forEach((name, i) =>
|
||||
this.core.setAttribute(copy, name, values[i]));
|
||||
|
||||
return copy;
|
||||
};
|
||||
|
||||
CreateExecution.prototype.addDataToMap = function (srcOp, dstOp, map) {
|
||||
@@ -209,5 +359,10 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
_.extend(
|
||||
CreateExecution.prototype,
|
||||
LocalExecutor.prototype
|
||||
);
|
||||
|
||||
return CreateExecution;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "CreateExecution",
|
||||
"name": "CreateExecution",
|
||||
"name": "Create Execution",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
@@ -10,5 +10,14 @@
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": []
|
||||
}
|
||||
"configStructure": [
|
||||
{
|
||||
"name": "snapshot",
|
||||
"displayName": "Snapshot",
|
||||
"description": "Freeze the operation definitions and attributes at current value",
|
||||
"value": true,
|
||||
"valueType": "boolean",
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,12 +9,22 @@ define([
|
||||
'plugin/PluginConfig',
|
||||
'plugin/PluginBase',
|
||||
'deepforge/js-yaml.min',
|
||||
'text!deepforge/layers.yml',
|
||||
'common/util/guid',
|
||||
'js/RegistryKeys',
|
||||
'js/Constants',
|
||||
'js/Panels/MetaEditor/MetaEditorConstants',
|
||||
'underscore',
|
||||
'text!deepforge/layers.json',
|
||||
'text!./metadata.json'
|
||||
], function (
|
||||
PluginConfig,
|
||||
PluginBase,
|
||||
yaml,
|
||||
generateGuid,
|
||||
REGISTRY_KEYS,
|
||||
CONSTANTS,
|
||||
META_CONSTANTS,
|
||||
_,
|
||||
DEFAULT_LAYERS,
|
||||
metadata
|
||||
) {
|
||||
@@ -31,6 +41,8 @@ define([
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = CreateTorchMeta.metadata;
|
||||
this.metaSheets = {};
|
||||
this.sheetCounts = {};
|
||||
};
|
||||
|
||||
CreateTorchMeta.metadata = JSON.parse(metadata);
|
||||
@@ -58,7 +70,7 @@ define([
|
||||
}
|
||||
|
||||
// Extra layer names
|
||||
this.getYamlText((err, text) => {
|
||||
this.getJsonLayers((err, text) => {
|
||||
if (err) {
|
||||
return callback(err, this.result);
|
||||
}
|
||||
@@ -67,15 +79,24 @@ define([
|
||||
// - (Abstract) CategoryLayerTypes
|
||||
// - LayerName
|
||||
// - Attributes (if exists)
|
||||
var content,
|
||||
var content = {},
|
||||
categories,
|
||||
nodes = {};
|
||||
config = this.getCurrentConfig(),
|
||||
nodes = {},
|
||||
layers;
|
||||
|
||||
try {
|
||||
content = yaml.load(text);
|
||||
layers = JSON.parse(text);
|
||||
} catch (e) {
|
||||
return callback('YAML parse error: ' + e, this.result);
|
||||
return callback('JSON parse error: ' + e, this.result);
|
||||
}
|
||||
layers.forEach(layer => {
|
||||
if (!content[layer.type]) {
|
||||
content[layer.type] = [];
|
||||
}
|
||||
content[layer.type].push(layer);
|
||||
});
|
||||
|
||||
categories = Object.keys(content);
|
||||
// Create the base class, if needed
|
||||
if (!this.META.Layer) {
|
||||
@@ -84,23 +105,52 @@ define([
|
||||
|
||||
// Create the category nodes
|
||||
categories
|
||||
.forEach(name => nodes[name] = this.createMetaNode(name, this.META.Layer));
|
||||
.forEach(name => {
|
||||
// Create a tab for each
|
||||
this.metaSheets[name] = this.createMetaSheetTab(name);
|
||||
this.sheetCounts[name] = 0;
|
||||
nodes[name] = this.createMetaNode(name, this.META.Layer, name);
|
||||
});
|
||||
|
||||
// Make them abstract
|
||||
categories
|
||||
.forEach(name => this.core.setRegistry(nodes[name], 'isAbstract', true));
|
||||
|
||||
|
||||
if (config.removeOldLayers) {
|
||||
var isNewLayer = {},
|
||||
newLayers = layers.map(layer => layer.name),
|
||||
oldLayers,
|
||||
oldNames;
|
||||
|
||||
newLayers = newLayers.concat(categories); // add the category nodes
|
||||
newLayers.forEach(name => isNewLayer[name] = true);
|
||||
|
||||
// Set the newLayer nodes 'base' to 'Layer' so we don't accidentally
|
||||
// delete them
|
||||
newLayers
|
||||
.map(name => this.META[name])
|
||||
.filter(layer => !!layer)
|
||||
.forEach(layer => this.core.setPointer(layer, 'base', this.META.Layer));
|
||||
|
||||
oldLayers = Object.keys(this.META)
|
||||
.filter(name => name !== 'Layer')
|
||||
.map(name => this.META[name])
|
||||
.filter(node => this.isMetaTypeOf(node, this.META.Layer))
|
||||
.filter(node => !isNewLayer[this.core.getAttribute(node, 'name')]);
|
||||
|
||||
oldNames = oldLayers.map(l => this.core.getAttribute(l, 'name'));
|
||||
// Get the old layer names
|
||||
this.logger.debug(`Removing layers: ${oldNames.join(', ')}`);
|
||||
oldLayers.forEach(layer => this.core.deleteNode(layer));
|
||||
}
|
||||
|
||||
// Create the actual nodes
|
||||
categories.forEach(cat => {
|
||||
content[cat]
|
||||
.forEach(name => {
|
||||
var attrs = null;
|
||||
if (typeof name !== 'string') {
|
||||
attrs = name[Object.keys(name)[0]];
|
||||
name = Object.keys(name)[0];
|
||||
}
|
||||
nodes[name] = this.createMetaNode(name, nodes[cat], attrs);
|
||||
.forEach(layer => {
|
||||
var attrs = layer.params,
|
||||
name = layer.name;
|
||||
nodes[name] = this.createMetaNode(name, nodes[cat], cat, attrs);
|
||||
// Make the node non-abstract
|
||||
this.core.setRegistry(nodes[name], 'isAbstract', false);
|
||||
});
|
||||
@@ -115,11 +165,48 @@ define([
|
||||
callback(null, self.result);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.getYamlText = function (callback) {
|
||||
CreateTorchMeta.prototype.removeFromMeta = function (nodeId) {
|
||||
var sheets = this.core.getRegistry(this.rootNode, REGISTRY_KEYS.META_SHEETS),
|
||||
sheet;
|
||||
|
||||
// Remove from meta
|
||||
this.core.delMember(this.rootNode, META_CONSTANTS.META_ASPECT_SET_NAME, nodeId);
|
||||
|
||||
// Remove from the given meta sheet
|
||||
sheet = sheets.find(sheet => {
|
||||
var paths = this.core.getMemberPaths(this.rootNode, sheet.SetID);
|
||||
return paths.indexOf(nodeId) > -1;
|
||||
});
|
||||
|
||||
if (sheet) {
|
||||
this.core.delMember(this.rootNode, sheet.SetID, nodeId);
|
||||
}
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.createMetaSheetTab = function (name) {
|
||||
var sheets = this.core.getRegistry(this.rootNode, REGISTRY_KEYS.META_SHEETS),
|
||||
id = META_CONSTANTS.META_ASPECT_SHEET_NAME_PREFIX + generateGuid(),
|
||||
sheet,
|
||||
desc = {
|
||||
SetID: id,
|
||||
order: sheets.length,
|
||||
title: name
|
||||
};
|
||||
|
||||
sheet = sheets.find(sheet => sheet.title === name);
|
||||
if (!sheet) {
|
||||
sheet = desc;
|
||||
this.logger.debug(`creating meta sheet "${name}"`);
|
||||
this.core.createSet(this.rootNode, sheet.SetID);
|
||||
sheets.push(sheet);
|
||||
this.core.setRegistry(this.rootNode, REGISTRY_KEYS.META_SHEETS, sheets);
|
||||
}
|
||||
return sheet.SetID;
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.getJsonLayers = function (callback) {
|
||||
var config = this.getCurrentConfig();
|
||||
|
||||
if (config.layerNameHash) {
|
||||
@@ -135,41 +222,72 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.createMetaNode = function (name, base, attrs) {
|
||||
var node;
|
||||
CreateTorchMeta.prototype.createMetaNode = function (name, base, tabName, attrs) {
|
||||
var node = this.META[name],
|
||||
nodeId = node && this.core.getPath(node),
|
||||
tabId = this.metaSheets[tabName],
|
||||
position = this.getPositionFor(name, tabName);
|
||||
|
||||
if (this.META[name]) {
|
||||
this.logger.warn('"' + name + '" already exists. skipping...');
|
||||
return this.META[name];
|
||||
if (!tabId) {
|
||||
this.logger.error(`No meta sheet for ${tabName}`);
|
||||
}
|
||||
|
||||
// Create a node
|
||||
node = this.core.createNode({
|
||||
parent: this.META.Language,
|
||||
base: base
|
||||
});
|
||||
this.core.setAttribute(node, 'name', name);
|
||||
if (!node) {
|
||||
// Create a node
|
||||
node = this.core.createNode({
|
||||
parent: this.META.Language,
|
||||
base: base
|
||||
});
|
||||
this.core.setAttribute(node, 'name', name);
|
||||
|
||||
nodeId = this.core.getPath(node);
|
||||
} else {
|
||||
// Remove from meta
|
||||
this.removeFromMeta(nodeId);
|
||||
this.core.setPointer(node, 'base', base);
|
||||
}
|
||||
|
||||
// Add it to the meta sheet
|
||||
this.core.addMember(this.rootNode, 'MetaAspectSet', node);
|
||||
this.core.addMember(this.rootNode, META_CONSTANTS.META_ASPECT_SET_NAME, node);
|
||||
this.core.addMember(this.rootNode, tabId, node);
|
||||
|
||||
// Add it to a tab of the meta sheet
|
||||
var set = this.core.getSetNames(this.rootNode)
|
||||
.find(name => name !== 'MetaAspectSet');
|
||||
|
||||
this.core.addMember(this.rootNode, set, node);
|
||||
// TODO: Position the nodes on the META
|
||||
// TODO: Put each group of nodes on their own META sheet
|
||||
this.core.setMemberRegistry(
|
||||
this.rootNode,
|
||||
META_CONSTANTS.META_ASPECT_SET_NAME,
|
||||
nodeId,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
position
|
||||
);
|
||||
this.core.setMemberRegistry(
|
||||
this.rootNode,
|
||||
tabId,
|
||||
nodeId,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
position
|
||||
);
|
||||
|
||||
if (attrs) { // Add the attributes
|
||||
attrs.forEach((name, index) => {
|
||||
var desc = null;
|
||||
if (typeof name !== 'string') {
|
||||
desc = name[Object.keys(name)[0]];
|
||||
name = Object.keys(name)[0];
|
||||
// Remove attributes not in the given list
|
||||
var currentAttrs = this.core.getValidAttributeNames(node),
|
||||
rmAttrs;
|
||||
|
||||
rmAttrs = _.difference(currentAttrs, attrs) // old attribute names
|
||||
.filter(attr => attr !== 'name');
|
||||
|
||||
if (rmAttrs.length) {
|
||||
this.logger.debug(`Removing ${rmAttrs.join(', ')} from ${name}`);
|
||||
}
|
||||
rmAttrs.forEach(attr => {
|
||||
this.core.delAttributeMeta(node, attr);
|
||||
if (this.core.getOwnAttribute(node, attr) !== undefined) {
|
||||
this.core.delAttribute(node, attr);
|
||||
}
|
||||
desc = desc || {};
|
||||
});
|
||||
|
||||
attrs.forEach((name, index) => {
|
||||
var desc = {};
|
||||
desc.argindex = index;
|
||||
desc.default = '';
|
||||
this.addAttribute(name, node, desc);
|
||||
});
|
||||
}
|
||||
@@ -178,11 +296,38 @@ define([
|
||||
return node;
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.getPositionFor = function(name, tabName) {
|
||||
var index = this.sheetCounts[tabName],
|
||||
dx = 140,
|
||||
dy = 100,
|
||||
MAX_WIDTH = 1200,
|
||||
x;
|
||||
|
||||
if (tabName === 'Convolution') {
|
||||
dx *= 1.3;
|
||||
dy *= 1.5;
|
||||
}
|
||||
|
||||
this.sheetCounts[tabName]++;
|
||||
if (index === 0) {
|
||||
return {
|
||||
x: MAX_WIDTH/2,
|
||||
y: 50
|
||||
};
|
||||
}
|
||||
|
||||
x = dx*index;
|
||||
return {
|
||||
x: x%MAX_WIDTH,
|
||||
y: Math.floor(x/MAX_WIDTH+1)*dy + 50
|
||||
};
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.addAttribute = function (name, node, def) {
|
||||
var initial,
|
||||
schema = {};
|
||||
|
||||
schema.type = def.type || 'integer';
|
||||
schema.type = def.type || 'string';
|
||||
if (schema.type === 'list') { // FIXME: add support for lists
|
||||
schema.type = 'string';
|
||||
}
|
||||
@@ -212,9 +357,7 @@ define([
|
||||
if (schema.type === 'boolean') {
|
||||
initial = initial !== null ? initial : false;
|
||||
}
|
||||
if (initial !== null) { // optional attribute - set default value
|
||||
this.core.setAttribute(node, name, initial);
|
||||
}
|
||||
this.core.setAttribute(node, name, initial);
|
||||
};
|
||||
|
||||
return CreateTorchMeta;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "CreateTorchMeta",
|
||||
"name": "Create Torch Meta",
|
||||
"name": "Update nn meta",
|
||||
"version": "0.1.0",
|
||||
"description": "Create metamodel from Torch yaml",
|
||||
"icon": {
|
||||
@@ -17,6 +17,14 @@
|
||||
"value": "",
|
||||
"valueType": "asset",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"name": "removeOldLayers",
|
||||
"displayName": "Delete old layers",
|
||||
"description": "Delete all layers not in the current description",
|
||||
"value": true,
|
||||
"valueType": "boolean",
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,927 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'executor/ExecutorClient',
|
||||
'plugin/PluginBase',
|
||||
'deepforge/plugin/LocalExecutor',
|
||||
'deepforge/plugin/PtrCodeGen',
|
||||
'deepforge/Constants',
|
||||
'./templates/index',
|
||||
'q',
|
||||
'underscore'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
ExecutorClient,
|
||||
PluginBase,
|
||||
LocalExecutor, // DeepForge operation primitives
|
||||
PtrCodeGen,
|
||||
CONSTANTS,
|
||||
Templates,
|
||||
Q,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
|
||||
var OUTPUT_INTERVAL = 1500,
|
||||
STDOUT_FILE = 'job_stdout.txt';
|
||||
|
||||
/**
|
||||
* Initializes a new instance of ExecuteJob.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin ExecuteJob.
|
||||
* @constructor
|
||||
*/
|
||||
var ExecuteJob = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
this._metadata = {};
|
||||
|
||||
// Metadata updating
|
||||
this._markForDeletion = {}; // id -> node
|
||||
this._oldMetadataByName = {}; // name -> id
|
||||
this.lastAppliedCmd = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
ExecuteJob.metadata = pluginMetadata;
|
||||
ExecuteJob.UPDATE_INTERVAL = 1500;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
ExecuteJob.prototype = Object.create(PluginBase.prototype);
|
||||
ExecuteJob.prototype.constructor = ExecuteJob;
|
||||
|
||||
/**
|
||||
* Main function for the plugin to execute. This will perform the execution.
|
||||
* Notes:
|
||||
* - Always log with the provided logger.[error,warning,info,debug].
|
||||
* - Do NOT put any user interaction logic UI, etc. inside this method.
|
||||
* - callback always has to be called even if error happened.
|
||||
*
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
ExecuteJob.prototype.main = function (callback) {
|
||||
// Check the activeNode to make sure it is a valid node
|
||||
var type = this.core.getMetaType(this.activeNode),
|
||||
typeName = type && this.core.getAttribute(type, 'name');
|
||||
|
||||
if (typeName !== 'Job') {
|
||||
return callback(`Cannot execute ${typeName} (expected Job)`, this.result);
|
||||
}
|
||||
|
||||
this._callback = callback;
|
||||
this.prepare()
|
||||
.then(() => this.executeJob(this.activeNode));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getConnections = function (nodes) {
|
||||
var conns = [];
|
||||
for (var i = nodes.length; i--;) {
|
||||
if (this.core.getPointerPath(nodes[i], 'src') &&
|
||||
this.core.getPointerPath(nodes[i], 'dst')) {
|
||||
|
||||
conns.push(nodes[i]);
|
||||
}
|
||||
}
|
||||
return conns;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.prepare = function () {
|
||||
var dstPortId,
|
||||
srcPortId,
|
||||
conns,
|
||||
executionNode = this.core.getParent(this.activeNode);
|
||||
|
||||
this.pipelineName = this.core.getAttribute(executionNode, 'name');
|
||||
return this.core.loadSubTree(executionNode)
|
||||
.then(nodes => {
|
||||
this.inputPortsFor = {};
|
||||
this.outputLineCount = {};
|
||||
|
||||
conns = this.getConnections(nodes);
|
||||
|
||||
// Create inputPortsFor for the given input ports
|
||||
for (var i = conns.length; i--;) {
|
||||
dstPortId = this.core.getPointerPath(conns[i], 'dst');
|
||||
srcPortId = this.core.getPointerPath(conns[i], 'src');
|
||||
|
||||
if (!this.inputPortsFor[dstPortId]) {
|
||||
this.inputPortsFor[dstPortId] = [srcPortId];
|
||||
} else {
|
||||
this.inputPortsFor[dstPortId].push(srcPortId);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => this.recordOldMetadata(this.activeNode));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.recordOldMetadata = function (job) {
|
||||
var nodeId = this.core.getPath(job),
|
||||
name,
|
||||
id,
|
||||
idsToDelete = [],
|
||||
child;
|
||||
|
||||
this.lastAppliedCmd[nodeId] = 0;
|
||||
this._oldMetadataByName[nodeId] = {};
|
||||
this._markForDeletion[nodeId] = {};
|
||||
return this.core.loadChildren(job)
|
||||
.then(jobChildren => {
|
||||
// Remove any metadata nodes
|
||||
for (var i = jobChildren.length; i--;) {
|
||||
child = jobChildren[i];
|
||||
if (this.isMetaTypeOf(child, this.META.Metadata)) {
|
||||
id = this.core.getPath(child);
|
||||
name = this.core.getAttribute(child, 'name');
|
||||
|
||||
this._markForDeletion[nodeId][id] = child;
|
||||
this._oldMetadataByName[nodeId][name] = id;
|
||||
|
||||
// children of metadata nodes get deleted
|
||||
idsToDelete = idsToDelete
|
||||
.concat(this.core.getChildrenPaths(child));
|
||||
}
|
||||
}
|
||||
|
||||
// make the deletion ids relative to the job node
|
||||
idsToDelete = idsToDelete.map(id => id.replace(nodeId, ''));
|
||||
return Q.all(idsToDelete.map(id => this.core.loadByPath(job, id)));
|
||||
})
|
||||
.then(nodes => nodes.forEach(node => this.core.deleteNode(node)));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.clearOldMetadata = function (job) {
|
||||
var nodeId = this.core.getPath(job),
|
||||
nodeIds = Object.keys(this._markForDeletion[nodeId]);
|
||||
|
||||
for (var i = nodeIds.length; i--;) {
|
||||
this.core.deleteNode(this._markForDeletion[nodeId][nodeIds[i]]);
|
||||
}
|
||||
delete this.lastAppliedCmd[nodeId];
|
||||
delete this._markForDeletion[nodeId];
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.onOperationFail =
|
||||
ExecuteJob.prototype.onOperationComplete =
|
||||
ExecuteJob.prototype.onComplete = function (opNode, err) {
|
||||
var job = this.core.getParent(opNode),
|
||||
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}` :
|
||||
`${name} executed successfully!`,
|
||||
promise = Q();
|
||||
|
||||
this.core.setAttribute(job, 'status', status);
|
||||
this.logger.info(`Setting ${name} (${jobId}) status to ${status}`);
|
||||
this.clearOldMetadata(job);
|
||||
|
||||
if (err) {
|
||||
this.core.setAttribute(exec, 'status', 'failed');
|
||||
} else {
|
||||
// Check if all the other jobs are successful. If so, set the
|
||||
// execution status to 'success'
|
||||
promise = this.core.loadChildren(exec)
|
||||
.then(nodes => {
|
||||
var execSuccess = true,
|
||||
type,
|
||||
typeName;
|
||||
|
||||
for (var i = nodes.length; i--;) {
|
||||
type = this.core.getMetaType(nodes[i]);
|
||||
typeName = this.core.getAttribute(type, 'name');
|
||||
|
||||
if (typeName === 'Job' &&
|
||||
this.core.getAttribute(nodes[i], 'status') !== 'success') {
|
||||
execSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (execSuccess) {
|
||||
this.core.setAttribute(exec, 'status', 'success');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
promise
|
||||
.then(() => this.save(msg))
|
||||
.then(() => {
|
||||
this.result.setSuccess(!err);
|
||||
this._callback(err, this.result);
|
||||
})
|
||||
.catch(err => {
|
||||
// Result success is false at invocation.
|
||||
this._callback(err, this.result);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getOperation = function (job) {
|
||||
return this.core.loadChildren(job).then(children =>
|
||||
children.find(child => this.isMetaTypeOf(child, this.META.Operation)));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.executeJob = function (job) {
|
||||
return this.getOperation(job).then(node => {
|
||||
var jobId = this.core.getPath(job),
|
||||
name = this.core.getAttribute(node, 'name'),
|
||||
localTypeId = this.getLocalOperationType(node),
|
||||
artifact,
|
||||
artifactName,
|
||||
files,
|
||||
data = {},
|
||||
inputs;
|
||||
|
||||
// Execute any special operation types here - not on an executor
|
||||
this.logger.debug(`Executing operation "${name}"`);
|
||||
if (localTypeId !== null) {
|
||||
return this.executeLocalOperation(localTypeId, node);
|
||||
} else {
|
||||
// Generate all execution files
|
||||
return this.createOperationFiles(node).then(results => {
|
||||
this.logger.info('Created operation files!');
|
||||
files = results;
|
||||
artifactName = `${name}_${jobId.replace(/\//g, '_')}-execution-files`;
|
||||
artifact = this.blobClient.createArtifact(artifactName);
|
||||
|
||||
// Add the input assets
|
||||
// - get the metadata (name)
|
||||
// - add the given inputs
|
||||
inputs = Object.keys(files.inputAssets);
|
||||
|
||||
return Q.all(
|
||||
inputs.map(input => { // Get the metadata for each input
|
||||
var hash = files.inputAssets[input];
|
||||
|
||||
// data asset for "input"
|
||||
return this.blobClient.getMetadata(hash);
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(mds => {
|
||||
// get (input, filename) tuples
|
||||
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;
|
||||
});
|
||||
|
||||
delete files.inputAssets;
|
||||
|
||||
// Add pointer assets
|
||||
Object.keys(files.ptrAssets)
|
||||
.forEach(path => data[path] = files.ptrAssets[path]);
|
||||
|
||||
delete files.ptrAssets;
|
||||
|
||||
// Add the executor config
|
||||
return this.getOutputs(node);
|
||||
})
|
||||
.then(outputArgs => {
|
||||
var config,
|
||||
outputs,
|
||||
file;
|
||||
|
||||
outputs = outputArgs.map(pair => pair[0])
|
||||
.map(name => {
|
||||
return {
|
||||
name: name,
|
||||
resultPatterns: [`outputs/${name}`]
|
||||
};
|
||||
});
|
||||
|
||||
outputs.push(
|
||||
{
|
||||
name: 'stdout',
|
||||
resultPatterns: [STDOUT_FILE]
|
||||
},
|
||||
{
|
||||
name: name + '-all-files',
|
||||
resultPatterns: []
|
||||
}
|
||||
);
|
||||
|
||||
config = {
|
||||
cmd: 'bash',
|
||||
args: ['run.sh'],
|
||||
outputInterval: OUTPUT_INTERVAL,
|
||||
resultArtifacts: outputs
|
||||
};
|
||||
files['executor_config.json'] = JSON.stringify(config, null, 4);
|
||||
files['run.sh'] = Templates.BASH;
|
||||
|
||||
// Save the artifact
|
||||
// Remove empty hashes
|
||||
for (file in data) {
|
||||
if (!data[file]) {
|
||||
this.logger.warn(`Empty data hash has been found for file "${file}". Removing it...`);
|
||||
delete data[file];
|
||||
}
|
||||
}
|
||||
return artifact.addObjectHashes(data);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added ptr/input data hashes for "${artifactName}"`);
|
||||
return artifact.addFiles(files);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added execution files for "${artifactName}"`);
|
||||
return artifact.save();
|
||||
})
|
||||
.then(hash => {
|
||||
this.logger.info(`Saved execution files "${artifactName}"`);
|
||||
this.result.addArtifact(hash); // Probably only need this for debugging...
|
||||
this.executeDistOperation(job, node, hash);
|
||||
})
|
||||
.fail(e => {
|
||||
this.onOperationFail(node, `Distributed operation "${name}" failed ${e}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.executeDistOperation = function (job, opNode, hash) {
|
||||
var name = this.core.getAttribute(opNode, 'name'),
|
||||
jobId = this.core.getPath(job),
|
||||
isHttps = typeof window === 'undefined' ? false :
|
||||
window.location.protocol !== 'http:',
|
||||
executor = new ExecutorClient({
|
||||
logger: this.logger,
|
||||
serverPort: this.gmeConfig.server.port,
|
||||
httpsecure: isHttps
|
||||
});
|
||||
|
||||
this.logger.info(`Executing operation "${name}"`);
|
||||
|
||||
this.outputLineCount[jobId] = 0;
|
||||
// Set the job status to 'running'
|
||||
this.core.setAttribute(job, 'status', 'queued');
|
||||
this.core.setAttribute(job, 'stdout', '');
|
||||
this.logger.info(`Setting ${jobId} status to "queued" (${this.currentHash})`);
|
||||
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))
|
||||
.catch(err => this.logger.error(`Could not execute "${name}": ${err}`));
|
||||
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createOperationFiles = function (node) {
|
||||
var files = {};
|
||||
// For each operation, generate the output files:
|
||||
// inputs/<arg-name>/init.lua (respective data deserializer)
|
||||
// pointers/<name>/init.lua (result of running the main plugin on pointer target - may need a rename)
|
||||
// outputs/<name>/ (make dirs for each of the outputs)
|
||||
// outputs/init.lua (serializers for data outputs)
|
||||
//
|
||||
// attributes.lua (returns lua table of operation attributes)
|
||||
// init.lua (main file -> calls main and serializes outputs)
|
||||
// <name>.lua (entry point -> calls main operation code)
|
||||
|
||||
// add the given files
|
||||
this.logger.info('About to create dist execution files');
|
||||
return this.createEntryFile(node, files)
|
||||
.then(() => this.createClasses(node, files))
|
||||
.then(() => this.createCustomLayers(node, files))
|
||||
.then(() => this.createInputs(node, files))
|
||||
.then(() => this.createOutputs(node, files))
|
||||
.then(() => this.createMainFile(node, files))
|
||||
.then(() => {
|
||||
this.createAttributeFile(node, files);
|
||||
return Q.ninvoke(this, 'createPointers', node, files);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createEntryFile = function (node, files) {
|
||||
this.logger.info('Creating entry files...');
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
content = {};
|
||||
|
||||
// inputs and outputs
|
||||
content.name = name;
|
||||
content.outputs = outputs;
|
||||
|
||||
files['init.lua'] = _.template(Templates.ENTRY)(content);
|
||||
|
||||
// Create the deepforge file
|
||||
files['deepforge.lua'] = _.template(Templates.DEEPFORGE)(CONSTANTS);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createClasses = function (node, files) {
|
||||
var metaDict = this.core.getAllMetaNodes(this.rootNode),
|
||||
isClass,
|
||||
metanodes,
|
||||
classNodes,
|
||||
code;
|
||||
|
||||
this.logger.info('Creating custom layer file...');
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
isClass = this.getTypeDictFor('Complex', metanodes);
|
||||
|
||||
classNodes = metanodes.filter(node => {
|
||||
var base = this.core.getBase(node),
|
||||
baseId = this.core.getPath(base);
|
||||
|
||||
return isClass[baseId];
|
||||
});
|
||||
|
||||
// Get the code definitions for each
|
||||
code = classNodes.map(node =>
|
||||
`require './${this.core.getAttribute(node, 'name')}.lua'`
|
||||
).join('\n');
|
||||
|
||||
// Create the class files
|
||||
classNodes.forEach(node => {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
files[`classes/${name}.lua`] = this.core.getAttribute(node, 'code');
|
||||
});
|
||||
|
||||
// Create the custom layers file
|
||||
files['classes/init.lua'] = code;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getTypeDictFor = function (name, metanodes) {
|
||||
var isType = {};
|
||||
// Get all the custom layers
|
||||
for (var i = metanodes.length; i--;) {
|
||||
if (this.core.getAttribute(metanodes[i], 'name') === name) {
|
||||
isType[this.core.getPath(metanodes[i])] = true;
|
||||
}
|
||||
}
|
||||
return isType;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createCustomLayers = function (node, files) {
|
||||
var metaDict = this.core.getAllMetaNodes(this.rootNode),
|
||||
isCustomLayer,
|
||||
metanodes,
|
||||
customLayers,
|
||||
code;
|
||||
|
||||
this.logger.info('Creating custom layer file...');
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
isCustomLayer = this.getTypeDictFor('CustomLayer', metanodes);
|
||||
|
||||
customLayers = metanodes.filter(node =>
|
||||
this.core.getMixinPaths(node).some(id => isCustomLayer[id]));
|
||||
|
||||
// Get the code definitions for each
|
||||
code = 'require \'nn\'\n\n' + customLayers
|
||||
.map(node => this.core.getAttribute(node, 'code')).join('\n');
|
||||
|
||||
// Create the custom layers file
|
||||
files['custom-layers.lua'] = code;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createInputs = function (node, files) {
|
||||
var tplContents,
|
||||
inputs;
|
||||
|
||||
this.logger.info('Retrieving inputs and deserialize fns...');
|
||||
return this.getInputs(node)
|
||||
.then(allInputs => {
|
||||
// For each input, match the connection with the input name
|
||||
// [ name, type ] => [ name, type, node ]
|
||||
//
|
||||
// For each input,
|
||||
// - create the deserializer
|
||||
// - put it in inputs/<name>/init.lua
|
||||
// - copy the data asset to /inputs/<name>/init.lua
|
||||
inputs = allInputs
|
||||
.filter(pair => !!this.core.getAttribute(pair[2], 'data')); // remove empty inputs
|
||||
|
||||
files.inputAssets = {}; // data assets
|
||||
return Q.all(inputs.map(pair => {
|
||||
var name = pair[0],
|
||||
node = pair[2],
|
||||
nodeId = this.core.getPath(node),
|
||||
fromNodeId;
|
||||
|
||||
// Get the deserialize function. First, try to get it from
|
||||
// the source method (this guarantees that the correct
|
||||
// deserialize method is used despite any auto-upcasting
|
||||
fromNodeId = this.inputPortsFor[nodeId][0] || nodeId;
|
||||
|
||||
return this.core.loadByPath(this.rootNode, fromNodeId)
|
||||
.then(fromNode => {
|
||||
var deserFn,
|
||||
base,
|
||||
className;
|
||||
|
||||
deserFn = this.core.getAttribute(fromNode, 'deserialize');
|
||||
|
||||
if (this.isMetaTypeOf(node, this.META.Complex)) {
|
||||
// Complex objects are expected to define their own
|
||||
// (static) deserialize factory method
|
||||
base = this.core.getMetaType(node);
|
||||
className = this.core.getAttribute(base, 'name');
|
||||
deserFn = `return ${className}.deserialize(path)`;
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
code: deserFn
|
||||
};
|
||||
});
|
||||
}));
|
||||
})
|
||||
.then(_tplContents => {
|
||||
tplContents = _tplContents;
|
||||
var hashes = inputs
|
||||
// storing the hash for now...
|
||||
.map(pair =>
|
||||
files.inputAssets[pair[0]] = this.core.getAttribute(pair[2], 'data')
|
||||
);
|
||||
return Q.all(hashes.map(h => this.blobClient.getMetadata(h)));
|
||||
})
|
||||
.then(metadatas => {
|
||||
// Create the deserializer
|
||||
tplContents.forEach((ctnt, i) => {
|
||||
// Get the name of the given asset
|
||||
ctnt.filename = metadatas[i].name;
|
||||
files['inputs/' + ctnt.name + '/init.lua'] = _.template(Templates.DESERIALIZE)(ctnt);
|
||||
});
|
||||
return files;
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createOutputs = function (node, files) {
|
||||
// For each of the output types, grab their serialization functions and
|
||||
// create the `outputs/init.lua` file
|
||||
this.logger.info('Creating outputs/init.lua...');
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var outputTypes = outputs
|
||||
// Get the serialize functions for each
|
||||
.map(tuple => {
|
||||
var node = tuple[2],
|
||||
serFn = this.core.getAttribute(node, 'serialize');
|
||||
|
||||
if (this.isMetaTypeOf(node, this.META.Complex)) {
|
||||
// Complex objects are expected to define their own
|
||||
// serialize methods
|
||||
serFn = 'if data ~= nil then data:serialize(path) end';
|
||||
}
|
||||
|
||||
return [tuple[1], serFn];
|
||||
});
|
||||
|
||||
files['outputs/init.lua'] = _.template(Templates.SERIALIZE)({types: outputTypes});
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createMainFile = function (node, files) {
|
||||
this.logger.info('Creating main file...');
|
||||
return this.getInputs(node)
|
||||
.then(inputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
code = this.core.getAttribute(node, 'code'),
|
||||
pointers = this.core.getPointerNames(node).filter(ptr => ptr !== 'base'),
|
||||
content = {
|
||||
name: name
|
||||
};
|
||||
|
||||
// Get input data arguments
|
||||
content.inputs = inputs
|
||||
.map(pair => [pair[0], !this.core.getAttribute(pair[2], 'data')]); // remove empty inputs
|
||||
|
||||
// Defined variables for each pointers
|
||||
content.pointers = pointers
|
||||
.map(id => [id, this.core.getPointerPath(node, id) === null]);
|
||||
|
||||
// Add remaining code
|
||||
content.code = code;
|
||||
|
||||
files['main.lua'] = _.template(Templates.MAIN)(content);
|
||||
|
||||
// Set the line offset
|
||||
var lineOffset = this.getLineOffset(files['main.lua'], code);
|
||||
this.core.setAttribute(node, CONSTANTS.LINE_OFFSET, lineOffset);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getLineOffset = function (main, snippet) {
|
||||
var i = main.indexOf(snippet),
|
||||
lines = main.substring(0, i).match(/\n/g);
|
||||
|
||||
return lines ? lines.length : 0;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createAttributeFile = function (node, files) {
|
||||
var skip = ['code'],
|
||||
numRegex = /^\d+\.?\d*((e|e-)\d+)?$/,
|
||||
table;
|
||||
|
||||
this.logger.info('Creating attributes file...');
|
||||
table = '{\n\t' + this.core.getAttributeNames(node)
|
||||
.filter(attr => skip.indexOf(attr) === -1)
|
||||
.map(name => {
|
||||
var value = this.core.getAttribute(node, name);
|
||||
if (!numRegex.test(value)) {
|
||||
value = `"${value}"`;
|
||||
}
|
||||
return [name, value];
|
||||
})
|
||||
.map(pair => pair.join(' = '))
|
||||
.join(',\n\t') + '\n}';
|
||||
|
||||
files['attributes.lua'] = `-- attributes of ${this.core.getAttribute(node, 'name')}\nreturn ${table}`;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createPointers = function (node, files, cb) {
|
||||
var pointers,
|
||||
nIds;
|
||||
|
||||
this.logger.info('Creating pointers file...');
|
||||
pointers = this.core.getPointerNames(node)
|
||||
.filter(name => name !== 'base')
|
||||
.filter(id => this.core.getPointerPath(node, id) !== null);
|
||||
|
||||
nIds = pointers.map(p => this.core.getPointerPath(node, p));
|
||||
files.ptrAssets = {};
|
||||
Q.all(
|
||||
nIds.map(nId => this.getPtrCodeHash(nId))
|
||||
)
|
||||
.then(resultHashes => {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
this.logger.info(`Pointer generation for ${name} FINISHED!`);
|
||||
resultHashes.forEach((hash, index) => {
|
||||
files.ptrAssets[`pointers/${pointers[index]}/init.lua`] = hash;
|
||||
});
|
||||
return cb(null, files);
|
||||
})
|
||||
.fail(e => {
|
||||
this.logger.error(`Could not generate pointer files for ${this.core.getAttribute(node, 'name')}: ${JSON.stringify(e)}`);
|
||||
return cb(e);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.watchOperation = function (executor, hash, op, job) {
|
||||
var jobId = this.core.getPath(job),
|
||||
opId = this.core.getPath(op),
|
||||
info,
|
||||
name;
|
||||
|
||||
return executor.getInfo(hash)
|
||||
.then(_info => { // Update the job's stdout
|
||||
var actualLine, // on executing job
|
||||
currentLine = this.outputLineCount[jobId];
|
||||
|
||||
info = _info;
|
||||
actualLine = info.outputNumber;
|
||||
if (actualLine !== null && actualLine >= currentLine) {
|
||||
this.outputLineCount[jobId] = actualLine + 1;
|
||||
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');
|
||||
|
||||
// parse deepforge commands
|
||||
output = this.parseForMetadataCmds(job, output);
|
||||
|
||||
if (output) {
|
||||
stdout += output;
|
||||
this.core.setAttribute(job, 'stdout', stdout);
|
||||
return this.save(`Received stdout for ${jobName}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (info.status === 'CREATED' || info.status === 'RUNNING') {
|
||||
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}`);
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
this.watchOperation.bind(this, executor, hash, op, job),
|
||||
ExecuteJob.UPDATE_INTERVAL
|
||||
);
|
||||
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);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => this.logger.error(`Could not get op info for ${opId}: ${err}`));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.onDistOperationComplete = function (node, result) {
|
||||
var nodeId = this.core.getPath(node),
|
||||
outputMap = {},
|
||||
outputs;
|
||||
|
||||
// Match the output names to the actual nodes
|
||||
// Create an array of [name, node]
|
||||
// For now, just match by type. Later we may use ports for input/outputs
|
||||
// Store the results in the outgoing ports
|
||||
this.getOutputs(node)
|
||||
.then(outputPorts => {
|
||||
outputs = outputPorts.map(tuple => [tuple[0], tuple[2]]);
|
||||
outputs.forEach(output => outputMap[output[0]] = output[1]);
|
||||
|
||||
// this should not be in directories -> flatten the data!
|
||||
return Q.all(outputs.map(tuple => // [ name, node ]
|
||||
this.blobClient.getArtifact(result.resultHashes[tuple[0]])
|
||||
));
|
||||
})
|
||||
.then(artifacts => {
|
||||
this.logger.info(`preparing outputs -> retrieved ${artifacts.length} objects`);
|
||||
// Create new metadata for each
|
||||
artifacts.forEach((artifact, i) => {
|
||||
var name = outputs[i][0],
|
||||
outputData = artifact.descriptor.content[`outputs/${name}`],
|
||||
hash = outputData && outputData.content;
|
||||
|
||||
if (hash) {
|
||||
this.core.setAttribute(outputMap[name], 'data', hash);
|
||||
this.logger.info(`Setting ${nodeId} data to ${hash}`);
|
||||
}
|
||||
});
|
||||
|
||||
return this.onOperationComplete(node);
|
||||
})
|
||||
.fail(e => this.onOperationFail(node, `Operation ${nodeId} failed: ${e}`));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getOutputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Outputs);
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getInputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Inputs);
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getOperationData = function (node, metaType) {
|
||||
// Load the children and the output's children
|
||||
return this.core.loadChildren(node)
|
||||
.then(containers => {
|
||||
var outputs = containers.find(c => this.core.isTypeOf(c, metaType));
|
||||
return outputs ? this.core.loadChildren(outputs) : [];
|
||||
})
|
||||
.then(outputs => {
|
||||
var bases = outputs.map(node => this.core.getMetaType(node));
|
||||
// return [[arg1, Type1, node1], [arg2, Type2, node2]]
|
||||
return outputs.map((node, i) => [
|
||||
this.core.getAttribute(node, 'name'),
|
||||
this.core.getAttribute(bases[i], 'name'),
|
||||
node
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
//////////////////////////// Special Operations ////////////////////////////
|
||||
ExecuteJob.prototype.executeLocalOperation = function (type, node) {
|
||||
// Retrieve the given LOCAL_OP type
|
||||
if (!this[type]) {
|
||||
this.logger.error(`No local operation handler for ${type}`);
|
||||
}
|
||||
this.logger.info(`Running local operation ${type}`);
|
||||
|
||||
return this[type](node);
|
||||
};
|
||||
|
||||
_.extend(
|
||||
ExecuteJob.prototype,
|
||||
PtrCodeGen.prototype,
|
||||
LocalExecutor.prototype
|
||||
);
|
||||
|
||||
//////////////////////////// Metadata ////////////////////////////
|
||||
ExecuteJob.prototype.parseForMetadataCmds = function (job, text, skip) {
|
||||
var jobId = this.core.getPath(job),
|
||||
lines = text.split('\n'),
|
||||
args,
|
||||
result = [],
|
||||
cmdCnt = 0,
|
||||
cmd;
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
// Check for a deepforge command
|
||||
if (lines[i].indexOf(CONSTANTS.START_CMD) === 0) {
|
||||
cmdCnt++;
|
||||
args = lines[i].split(/\s+/);
|
||||
args.shift();
|
||||
cmd = args[0];
|
||||
args[0] = job;
|
||||
if (this[cmd] && (!skip || cmdCnt >= this.lastAppliedCmd[jobId])) {
|
||||
this[cmd].apply(this, args);
|
||||
this.lastAppliedCmd[jobId]++;
|
||||
} else if (!this[cmd]) {
|
||||
this.logger.error(`Invoked unimplemented metadata method "${cmd}"`);
|
||||
}
|
||||
} else {
|
||||
result.push(lines[i]);
|
||||
}
|
||||
}
|
||||
return result.join('\n');
|
||||
};
|
||||
|
||||
ExecuteJob.prototype[CONSTANTS.GRAPH_CREATE] = function (job, id) {
|
||||
var graph,
|
||||
name = Array.prototype.slice.call(arguments, 2).join(' '),
|
||||
jobId = this.core.getPath(job),
|
||||
oldMetadata = this._oldMetadataByName[jobId],
|
||||
oldId;
|
||||
|
||||
id = jobId + '/' + id;
|
||||
this.logger.info(`Creating graph ${id} named ${name}`);
|
||||
|
||||
// Check if the graph already exists
|
||||
if (oldMetadata && oldMetadata[name]) {
|
||||
oldId = oldMetadata[name];
|
||||
graph = this._markForDeletion[jobId][oldId];
|
||||
|
||||
// Reset points
|
||||
this.core.setAttribute(graph, 'points', '');
|
||||
|
||||
delete this._markForDeletion[jobId][oldId];
|
||||
} else { // create new graph
|
||||
graph = this.core.createNode({
|
||||
base: this.META.Graph,
|
||||
parent: job
|
||||
});
|
||||
|
||||
if (name) {
|
||||
this.core.setAttribute(graph, 'name', name);
|
||||
}
|
||||
}
|
||||
|
||||
this._metadata[id] = graph;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype[CONSTANTS.GRAPH_PLOT] = function (job, id, x, y) {
|
||||
var jobId = this.core.getPath(job),
|
||||
graph,
|
||||
points;
|
||||
|
||||
|
||||
id = jobId + '/' + id;
|
||||
this.logger.info(`Adding point ${x}, ${y} to ${id}`);
|
||||
graph = this._metadata[id];
|
||||
if (!graph) {
|
||||
this.logger.warn(`Can't add point to non-existent graph: ${id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
points = this.core.getAttribute(graph, 'points');
|
||||
points += `${x},${y};`;
|
||||
this.core.setAttribute(graph, 'points', points);
|
||||
};
|
||||
|
||||
ExecuteJob.prototype[CONSTANTS.GRAPH_CREATE_LINE] = function (job, graphId, id) {
|
||||
var jobId = this.core.getPath(job),
|
||||
graph = this._metadata[jobId + '/' + graphId],
|
||||
name = Array.prototype.slice.call(arguments, 3).join(' '),
|
||||
line;
|
||||
|
||||
// Create a 'line' node in the given Graph metadata node
|
||||
name = name.replace(/\s+$/, '');
|
||||
line = this.core.createNode({
|
||||
base: this.META.Line,
|
||||
parent: graph
|
||||
});
|
||||
this.core.setAttribute(line, 'name', name);
|
||||
this._metadata[jobId + '/' + id] = line;
|
||||
};
|
||||
|
||||
return ExecuteJob;
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "ExecuteJob",
|
||||
"name": "ExecuteJob",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": []
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
-- Instantiate the deepforge object
|
||||
deepforge = {}
|
||||
|
||||
function deepforge.id()
|
||||
if __deepforge_id == nil then
|
||||
__deepforge_id = 0
|
||||
end
|
||||
__deepforge_id = __deepforge_id + 1
|
||||
return __deepforge_id
|
||||
end
|
||||
|
||||
function deepforge._cmd(...)
|
||||
local cmd = '<%= START_CMD %>'
|
||||
local arg = {...}
|
||||
local n = #arg
|
||||
for i=1,n do
|
||||
cmd = cmd .. ' ' .. tostring(arg[i])
|
||||
end
|
||||
print(cmd)
|
||||
end
|
||||
|
||||
Graph = torch.class('deepforge.Graph')
|
||||
|
||||
function Graph:__init(name)
|
||||
self.id = deepforge.id()
|
||||
self.name = name
|
||||
deepforge._cmd('<%= GRAPH_CREATE %>', self.id, name)
|
||||
end
|
||||
|
||||
_Line = torch.class('deepforge._Line')
|
||||
|
||||
function _Line:__init(graphId, name, opts)
|
||||
self.id = deepforge.id()
|
||||
self.name = name
|
||||
deepforge._cmd('<%= GRAPH_CREATE_LINE %>', graphId, self.id, name)
|
||||
end
|
||||
|
||||
function _Line:add(x, y)
|
||||
deepforge._cmd('<%= GRAPH_PLOT %>', self.id, x, y)
|
||||
end
|
||||
|
||||
function Graph:line(name, opts)
|
||||
return deepforge._Line(self.id, name, opts)
|
||||
end
|
||||
|
||||
return deepforge
|
||||
+2
@@ -1,4 +1,6 @@
|
||||
require 'paths'
|
||||
|
||||
local path = 'inputs/<%= name %>/<%= filename %>'
|
||||
local abs_path = paths.concat('inputs', '<%= name %>', '<%= filename %>')
|
||||
|
||||
<%= code %>
|
||||
+5
-2
@@ -1,7 +1,10 @@
|
||||
-- Instantiate the deepforge object
|
||||
require './deepforge'
|
||||
|
||||
-- run the <%= name %> and serialize the results
|
||||
print('\n############### Running "<%= name %>" Operation ############### ')
|
||||
print('\n############### Running "<%= name.replace(/'/g, '\\\'') %>" Operation ############### ')
|
||||
results = require './main'
|
||||
print('############### "<%= name %>" Operation Complete! ###############')
|
||||
print('############### "<%= name.replace(/'/g, '\\\'') %>" Operation Complete! ###############')
|
||||
|
||||
-- serialize by type
|
||||
outputs = require './outputs'
|
||||
+5
@@ -2,19 +2,24 @@
|
||||
define([
|
||||
'text!./entry.ejs',
|
||||
'text!./main.ejs',
|
||||
'text!./deepforge.ejs',
|
||||
'text!./serialize.ejs',
|
||||
'text!./deserialize.ejs'
|
||||
], function(
|
||||
ENTRY,
|
||||
MAIN,
|
||||
DEEPFORGE,
|
||||
SERIALIZE,
|
||||
DESERIALIZE
|
||||
) {
|
||||
|
||||
var BASH = 'th init.lua 2>&1';
|
||||
return {
|
||||
BASH,
|
||||
ENTRY,
|
||||
MAIN,
|
||||
SERIALIZE,
|
||||
DEEPFORGE,
|
||||
DESERIALIZE
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
-- load custom layers
|
||||
require './custom-layers'
|
||||
|
||||
-- load custom class definitions
|
||||
require './classes'
|
||||
|
||||
-- input data<% inputs.forEach(function(pair) { var input = pair[0], isNil = pair[1];%>
|
||||
<%= isNil ? 'local ' : ''%><%= input %> = <% if (isNil) { %>nil<% } else { %>require './inputs/<%= input %>'<%}}); %>
|
||||
|
||||
-- load references<% pointers.forEach(function(pair) { var pointer = pair[0], isNil = pair[1];%>
|
||||
<%= isNil ? 'local ' : ''%><%= pointer %> = <% if (isNil) { %>nil<% } else { %>require './pointers/<%= pointer %>'<%}}); %>
|
||||
attributes = require './attributes'
|
||||
|
||||
-- main operation code for <%= name %>
|
||||
<%= code %>
|
||||
@@ -1,25 +1,21 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*globals define */
|
||||
/*jshint node:true, browser:true, esversion: 6*/
|
||||
|
||||
define([
|
||||
'plugin/CreateExecution/CreateExecution/CreateExecution',
|
||||
'plugin/ExecuteJob/ExecuteJob/ExecuteJob',
|
||||
'common/storage/constants',
|
||||
'common/core/constants',
|
||||
'q',
|
||||
'text!./metadata.json',
|
||||
'./Templates/index',
|
||||
'./LocalExecutor',
|
||||
'executor/ExecutorClient',
|
||||
'jszip',
|
||||
'underscore'
|
||||
], function (
|
||||
CreateExecution,
|
||||
ExecuteJob,
|
||||
STORAGE_CONSTANTS,
|
||||
CONSTANTS,
|
||||
Q,
|
||||
pluginMetadata,
|
||||
Templates,
|
||||
LocalExecutor, // DeepForge operation primitives
|
||||
ExecutorClient,
|
||||
JsZip,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
@@ -37,6 +33,24 @@ define([
|
||||
CreateExecution.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
|
||||
this._currentSave = Q();
|
||||
this.initRun();
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
ExecutePipeline.metadata = pluginMetadata;
|
||||
|
||||
// Prototypical inheritance from CreateExecution.
|
||||
ExecutePipeline.prototype = Object.create(CreateExecution.prototype);
|
||||
ExecutePipeline.prototype.constructor = ExecutePipeline;
|
||||
|
||||
_.extend(ExecutePipeline.prototype, ExecuteJob.prototype);
|
||||
|
||||
ExecutePipeline.prototype.initRun = function () {
|
||||
// Cache
|
||||
this.nodes = {};
|
||||
|
||||
@@ -50,20 +64,25 @@ define([
|
||||
this.finished = {};
|
||||
this.completedCount = 0;
|
||||
this.totalCount = 0;
|
||||
this.outputLineCount = {};
|
||||
|
||||
// When a pipeline fails, it will let all running jobs finish and record
|
||||
// the results of each job
|
||||
//
|
||||
// The following variables are used to...
|
||||
// - keep track of the number of jobs currently running
|
||||
// - keep track if the pipeline has errored
|
||||
// - if so, don't start any more jobs
|
||||
this.pipelineError = null;
|
||||
this.runningJobs = 0;
|
||||
|
||||
// metadata records
|
||||
this._metadata = {};
|
||||
this._markForDeletion = {}; // id -> node
|
||||
this._oldMetadataByName = {}; // name -> id
|
||||
this.lastAppliedCmd = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
ExecutePipeline.metadata = pluginMetadata;
|
||||
ExecutePipeline.UPDATE_INTERVAL = 1500;
|
||||
|
||||
// Prototypical inheritance from CreateExecution.
|
||||
ExecutePipeline.prototype = Object.create(CreateExecution.prototype);
|
||||
ExecutePipeline.prototype.constructor = ExecutePipeline;
|
||||
|
||||
/**
|
||||
* Main function for the plugin to execute. This will perform the execution.
|
||||
* Notes:
|
||||
@@ -78,16 +97,16 @@ define([
|
||||
// inputs for the next operation cannot be created until the inputs have
|
||||
// been generated
|
||||
|
||||
this.initRun();
|
||||
var startPromise;
|
||||
if (this.core.isTypeOf(this.activeNode, this.META.Pipeline)) {
|
||||
// If starting with a pipeline, we will create an Execution first
|
||||
startPromise = this.createExecution(this.activeNode)
|
||||
.then(execNode => {
|
||||
this.activeNode = execNode;
|
||||
return this.core.loadSubTree(this.activeNode);
|
||||
});
|
||||
} else if (this.core.isTypeOf(this.activeNode, this.META.Execution)) {
|
||||
startPromise = this.core.loadSubTree(this.activeNode);
|
||||
startPromise = Q();
|
||||
} else {
|
||||
return callback('Current node is not a Pipeline or Execution!', this.result);
|
||||
}
|
||||
@@ -96,41 +115,88 @@ define([
|
||||
this.debug = true; // this.getCurrentConfig().debug;
|
||||
this._callback = callback;
|
||||
|
||||
startPromise.then(subtree => {
|
||||
startPromise
|
||||
.then(() => this.core.loadSubTree(this.activeNode))
|
||||
.then(subtree => {
|
||||
var children = subtree
|
||||
.filter(n => this.core.getParent(n) === this.activeNode);
|
||||
|
||||
this.pipelineName = this.core.getAttribute(this.activeNode, 'name');
|
||||
this.buildCache(subtree);
|
||||
this.parsePipeline(children); // record deps, etc
|
||||
|
||||
//if (this.getCurrentConfig().reset) {
|
||||
this.clearResults();
|
||||
//}
|
||||
|
||||
// Execute the operations in the proper order
|
||||
this.executePipeline();
|
||||
return this.clearResults();
|
||||
})
|
||||
.then(() => this.executePipeline())
|
||||
.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(() => CreateExecution.prototype.save.call(this, msg))
|
||||
.then(result => {
|
||||
var msg;
|
||||
if (result.status === STORAGE_CONSTANTS.FORKED) {
|
||||
msg = `"${this.pipelineName}" execution has forked to "${result.forkName}"`;
|
||||
this.sendNotification(msg);
|
||||
}
|
||||
});
|
||||
|
||||
return this._currentSave;
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.isInputData = function (node) {
|
||||
var prnt = this.core.getParent(node);
|
||||
return this.core.isTypeOf(prnt, this.META.Inputs);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.clearResults = function () {
|
||||
var nodes = Object.keys(this.nodes).map(id => this.nodes[id]);
|
||||
// Clear the pipeline's results
|
||||
this.logger.info('Clearing all intermediate execution results');
|
||||
|
||||
nodes.filter(node => this.core.isTypeOf(node, this.META.Data))
|
||||
// Only input data nodes should be cleared. Outputs will be overwritten
|
||||
.filter(node => this.isInputData(node))
|
||||
.forEach(conn => this.core.delAttribute(conn, 'data'));
|
||||
|
||||
// Set the status for each job to 'pending'
|
||||
nodes.filter(node => this.core.isTypeOf(node, this.META.Job))
|
||||
.forEach(node => this.core.setAttribute(node, 'status', 'pending'));
|
||||
.forEach(node => {
|
||||
this.recordOldMetadata(node);
|
||||
this.core.setAttribute(node, 'status', 'pending');
|
||||
});
|
||||
|
||||
// Set the status of the execution to 'running'
|
||||
this.core.setAttribute(this.activeNode, 'status', 'running');
|
||||
this.logger.info('Setting all jobs status to "pending"');
|
||||
this.logger.debug(`Making a commit from ${this.currentHash}`);
|
||||
return this.save(`Initializing ${this.pipelineName} for execution`);
|
||||
};
|
||||
|
||||
//////////////////////////// Operation Preparation/Execution ////////////////////////////
|
||||
ExecutePipeline.prototype.buildCache = function (nodes) {
|
||||
// Cache all nodes
|
||||
// Do I need to cache the data inputs? TODO
|
||||
// Probably not - I should be able to look them up as needed
|
||||
nodes.forEach(node => this.nodes[this.core.getPath(node)] = node);
|
||||
};
|
||||
|
||||
@@ -149,9 +215,7 @@ define([
|
||||
this.completedCount = 0;
|
||||
|
||||
// Get all connections
|
||||
conns = nodes.filter(node =>
|
||||
this.core.getPointerPath(node, 'src') && this.core.getPointerPath(node, 'dst')
|
||||
);
|
||||
conns = this.getConnections(nodes);
|
||||
|
||||
// Get all operations
|
||||
nodes
|
||||
@@ -214,14 +278,41 @@ define([
|
||||
this.executeReadyOperations();
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onOperationFail = function(node, err) {
|
||||
var job = this.core.getParent(node),
|
||||
id = this.core.getPath(node),
|
||||
name = this.core.getAttribute(node, 'name');
|
||||
|
||||
this.logger.debug(`Operation ${name} (${id}) failed: ${err}`);
|
||||
this.core.setAttribute(job, 'status', 'fail');
|
||||
this.clearOldMetadata(job);
|
||||
this.onPipelineComplete(err);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onPipelineComplete = function(err) {
|
||||
var name = this.core.getAttribute(this.activeNode, 'name');
|
||||
this.logger.debug(`Pipeline "${name}" complete!`);
|
||||
|
||||
if (err) {
|
||||
this.runningJobs--;
|
||||
}
|
||||
|
||||
this.pipelineError = this.pipelineError || err;
|
||||
|
||||
if (this.pipelineError && this.runningJobs > 0) {
|
||||
this.logger.info('Pipeline errored but is waiting for the running ' +
|
||||
'jobs to finish');
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.debug(`Pipeline "${name}" complete!`);
|
||||
this.core.setAttribute(this.activeNode, 'status',
|
||||
(!this.pipelineError ? 'success' : 'failed'));
|
||||
|
||||
this._finished = true;
|
||||
this.save('Pipeline execution finished')
|
||||
.then(() => {
|
||||
this.result.setSuccess(!err);
|
||||
this._callback(err || null, this.result);
|
||||
this.result.setSuccess(!this.pipelineError);
|
||||
this._callback(this.pipelineError || null, this.result);
|
||||
})
|
||||
.fail(e => this.logger.error(e));
|
||||
};
|
||||
@@ -232,258 +323,84 @@ define([
|
||||
readyOps = operations.filter(name => this.incomingCounts[name] === 0);
|
||||
|
||||
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.runningJobs === 0) {
|
||||
this.onPipelineComplete();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Execute all ready operations
|
||||
readyOps.forEach(jobId => {
|
||||
delete this.incomingCounts[jobId];
|
||||
this.executeOperation(jobId);
|
||||
});
|
||||
this.logger.info(`Found ${readyOps.length} ready job(s)`);
|
||||
readyOps.reduce((prev, jobId) => {
|
||||
return prev.then(() => this.executeJob(this.nodes[jobId]));
|
||||
}, Q());
|
||||
this.runningJobs += readyOps.length;
|
||||
this.logger.info(`There ${this.runningJobs === 1 ? 'is' : 'are'} now ${this.runningJobs} running job(s)`);
|
||||
|
||||
return readyOps.length;
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getOperation = function (jobId) {
|
||||
var node = this.nodes[jobId],
|
||||
children = this.core.getChildrenPaths(node).map(id => this.nodes[id]);
|
||||
|
||||
// Currently, jobs
|
||||
return children.find(child => this.isMetaTypeOf(child, this.META.Operation));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.executeOperation = function (jobId) {
|
||||
var node = this.getOperation(jobId),
|
||||
name = this.core.getAttribute(node, 'name'),
|
||||
localTypeId = this.getLocalOpType(node),
|
||||
artifact,
|
||||
artifactName,
|
||||
files,
|
||||
data = {},
|
||||
inputs;
|
||||
|
||||
// Execute any special operation types here - not on an executor
|
||||
if (localTypeId !== null) {
|
||||
this.executeLocalOperation(localTypeId, node);
|
||||
} else {
|
||||
// Generate all execution files
|
||||
this.createOperationFiles(node).then(results => {
|
||||
files = results;
|
||||
artifactName = `${name}_${jobId.replace(/\//g, '_')}-execution-files`;
|
||||
artifact = this.blobClient.createArtifact(artifactName);
|
||||
|
||||
// Add the input assets
|
||||
// - get the metadata (name)
|
||||
// - add the given inputs
|
||||
inputs = Object.keys(files.inputAssets);
|
||||
|
||||
return Q.all(
|
||||
inputs.map(input => { // Get the metadata for each input
|
||||
var hash = files.inputAssets[input];
|
||||
|
||||
// data asset for "input"
|
||||
return this.blobClient.getMetadata(hash);
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(mds => {
|
||||
// get (input, filename) tuples
|
||||
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;
|
||||
});
|
||||
|
||||
delete files.inputAssets;
|
||||
|
||||
// Add pointer assets
|
||||
Object.keys(files.ptrAssets)
|
||||
.forEach(path => data[path] = files.ptrAssets[path]);
|
||||
|
||||
delete files.ptrAssets;
|
||||
|
||||
// Add the executor config
|
||||
return this.getOutputs(node);
|
||||
})
|
||||
.then(outputArgs => {
|
||||
var config,
|
||||
args = ['init.lua'],
|
||||
outputs;
|
||||
|
||||
outputs = outputArgs.map(pair => pair[0])
|
||||
.map(name => {
|
||||
return {
|
||||
name: name,
|
||||
resultPatterns: [`outputs/${name}`]
|
||||
};
|
||||
});
|
||||
|
||||
if (this.debug) {
|
||||
args.push('#' + Date.now());
|
||||
outputs.push({
|
||||
name: name + '-all-files',
|
||||
resultPatterns: []
|
||||
});
|
||||
}
|
||||
|
||||
config = {
|
||||
cmd: 'th',
|
||||
args: args,
|
||||
resultArtifacts: outputs
|
||||
};
|
||||
files['executor_config.json'] = JSON.stringify(config, null, 4);
|
||||
|
||||
// Save the artifact
|
||||
return artifact.addObjectHashes(data);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added ptr/input data hashes for "${artifactName}"`);
|
||||
return artifact.addFiles(files);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added execution files for "${artifactName}"`);
|
||||
return artifact.save();
|
||||
})
|
||||
.then(hash => {
|
||||
this.logger.info(`Saved execution files "${artifactName}"`);
|
||||
this.result.addArtifact(hash); // Probably only need this for debugging...
|
||||
this.executeDistOperation(jobId, node, hash);
|
||||
})
|
||||
.fail(e => {
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'fail');
|
||||
this.onPipelineComplete(`Distributed operation "${name}" failed ${e}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.executeDistOperation = function (jobId, opNode, hash) {
|
||||
var name = this.core.getAttribute(opNode, 'name'),
|
||||
opId = this.core.getPath(opNode),
|
||||
executor = new ExecutorClient({
|
||||
logger: this.logger,
|
||||
serverPort: this.gmeConfig.server.port
|
||||
});
|
||||
|
||||
this.logger.info(`Executing operation "${name}"`);
|
||||
|
||||
// Set the job status to 'running'
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'running');
|
||||
|
||||
// Run the operation on an executor
|
||||
executor.createJob({hash})
|
||||
.then(() => this.watchOperation(executor, hash, opId, jobId))
|
||||
.catch(err => this.logger.error(`Could not execute "${name}": ${err}`));
|
||||
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.watchOperation = function (executor, hash, opId, jobId) {
|
||||
var name;
|
||||
|
||||
return executor.getInfo(hash)
|
||||
.then(info => {
|
||||
if (info.status === 'CREATED' || info.status === 'RUNNING') {
|
||||
setTimeout(
|
||||
this.watchOperation.bind(this, executor, hash, opId, jobId),
|
||||
ExecutePipeline.UPDATE_INTERVAL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.status !== 'SUCCESS') {
|
||||
name = this.core.getAttribute(this.nodes[opId], 'name');
|
||||
// Download all files
|
||||
this.result.addArtifact(info.resultHashes[name + '-all-files']);
|
||||
// Set the job to failed! Store the error
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'fail');
|
||||
this.onPipelineComplete(`Operation "${opId}" failed! ${JSON.stringify(info)}`); // Failed
|
||||
} else {
|
||||
name = this.core.getAttribute(this.nodes[opId], 'name');
|
||||
if (this.debug) {
|
||||
this.result.addArtifact(info.resultHashes[name + '-all-files']);
|
||||
}
|
||||
this.onDistOperationComplete(opId, info);
|
||||
}
|
||||
})
|
||||
.catch(err => this.logger.error(`Could not get op info for ${opId}: ${err}`));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onDistOperationComplete = function (nodeId, result) {
|
||||
var node = this.nodes[nodeId],
|
||||
outputMap = {},
|
||||
outputs;
|
||||
|
||||
// Match the output names to the actual nodes
|
||||
// Create an array of [name, node]
|
||||
// For now, just match by type. Later we may use ports for input/outputs
|
||||
// Store the results in the outgoing ports
|
||||
this.getOutputs(node)
|
||||
.then(outputPorts => {
|
||||
outputs = outputPorts.map(tuple => [tuple[0], tuple[2]]);
|
||||
outputs.forEach(output => outputMap[output[0]] = output[1]);
|
||||
|
||||
// this should not be in directories -> flatten the data!
|
||||
return Q.all(outputs.map(tuple => // [ name, node ]
|
||||
this.blobClient.getArtifact(result.resultHashes[tuple[0]])
|
||||
));
|
||||
})
|
||||
.then(artifacts => {
|
||||
this.logger.info(`preparing outputs -> retrieved ${artifacts.length} objects`);
|
||||
// Create new metadata for each
|
||||
artifacts.forEach((artifact, i) => {
|
||||
var name = outputs[i][0],
|
||||
hash = artifact.descriptor.content[`outputs/${name}`].content;
|
||||
|
||||
this.core.setAttribute(outputMap[name], 'data', hash);
|
||||
});
|
||||
|
||||
return this.onOperationComplete(node);
|
||||
})
|
||||
.fail(e => this.onPipelineComplete(`Operation ${nodeId} failed: ${e}`));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onOperationComplete = function (opNode) {
|
||||
var name = this.core.getAttribute(opNode, 'name'),
|
||||
nextPortIds = this.getOperationOutputIds(opNode),
|
||||
jNode = this.core.getParent(opNode),
|
||||
resultPorts,
|
||||
jobId = this.core.getPath(jNode),
|
||||
hasReadyOps;
|
||||
|
||||
// Set the operation to 'success'!
|
||||
this.clearOldMetadata(jNode);
|
||||
this.runningJobs--;
|
||||
this.core.setAttribute(jNode, 'status', 'success');
|
||||
this.logger.info(`Setting ${jobId} status to "success"`);
|
||||
this.logger.info(`There are now ${this.runningJobs} running jobs`);
|
||||
this.logger.debug(`Making a commit from ${this.currentHash}`);
|
||||
this.save(`Operation "${name}" in ${this.pipelineName} completed successfully`)
|
||||
.then(() => {
|
||||
|
||||
// Transport the data from the outputs to any connected inputs
|
||||
// - Get all the connections from each outputId
|
||||
// - Get the corresponding dst outputs
|
||||
// - Use these new ids for checking 'hasReadyOps'
|
||||
resultPorts = nextPortIds.map(id => this.inputPortsFor[id])
|
||||
.reduce((l1, l2) => l1.concat(l2), []);
|
||||
// Transport the data from the outputs to any connected inputs
|
||||
// - Get all the connections from each outputId
|
||||
// - Get the corresponding dst outputs
|
||||
// - Use these new ids for checking 'hasReadyOps'
|
||||
resultPorts = nextPortIds.map(id => this.inputPortsFor[id])
|
||||
.reduce((l1, l2) => l1.concat(l2), []);
|
||||
|
||||
resultPorts.map((id, i) => [this.nodes[id], this.nodes[nextPortIds[i]]])
|
||||
.forEach(pair => { // [ resultPort, nextPort ]
|
||||
var result = pair[0],
|
||||
next = pair[1],
|
||||
hash = this.core.getAttribute(result, 'data');
|
||||
|
||||
this.logger.info(`forwarding data (${hash}) from ${this.core.getPath(result)} ` +
|
||||
`to ${this.core.getPath(next)}`);
|
||||
this.core.setAttribute(next, 'data', hash);
|
||||
resultPorts
|
||||
.map((id, i) => [this.nodes[id], this.nodes[nextPortIds[i]]])
|
||||
.forEach(pair => { // [ resultPort, nextPort ]
|
||||
var result = pair[0],
|
||||
next = pair[1],
|
||||
hash = this.core.getAttribute(result, 'data');
|
||||
|
||||
this.logger.info(`forwarding data (${hash}) from ${this.core.getPath(result)} ` +
|
||||
`to ${this.core.getPath(next)}`);
|
||||
this.core.setAttribute(next, 'data', hash);
|
||||
this.logger.info(`Setting ${jobId} data to ${hash}`);
|
||||
});
|
||||
|
||||
// For all the nextPortIds, decrement the corresponding operation's incoming counts
|
||||
hasReadyOps = nextPortIds.map(id => this.getSiblingIdContaining(id))
|
||||
.reduce((l1, l2) => l1.concat(l2), [])
|
||||
|
||||
// decrement the incoming counts for each operation id
|
||||
.map(opId => --this.incomingCounts[opId])
|
||||
.indexOf(0) > -1;
|
||||
|
||||
this.completedCount++;
|
||||
this.logger.debug(`Operation "${name}" completed. ` +
|
||||
`${this.totalCount - this.completedCount} remaining.`);
|
||||
if (hasReadyOps) {
|
||||
this.executeReadyOperations();
|
||||
} else if (this.completedCount === this.totalCount) {
|
||||
this.onPipelineComplete();
|
||||
}
|
||||
});
|
||||
|
||||
// For all the nextPortIds, decrement the corresponding operation's incoming counts
|
||||
hasReadyOps = resultPorts.map(id => this.opFor[id])
|
||||
.reduce((l1, l2) => l1.concat(l2), [])
|
||||
|
||||
// decrement the incoming counts for each operation id
|
||||
.map(opId => --this.incomingCounts[opId])
|
||||
.indexOf(0) > -1;
|
||||
|
||||
this.completedCount++;
|
||||
this.logger.info(`Operation "${name}" completed. ` +
|
||||
`${this.totalCount - this.completedCount} remaining.`);
|
||||
if (hasReadyOps) {
|
||||
this.executeReadyOperations();
|
||||
} else if (this.completedCount === this.totalCount) {
|
||||
this.onPipelineComplete();
|
||||
}
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getOperationOutputIds = function(node) {
|
||||
@@ -496,256 +413,5 @@ define([
|
||||
return this.getOperationOutputIds(node).map(id => this.nodes[id]);
|
||||
};
|
||||
|
||||
//////////////////////////// Operation File/Dir Creators ////////////////////////////
|
||||
ExecutePipeline.prototype.createOperationFiles = function (node) {
|
||||
var files = {};
|
||||
// For each operation, generate the output files:
|
||||
// inputs/<arg-name>/init.lua (respective data deserializer)
|
||||
// pointers/<name>/init.lua (result of running the main plugin on pointer target - may need a rename)
|
||||
// outputs/<name>/ (make dirs for each of the outputs)
|
||||
// outputs/init.lua (serializers for data outputs)
|
||||
//
|
||||
// attributes.lua (returns lua table of operation attributes)
|
||||
// init.lua (main file -> calls main and serializes outputs)
|
||||
// <name>.lua (entry point -> calls main operation code)
|
||||
|
||||
// add the given files
|
||||
return this.createEntryFile(node, files)
|
||||
.then(() => this.createInputs(node, files))
|
||||
.then(() => this.createOutputs(node, files))
|
||||
.then(() => this.createMainFile(node, files))
|
||||
.then(() => {
|
||||
this.createAttributeFile(node, files);
|
||||
return Q.ninvoke(this, 'createPointers', node, files);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createInputs = function (node, files) {
|
||||
var tplContents;
|
||||
return this.getInputs(node)
|
||||
.then(inputs => {
|
||||
// For each input, match the connection with the input name
|
||||
// [ name, type ] => [ name, type, node ]
|
||||
if (inputs.length > 1) {
|
||||
this.logger.warn('multiple inputs not yet fully supported!');
|
||||
}
|
||||
|
||||
// For each input,
|
||||
// - create the deserializer
|
||||
// - put it in inputs/<name>/init.lua
|
||||
// - copy the data asset to /inputs/<name>/init.lua
|
||||
files.inputAssets = {}; // data assets
|
||||
tplContents = inputs.map(pair => {
|
||||
var name = pair[0],
|
||||
node = pair[2];
|
||||
|
||||
return {
|
||||
name: name,
|
||||
code: this.core.getAttribute(node, 'deserialize')
|
||||
};
|
||||
});
|
||||
var hashes = inputs.map(pair =>
|
||||
// storing the hash for now...
|
||||
files.inputAssets[pair[0]] = this.core.getAttribute(pair[2], 'data')
|
||||
);
|
||||
return Q.all(hashes.map(h => this.blobClient.getMetadata(h)));
|
||||
})
|
||||
.then(metadatas => {
|
||||
// Create the deserializer
|
||||
tplContents.forEach((ctnt, i) => {
|
||||
// Get the name of the given asset
|
||||
ctnt.filename = metadatas[i].name;
|
||||
files['inputs/' + ctnt.name + '/init.lua'] = _.template(Templates.DESERIALIZE)(ctnt);
|
||||
});
|
||||
return files;
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createPointers = function (node, files, cb) {
|
||||
var pointers = this.core.getPointerNames(node).filter(name => name !== 'base'),
|
||||
nIds = pointers.map(p => this.core.getPointerPath(node, p));
|
||||
|
||||
files.ptrAssets = {};
|
||||
Q.all(
|
||||
nIds.map(nId => this.core.loadByPath(this.rootNode, nId))
|
||||
)
|
||||
.then(nodes => {
|
||||
|
||||
var executePlugin = (pluginId, config, callback) => {
|
||||
// Call the Interpreter manager in a Q.ninvoke friendly way
|
||||
// I need to create a custom context for the given plugin:
|
||||
// - Set the activeNode to the given referenced node
|
||||
// - If the activeNode is namespaced, set META to the given namespace
|
||||
//
|
||||
// FIXME: Check if it is running in the browser or on the server
|
||||
WebGMEGlobal.Client.runBrowserPlugin(pluginId, config, (err, result) => {
|
||||
if (!result.success) {
|
||||
return callback(result.getError());
|
||||
}
|
||||
this.logger.info('Finished calling ' + pluginId);
|
||||
callback(null, result.artifacts);
|
||||
});
|
||||
};
|
||||
|
||||
return Q.all(
|
||||
nodes.map(ptrNode => {
|
||||
// Look up the plugin to use
|
||||
var metanode = this.core.getMetaType(ptrNode),
|
||||
pluginId;
|
||||
|
||||
pluginId = this.core.getRegistry(ptrNode, 'validPlugins').split(' ').shift();
|
||||
this.logger.info(`generating code for ${this.core.getAttribute(ptrNode, 'name')} using ${pluginId}`);
|
||||
|
||||
var context = WebGMEGlobal.Client.getCurrentPluginContext(pluginId);
|
||||
|
||||
context.managerConfig.namespace = this.core.getNamespace(metanode);
|
||||
context.managerConfig.activeNode = this.core.getPath(ptrNode);
|
||||
|
||||
// Load and run the plugin
|
||||
return Q.nfcall(executePlugin, pluginId, context);
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(resultHashes => {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
this.logger.info(`Pointer generation for ${name} FINISHED!`);
|
||||
resultHashes.forEach((hashes, index) => {
|
||||
// Grab the first asset for now
|
||||
// FIXME
|
||||
files.ptrAssets[`pointers/${pointers[index]}/init.lua`] = hashes[0];
|
||||
});
|
||||
return cb(null, files);
|
||||
})
|
||||
.fail(e => {
|
||||
this.logger.error(`Could not generate pointer files for ${this.core.getAttribute(node, 'name')}: ${JSON.stringify(e)}`);
|
||||
return cb(e);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createOutputs = function (node, files) {
|
||||
// For each of the output types, grab their serialization functions and
|
||||
// create the `outputs/init.lua` file
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var outputTypes = outputs
|
||||
// Get the serialize functions for each
|
||||
.map(tuple => [tuple[1], this.core.getAttribute(tuple[2], 'serialize')]);
|
||||
|
||||
// Remove duplicates
|
||||
// TODO
|
||||
|
||||
files['outputs/init.lua'] = _.template(Templates.SERIALIZE)({types: outputTypes});
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getOutputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Outputs);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getInputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Inputs);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getOperationData = function (node, metaType) {
|
||||
// Load the children and the output's children
|
||||
return this.core.loadChildren(node)
|
||||
.then(containers => {
|
||||
var outputs = containers.find(c => this.core.isTypeOf(c, metaType));
|
||||
return outputs ? this.core.loadChildren(outputs) : [];
|
||||
})
|
||||
.then(outputs => {
|
||||
var bases = outputs.map(node => this.core.getMetaType(node));
|
||||
// return [[arg1, Type1, node1], [arg2, Type2, node2]]
|
||||
return outputs.map((node, i) => [
|
||||
this.core.getAttribute(node, 'name'),
|
||||
this.core.getAttribute(bases[i], 'name'),
|
||||
node
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createEntryFile = function (node, files) {
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
content = {};
|
||||
|
||||
// sort the outputs by the return values?
|
||||
if (outputs.length > 1) {
|
||||
this.logger.error('Multiple outputs not yet supported!');
|
||||
}
|
||||
|
||||
// inputs and outputs
|
||||
content.name = name;
|
||||
content.outputs = outputs;
|
||||
|
||||
files['init.lua'] = _.template(Templates.ENTRY)(content);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createMainFile = function (node, files) {
|
||||
return this.getInputs(node)
|
||||
.then(inputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
code = this.core.getAttribute(node, 'code'),
|
||||
pointers = this.core.getPointerNames(node).filter(ptr => ptr !== 'base'),
|
||||
content = {
|
||||
name: name
|
||||
};
|
||||
|
||||
// Get input data arguments
|
||||
content.inputs = inputs;
|
||||
|
||||
// Defined variables for each pointers
|
||||
content.pointers = pointers;
|
||||
|
||||
// Add remaining code
|
||||
content.code = code;
|
||||
|
||||
files['main.lua'] = _.template(Templates.MAIN)(content);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createAttributeFile = function (node, files) {
|
||||
var skip = ['outputs', 'inputs'],
|
||||
table;
|
||||
|
||||
table = '{\n\t' + this.core.getAttributeNames(node)
|
||||
.filter(attr => skip.indexOf(attr) === -1)
|
||||
.map(name => [name, JSON.stringify(this.core.getAttribute(node, name))])
|
||||
.map(pair => pair.join(' = '))
|
||||
.join(',\n\t') + '\n}';
|
||||
|
||||
files['attributes.lua'] = `-- attributes of ${this.core.getAttribute(node, 'name')}\nreturn ${table}`;
|
||||
};
|
||||
|
||||
//////////////////////////// Special Operations ////////////////////////////
|
||||
ExecutePipeline.prototype.getLocalOpType = function (node) {
|
||||
var type;
|
||||
for (var i = LocalExecutor.TYPES.length; i--;) {
|
||||
type = LocalExecutor.TYPES[i];
|
||||
if (!this.META[type]) {
|
||||
this.logger.warn(`Missing local operation: ${type}`);
|
||||
continue;
|
||||
}
|
||||
if (this.isMetaTypeOf(node, this.META[type])) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.executeLocalOperation = function (type, node) {
|
||||
// Retrieve the given LOCAL_OP type
|
||||
if (!this[type]) {
|
||||
this.logger.error(`No local operation handler for ${type}`);
|
||||
}
|
||||
this.logger.info(`Running local operation ${type}`);
|
||||
|
||||
return this[type](node);
|
||||
};
|
||||
|
||||
_.extend(ExecutePipeline.prototype, LocalExecutor.prototype);
|
||||
|
||||
return ExecutePipeline;
|
||||
});
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
{
|
||||
"id": "ExecutePipeline",
|
||||
"name": "ExecutePipeline",
|
||||
"name": "Execute Pipeline",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"class": "glyphicon glyphicon-random",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"disableBrowserSideExecution": true,
|
||||
"configStructure": [
|
||||
{
|
||||
"name": "debug",
|
||||
"displayName": "Debug Mode",
|
||||
"description": "Download all files for each operation",
|
||||
"value": false,
|
||||
"valueType": "boolean",
|
||||
"readOnly": false
|
||||
}
|
||||
{
|
||||
"name": "snapshot",
|
||||
"displayName": "Snapshot",
|
||||
"description": "Freeze the operation definitions and attributes at current value",
|
||||
"value": true,
|
||||
"valueType": "boolean",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"displayName": "Debug Mode",
|
||||
"description": "Download all files for each operation",
|
||||
"value": false,
|
||||
"valueType": "boolean",
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
-- input data
|
||||
<% inputs.forEach(function(pair) { var input = pair[0]%>
|
||||
<%= input %> = require './inputs/<%= input %>'
|
||||
<% }); %>
|
||||
|
||||
-- load references
|
||||
<% pointers.forEach(function(pointer) { %><%= pointer %> = require './pointers/<%= pointer %>'
|
||||
<% }); %>
|
||||
attributes = require './attributes'
|
||||
|
||||
-- main operation code for <%= name %>
|
||||
<%= code %>
|
||||
@@ -29,6 +29,7 @@ define([
|
||||
* @classdesc This class represents the plugin GenerateArchitecture.
|
||||
* @constructor
|
||||
*/
|
||||
var INDEX = '__index__';
|
||||
var GenerateArchitecture = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
@@ -42,57 +43,166 @@ define([
|
||||
GenerateArchitecture.prototype.constructor = GenerateArchitecture;
|
||||
|
||||
GenerateArchitecture.prototype.main = function () {
|
||||
this.addCustomLayersToMeta();
|
||||
this.LayerDict = createLayerDict(this.core, this.META);
|
||||
this.uniqueId = 2;
|
||||
this._oldTemplateSettings = _.templateSettings;
|
||||
return PluginBase.prototype.main.apply(this, arguments);
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.addCustomLayersToMeta = function () {
|
||||
var metaDict = this.core.getAllMetaNodes(this.rootNode);
|
||||
|
||||
Object.keys(metaDict).map(id => metaDict[id])
|
||||
// Get all custom layers
|
||||
.filter(node => this.core.isTypeOf(node, this.META.Layer))
|
||||
// Add them to the meta
|
||||
.forEach(node => this.META[this.core.getAttribute(node, 'name')] = node);
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createOutputFiles = function (tree) {
|
||||
var layers = tree[Constants.CHILDREN],
|
||||
//initialLayers,
|
||||
result = {},
|
||||
template,
|
||||
snippet,
|
||||
code,
|
||||
args;
|
||||
code = 'require \'nn\'\n';
|
||||
|
||||
code = [
|
||||
'require \'nn\'',
|
||||
'',
|
||||
'local net = nn.Sequential()'
|
||||
].join('\n');
|
||||
//initialLayers = layers.filter(layer => layer[Constants.PREV].length === 0);
|
||||
// Add an index to each layer
|
||||
layers.forEach((l, index) => l[INDEX] = index);
|
||||
|
||||
// Start with sequential (just one input)
|
||||
for (var i = 0; i < layers.length; i++) {
|
||||
if (layers[i][Constants.NEXT].length > 1) {
|
||||
// no support for
|
||||
this.logger.error('No support for parallel layers... yet');
|
||||
break;
|
||||
} else {
|
||||
// args
|
||||
args = this.createArgString(layers[i]);
|
||||
template = _.template('net:add(nn.{{= name }}' + args + ')');
|
||||
snippet = template(layers[i]);
|
||||
code += '\n' + snippet;
|
||||
}
|
||||
// Define custom layers
|
||||
if (this.getCurrentConfig().standalone) {
|
||||
code += this.genLayerDefinitions(layers);
|
||||
}
|
||||
|
||||
code += '\n\nreturn net';
|
||||
code += this.genArchCode(layers);
|
||||
|
||||
result[tree.name + '.lua'] = code;
|
||||
_.templateSettings = this._oldTemplateSettings; // FIXME: Fix this in SimpleNodes
|
||||
return result;
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createArgString = function (layer) {
|
||||
return '(' + this.LayerDict[layer.name].map(arg => {
|
||||
var value = layer[arg.name];
|
||||
// Infer if value is unset and infer.dimensionality is set
|
||||
if (!value && arg.infer === 'dimensionality') {
|
||||
value = dimensionality(layer[Constants.PREV][0]);
|
||||
}
|
||||
return value;
|
||||
}).join(', ') + ')';
|
||||
GenerateArchitecture.prototype.genArchCode = function (layers) {
|
||||
return [
|
||||
this.createSequential(layers[0], 'net').code,
|
||||
'\nreturn net'
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createSequential = function (layer, name) {
|
||||
var next = layer[Constants.NEXT][0],
|
||||
args,
|
||||
template,
|
||||
snippet,
|
||||
snippets,
|
||||
code = `\nlocal ${name} = nn.Sequential()`,
|
||||
|
||||
group,
|
||||
i,
|
||||
result;
|
||||
|
||||
while (layer) {
|
||||
// if there is only one successor, just add the given layer
|
||||
if (layer[Constants.PREV].length > 1) { // sequential layers are over
|
||||
next = layer; // the given layer will be added by the caller
|
||||
break;
|
||||
} else { // add the given layer
|
||||
args = this.createArgString(layer);
|
||||
template = _.template(name + ':add(nn.{{= name }}' + args + ')');
|
||||
snippet = template(layer);
|
||||
code += '\n' + snippet;
|
||||
|
||||
}
|
||||
|
||||
while (layer && layer[Constants.NEXT].length > 1) { // concat/parallel
|
||||
// if there is a fork, recurse and add a concat layer
|
||||
|
||||
this.logger.debug(`detected fork of size ${layer[Constants.NEXT].length}`);
|
||||
snippets = layer[Constants.NEXT].map(nlayer =>
|
||||
this.createSequential(nlayer, 'net_'+(this.uniqueId++)));
|
||||
code += '\n' + snippets.map(snippet => snippet.code).join('\n');
|
||||
|
||||
// Make sure all snippets end at the same concat node
|
||||
|
||||
// Until all snippets end at the same concat node
|
||||
snippets.sort((a, b) => a.endIndex < b.endIndex ? -1 : 1);
|
||||
group = [];
|
||||
while (snippets.length > 0) {
|
||||
// Add snippets to the group
|
||||
i = 0;
|
||||
while (i < snippets.length &&
|
||||
snippets[0].endIndex === snippets[i].endIndex) {
|
||||
|
||||
group.push(snippets[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Add concat layer
|
||||
layer = group[0].next;
|
||||
if (layer) {
|
||||
args = this.createArgString(layer);
|
||||
code += `\n\nlocal concat_${layer[INDEX]} = nn.Concat${args}\n` +
|
||||
group.map(snippet =>
|
||||
`concat_${layer[INDEX]}:add(${snippet.name})`)
|
||||
.join('\n') + `\n\n${name}:add(concat_${layer[INDEX]})`;
|
||||
|
||||
next = layer[Constants.NEXT][0];
|
||||
} else {
|
||||
next = null; // no next layers
|
||||
}
|
||||
|
||||
// Remove the updated snippets
|
||||
this.logger.debug('removing ' + i + ' snippet(s)');
|
||||
snippets.splice(0, i);
|
||||
|
||||
// merge the elements in the group
|
||||
if (snippets.length) { // prepare next iteration
|
||||
result = this.createSequential(next, 'net_'+(this.uniqueId++));
|
||||
code += result.code;
|
||||
group = [result];
|
||||
this.logger.debug('updating group ('+ snippets.length+ ' left)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer = next;
|
||||
next = layer && layer[Constants.NEXT][0];
|
||||
}
|
||||
|
||||
return {
|
||||
code: code,
|
||||
name: name,
|
||||
endIndex: next ? next[INDEX] : Infinity,
|
||||
next: next
|
||||
};
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createArgString = function (layer) {
|
||||
return '(' + this.LayerDict[layer.name]
|
||||
.map(arg => layer[arg.name])
|
||||
.filter(GenerateArchitecture.isSet)
|
||||
.join(', ') + ')';
|
||||
};
|
||||
|
||||
GenerateArchitecture.isSet = function (value) {
|
||||
return !(value === undefined || value === null || value === '');
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.genLayerDefinitions = function(layers) {
|
||||
var code = '',
|
||||
customLayerId = this.core.getPath(this.META.CustomLayer),
|
||||
customLayers = layers.filter(layer => { // Get the custom layers
|
||||
var node = this.META[layer.name];
|
||||
return this.core.getMixinPaths(node).indexOf(customLayerId) !== -1;
|
||||
});
|
||||
|
||||
if (customLayers.length) {
|
||||
code += '\n-------------- Custom Layer Definitions --------------\n\n';
|
||||
code += customLayers.map(layer => layer.code).join('\n');
|
||||
code += '\n\n-------------- Network --------------\n';
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
return GenerateArchitecture;
|
||||
});
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
{
|
||||
"id": "GenerateArchitecture",
|
||||
"name": "Generate Architecture",
|
||||
"name": "Generate Torch Code",
|
||||
"version": "0.1.0",
|
||||
"description": "Generate torch architecture code",
|
||||
"icon": {
|
||||
"src": "",
|
||||
"class": "glyphicon glyphicon-ok-circle"
|
||||
"class": "glyphicon glyphicon-file"
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"configStructure": []
|
||||
"configStructure": [
|
||||
{
|
||||
"name": "standalone",
|
||||
"displayName": "Standalone",
|
||||
"description": "Prepend custom layer definitions",
|
||||
"value": false,
|
||||
"valueType": "boolean",
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'plugin/PluginBase'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
PluginBase
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
|
||||
/**
|
||||
* Initializes a new instance of GenerateCriterion.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin GenerateCriterion.
|
||||
* @constructor
|
||||
*/
|
||||
var GenerateCriterion = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
GenerateCriterion.metadata = pluginMetadata;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
GenerateCriterion.prototype = Object.create(PluginBase.prototype);
|
||||
GenerateCriterion.prototype.constructor = GenerateCriterion;
|
||||
|
||||
/**
|
||||
* Main function for the plugin to execute. This will perform the execution.
|
||||
* Notes:
|
||||
* - Always log with the provided logger.[error,warning,info,debug].
|
||||
* - Do NOT put any user interaction logic UI, etc. inside this method.
|
||||
* - callback always has to be called even if error happened.
|
||||
*
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
GenerateCriterion.prototype.main = function (callback) {
|
||||
// Generate the code for the criterion layer and return a file
|
||||
var name = this.core.getAttribute(this.activeNode, 'name'),
|
||||
code = `require 'nn'\nreturn nn.${name}()`,
|
||||
filename = `${name}.lua`;
|
||||
|
||||
// Using the logger.
|
||||
this.logger.debug(`Generating code for ${name} criterion layer.`);
|
||||
|
||||
// Save the file
|
||||
this.blobClient.putFile(filename, code)
|
||||
.then(hash => {
|
||||
this.result.setSuccess(true);
|
||||
this.result.addArtifact(hash);
|
||||
callback(null, this.result);
|
||||
})
|
||||
.catch(err => callback(err, this.result));
|
||||
|
||||
};
|
||||
|
||||
return GenerateCriterion;
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "GenerateCriterion",
|
||||
"name": "Generate Criterion Code",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": []
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
/*globals define, _*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
/**
|
||||
* Generated by PluginGenerator 1.7.0 from webgme on Sat Jun 04 2016 18:01:54 GMT-0500 (CDT).
|
||||
* A plugin that inherits from the PluginBase. To see source code documentation about available
|
||||
* properties and methods visit %host%/docs/source/PluginBase.html.
|
||||
*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'plugin/PluginBase',
|
||||
'deepforge/plugin/PtrCodeGen',
|
||||
'q'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
PluginBase,
|
||||
PtrCodeGen,
|
||||
Q
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
var HEADER_LENGTH = 60;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of GenerateExecFile.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin GenerateExecFile.
|
||||
* @constructor
|
||||
*/
|
||||
var GenerateExecFile = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
|
||||
this._srcIdFor = {}; // input path -> output data node path
|
||||
|
||||
this._nameFor = {}; // input path -> opname
|
||||
this._dataNameFor = {};
|
||||
this._opNames = {};
|
||||
|
||||
// topo sort stuff
|
||||
this._nextOps = {};
|
||||
this._incomingCnts = {};
|
||||
|
||||
this._operations = {};
|
||||
this.activeNodeId = null;
|
||||
this.activeNodeDepth = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
GenerateExecFile.metadata = pluginMetadata;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
GenerateExecFile.prototype = Object.create(PluginBase.prototype);
|
||||
GenerateExecFile.prototype.constructor = GenerateExecFile;
|
||||
|
||||
/**
|
||||
* Main function for the plugin to execute. This will perform the execution.
|
||||
* Notes:
|
||||
* - Always log with the provided logger.[error,warning,info,debug].
|
||||
* - Do NOT put any user interaction logic UI, etc. inside this method.
|
||||
* - callback always has to be called even if error happened.
|
||||
*
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
GenerateExecFile.prototype.main = function (callback) {
|
||||
// Get all the children and call generate exec file
|
||||
this.activeNodeId = this.core.getPath(this.activeNode);
|
||||
this.activeNodeDepth = this.activeNodeId.split('/').length + 1;
|
||||
|
||||
if (this.isMetaTypeOf(this.activeNode, this.META.Execution)) {
|
||||
this.activeNodeDepth++;
|
||||
}
|
||||
|
||||
return this.core.loadChildren(this.activeNode)
|
||||
.then(nodes => this.createExecFile(nodes))
|
||||
.then(code => this.blobClient.putFile('init.lua', code))
|
||||
.then(hash => {
|
||||
this.result.addArtifact(hash);
|
||||
this.result.setSuccess(true);
|
||||
callback(null, this.result);
|
||||
})
|
||||
.fail(err => callback(err));
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.createExecFile = function (children) {
|
||||
// Convert opNodes' jobs to the nested operations
|
||||
var opNodes,
|
||||
nodes;
|
||||
|
||||
return this.unpackJobs(children)
|
||||
.then(_nodes => {
|
||||
nodes = _nodes;
|
||||
opNodes = nodes
|
||||
.filter(node => this.isMetaTypeOf(node, this.META.Operation));
|
||||
return Q.all(nodes.map(node => this.registerNameAndData(node)));
|
||||
})
|
||||
.then(() => Q.all(opNodes.map(node => this.createOperation(node))))
|
||||
.then(operations => {
|
||||
var nextIds = opNodes.map(n => this.core.getPath(n))
|
||||
.filter(id => !this._incomingCnts[id]);
|
||||
|
||||
operations.forEach(op => this._operations[op.id] = op);
|
||||
|
||||
// Toposort and concat!
|
||||
return this.combineOpNodes(nextIds);
|
||||
})
|
||||
.fail(err => this.logger.error(err));
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.unpackJobs = function (nodes) {
|
||||
return Q.all(
|
||||
nodes.map(node => {
|
||||
if (!this.isMetaTypeOf(node, this.META.Job)) {
|
||||
return node;
|
||||
}
|
||||
return this.core.loadChildren(node)
|
||||
.then(children =>
|
||||
children.find(c => this.isMetaTypeOf(c, this.META.Operation))
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.combineOpNodes = function (opIds) {
|
||||
var nextIds = [],
|
||||
dstIds,
|
||||
code,
|
||||
id;
|
||||
|
||||
// Combine all nodes with incoming cnts of 0
|
||||
code = opIds.map(id => this._operations[id].code).join('\n');
|
||||
|
||||
// Decrement all next ops
|
||||
dstIds = opIds.map(id => this._nextOps[id])
|
||||
.reduce((l1, l2) => l1.concat(l2), []);
|
||||
for (var i = dstIds.length; i--;) {
|
||||
id = dstIds[i];
|
||||
if (--this._incomingCnts[id] === 0) {
|
||||
nextIds.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
// append
|
||||
return [
|
||||
code,
|
||||
nextIds.length ? this.combineOpNodes(nextIds) : ''
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.registerNameAndData = function (node) {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
id = this.core.getPath(node),
|
||||
basename = name,
|
||||
i = 2;
|
||||
|
||||
if (this.isMetaTypeOf(node, this.META.Operation)) {
|
||||
|
||||
// Get a unique operation name
|
||||
while (this._opNames[name]) {
|
||||
name = basename + '_' + i;
|
||||
i++;
|
||||
}
|
||||
|
||||
// register the unique name
|
||||
this._opNames[name] = true;
|
||||
this._nameFor[id] = name;
|
||||
|
||||
// For operations, register all output data node names by path
|
||||
return this.core.loadChildren(node)
|
||||
.then(cntrs => {
|
||||
var cntr = cntrs.find(n => this.isMetaTypeOf(n, this.META.Outputs));
|
||||
return this.core.loadChildren(cntr);
|
||||
})
|
||||
.then(outputs => {
|
||||
outputs.forEach(output => {
|
||||
var dataId = this.core.getPath(output);
|
||||
|
||||
name = this.core.getAttribute(output, 'name');
|
||||
this._dataNameFor[dataId] = name;
|
||||
});
|
||||
});
|
||||
|
||||
// For each input data node, register the associated output id
|
||||
} else if (this.isMetaTypeOf(node, this.META.Transporter)) {
|
||||
var outputData = this.core.getPointerPath(node, 'src'),
|
||||
inputData = this.core.getPointerPath(node, 'dst'),
|
||||
srcOpId = this.getOpIdFor(outputData),
|
||||
dstOpId = this.getOpIdFor(inputData);
|
||||
|
||||
this._srcIdFor[inputData] = outputData;
|
||||
|
||||
// Store the next operation ids for the op id
|
||||
if (!this._nextOps[srcOpId]) {
|
||||
this._nextOps[srcOpId] = [];
|
||||
}
|
||||
this._nextOps[srcOpId].push(dstOpId);
|
||||
|
||||
// Increment the incoming counts for each dst op
|
||||
this._incomingCnts[dstOpId] = this._incomingCnts[dstOpId] || 0;
|
||||
this._incomingCnts[dstOpId]++;
|
||||
}
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.getOpIdFor = function (dataId) {
|
||||
var ids = dataId.split('/'),
|
||||
depth = ids.length;
|
||||
|
||||
ids.splice(this.activeNodeDepth - depth);
|
||||
return ids.join('/');
|
||||
};
|
||||
|
||||
// For each operation...
|
||||
// - unpack the inputs from prev ops
|
||||
// - add the attributes table (if used)
|
||||
// - check for '\<attributes\>' in code
|
||||
// - add the references
|
||||
// - generate the code
|
||||
// - replace the `return <thing>` w/ `<ref-name> = <thing>`
|
||||
GenerateExecFile.prototype.createOperation = function (node) {
|
||||
var id = this.core.getPath(node),
|
||||
operation = {};
|
||||
|
||||
operation.name = this._nameFor[id];
|
||||
operation.id = id;
|
||||
operation.code = this.core.getAttribute(node, 'code');
|
||||
|
||||
// Update the 'code' attribute
|
||||
// Change the last return statement to assign the results to a table
|
||||
operation.code = this.assignResultToVar(operation.code,
|
||||
`${operation.name}_results`);
|
||||
|
||||
// Get all the input names (and sources)
|
||||
return this.core.loadChildren(node)
|
||||
.then(containers => {
|
||||
var inputs;
|
||||
|
||||
inputs = containers
|
||||
.find(cntr => this.isMetaTypeOf(cntr, this.META.Inputs));
|
||||
|
||||
this.logger.info(`${name} has ${containers.length} cntrs`);
|
||||
return this.core.loadChildren(inputs);
|
||||
})
|
||||
.then(data => {
|
||||
// Get the input names and sources
|
||||
var inputNames = data.map(d => this.core.getAttribute(d, 'name')),
|
||||
ids = data.map(d => this.core.getPath(d)),
|
||||
srcIds = ids.map(id => this._srcIdFor[id]);
|
||||
|
||||
operation.inputs = inputNames.map((name, i) => {
|
||||
var id = srcIds[i],
|
||||
srcDataName = this._dataNameFor[id],
|
||||
srcOpId = this.getOpIdFor(id),
|
||||
srcOpName = this._nameFor[srcOpId];
|
||||
|
||||
return `local ${name} = ${srcOpName}_results.${srcDataName}`;
|
||||
});
|
||||
|
||||
return operation;
|
||||
|
||||
})
|
||||
.then(operation => {
|
||||
|
||||
// For each reference, run the plugin and retrieve the generated code
|
||||
operation.refNames = this.core.getPointerNames(node)
|
||||
.filter(name => name !== 'base');
|
||||
|
||||
var refs = operation.refNames
|
||||
.map(ref => [ref, this.core.getPointerPath(node, ref)]);
|
||||
|
||||
return Q.all(
|
||||
refs.map(pair => this.genPtrSnippet.apply(this, pair))
|
||||
);
|
||||
})
|
||||
.then(codeFiles => {
|
||||
operation.refs = codeFiles;
|
||||
this.genOperationCode(operation);
|
||||
return operation;
|
||||
});
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.genPtrSnippet = function (ptrName, pId) {
|
||||
return this.getPtrCodeHash(pId)
|
||||
.then(hash => this.blobClient.getObjectAsString(hash))
|
||||
.then(code => this.createHeader(`creating ${ptrName}`, 40) + '\n' +
|
||||
this.assignResultToVar(code, ptrName));
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.createHeader = function (title, length) {
|
||||
var len;
|
||||
title = ` ${title} `;
|
||||
length = length || HEADER_LENGTH;
|
||||
|
||||
len = Math.max(
|
||||
Math.floor((length - title.length)/2),
|
||||
2
|
||||
);
|
||||
|
||||
return [
|
||||
'',
|
||||
title,
|
||||
''
|
||||
].join(new Array(len+1).join('-')) + '\n';
|
||||
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.genOperationCode = function (operation) {
|
||||
var header = this.createHeader(`"${operation.name}" Operation`),
|
||||
codeParts = [];
|
||||
|
||||
codeParts.push(header);
|
||||
|
||||
if (operation.inputs.length) {
|
||||
codeParts.push(operation.inputs.join('\n'));
|
||||
}
|
||||
|
||||
if (operation.refs.length) {
|
||||
codeParts.push(operation.refs.join('\n'));
|
||||
}
|
||||
|
||||
codeParts.push(operation.code);
|
||||
codeParts.push('');
|
||||
operation.code = codeParts.join('\n');
|
||||
return operation;
|
||||
};
|
||||
|
||||
GenerateExecFile.prototype.assignResultToVar = function (code, name) {
|
||||
var i = code.lastIndexOf('return');
|
||||
return code.substring(0, i) +
|
||||
code.substring(i)
|
||||
.replace('return', `local ${name} = `);
|
||||
};
|
||||
|
||||
_.extend(GenerateExecFile.prototype, PtrCodeGen.prototype);
|
||||
|
||||
return GenerateExecFile;
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "GenerateExecFile",
|
||||
"name": "Generate Execution File",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": []
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
/**
|
||||
* Generated by PluginGenerator 1.7.0 from webgme on Tue Jun 07 2016 11:25:09 GMT-0500 (CDT).
|
||||
* A plugin that inherits from the PluginBase. To see source code documentation about available
|
||||
* properties and methods visit %host%/docs/source/PluginBase.html.
|
||||
*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'plugin/PluginBase',
|
||||
'q'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
PluginBase,
|
||||
Q
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
|
||||
/**
|
||||
* Initializes a new instance of ImportArtifact.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin ImportArtifact.
|
||||
* @constructor
|
||||
*/
|
||||
var ImportArtifact = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
ImportArtifact.metadata = pluginMetadata;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
ImportArtifact.prototype = Object.create(PluginBase.prototype);
|
||||
ImportArtifact.prototype.constructor = ImportArtifact;
|
||||
|
||||
/**
|
||||
* Main function for the plugin to execute. This will perform the execution.
|
||||
* Notes:
|
||||
* - Always log with the provided logger.[error,warning,info,debug].
|
||||
* - Do NOT put any user interaction logic UI, etc. inside this method.
|
||||
* - callback always has to be called even if error happened.
|
||||
*
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
ImportArtifact.prototype.main = function (callback) {
|
||||
var self = this,
|
||||
config = this.getCurrentConfig(),
|
||||
hash = config.dataHash,
|
||||
baseName = config.dataTypeId,
|
||||
name,
|
||||
baseType,
|
||||
dataNode,
|
||||
|
||||
metaDict,
|
||||
metanodes;
|
||||
|
||||
// Create node of type "typeId" in the activeNode and set the hash, name
|
||||
metaDict = this.core.getAllMetaNodes(this.activeNode);
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
baseType = metanodes.find(node =>
|
||||
this.core.getAttribute(node, 'name') === baseName
|
||||
);
|
||||
|
||||
if (!baseType) {
|
||||
callback(`Could not find data type "${baseName}"`, this.result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the base node
|
||||
this.getArtifactsDir()
|
||||
.then(targetDir => {
|
||||
dataNode = this.core.createNode({
|
||||
base: baseType,
|
||||
parent: targetDir
|
||||
});
|
||||
|
||||
this.core.setAttribute(dataNode, 'data', hash);
|
||||
baseName = this.core.getAttribute(baseType, 'name');
|
||||
|
||||
var getName;
|
||||
if (config.name) {
|
||||
getName = Q().then(() => config.name);
|
||||
} else {
|
||||
getName = this.blobClient.getMetadata(hash)
|
||||
.then(md => {
|
||||
name = baseName[0].toLowerCase() + baseName.substring(1);
|
||||
if (md) {
|
||||
name = md.name.replace(/\.[^\.]*?$/, '');
|
||||
}
|
||||
return name;
|
||||
});
|
||||
}
|
||||
return getName;
|
||||
})
|
||||
.then(name => this.core.setAttribute(dataNode, 'name', name))
|
||||
.then(() => this.save(`Uploaded "${name}" data`))
|
||||
.then(function () {
|
||||
self.result.setSuccess(true);
|
||||
callback(null, self.result);
|
||||
})
|
||||
.fail(function (err) {
|
||||
callback(err, self.result);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
ImportArtifact.prototype.getArtifactsDir = function() {
|
||||
// Find the artifacts dir
|
||||
return this.core.loadChildren(this.rootNode)
|
||||
.then(children => children
|
||||
.find(child => this.core.getAttribute(child, 'name') === 'MyArtifacts') ||
|
||||
this.activeNode
|
||||
);
|
||||
};
|
||||
|
||||
return ImportArtifact;
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"id": "ImportArtifact",
|
||||
"name": "Import Artifact",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cloud-upload",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"writeAccessRequired": true,
|
||||
"configStructure": [
|
||||
{
|
||||
"name": "name",
|
||||
"displayName": "Data name",
|
||||
"description": "Optional name for artifact",
|
||||
"value": "",
|
||||
"valueType": "string",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"name": "dataHash",
|
||||
"displayName": "Data to upload",
|
||||
"description": "",
|
||||
"value": "",
|
||||
"valueType": "asset",
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -57,12 +57,17 @@ define([
|
||||
|
||||
this.blobClient.getMetadata(srcHash)
|
||||
.then(mdata => { // Create the new model
|
||||
var name = mdata.name.replace('.lua', '');
|
||||
this.tgtNode = this.core.createNode({
|
||||
base: this.META.Architecture,
|
||||
parent: this.activeNode
|
||||
});
|
||||
this.core.setAttribute(this.tgtNode, 'name', name);
|
||||
// If the current node is an architecture, assume we are just extending it
|
||||
this.importedName = mdata.name.replace('.lua', '');
|
||||
if (this.isMetaTypeOf(this.activeNode, this.META.Architecture)) {
|
||||
this.tgtNode = this.activeNode;
|
||||
} else { // Create a new architecture
|
||||
this.tgtNode = this.core.createNode({
|
||||
base: this.META.Architecture,
|
||||
parent: this.activeNode
|
||||
});
|
||||
this.core.setAttribute(this.tgtNode, 'name', this.importedName);
|
||||
}
|
||||
return this.blobClient.getObjectAsString(srcHash);
|
||||
})
|
||||
.then(src => { // Retrieved the source code
|
||||
@@ -81,7 +86,10 @@ define([
|
||||
return this.save('ImportTorch updated model.');
|
||||
})
|
||||
.then(() => { // changes saved successfully
|
||||
var name = this.importedName;
|
||||
this.result.setSuccess(true);
|
||||
this.createMessage(this.tgtNode,
|
||||
`Successfully imported ${name} architecture`);
|
||||
callback(null, this.result);
|
||||
})
|
||||
.fail(err =>
|
||||
@@ -93,7 +101,7 @@ define([
|
||||
ImportTorch.prototype.loadNNMock = function () {
|
||||
// This needs a refactor...
|
||||
// createNN(this)
|
||||
var lib = createNNSearcher(this).bind(this.context);
|
||||
var lib = createNNSearcher(this, this.context).bind(this.context);
|
||||
|
||||
// Create a "searcher" to allow this 'nn' to be in the lib path
|
||||
this.context._G.get('package').set('searchers', [function(name) {
|
||||
|
||||
@@ -11,12 +11,23 @@ define([
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var createSearcher = function(plugin) {
|
||||
var createSearcher = function(plugin, context) {
|
||||
var core = plugin.core,
|
||||
META = plugin.META,
|
||||
logger = plugin.logger.fork('nn'),
|
||||
parent = plugin.tgtNode,
|
||||
LayerDict = createLayerDict(core, META);
|
||||
LayerDict = createLayerDict(core, META),
|
||||
helpers = context.__helpers,
|
||||
oldSet = helpers.__set,
|
||||
isSetting = false;
|
||||
|
||||
// Override the helper's '__set' method to detect
|
||||
// if the code is in the middle of a "set".
|
||||
helpers.__set = function() {
|
||||
isSetting = true;
|
||||
oldSet.apply(this, arguments);
|
||||
isSetting = false;
|
||||
};
|
||||
|
||||
var connect = function(src, dst) {
|
||||
var conn = core.createNode({
|
||||
@@ -145,6 +156,7 @@ define([
|
||||
var CreateLayer = function(type) {
|
||||
var res = luajs.newContext()._G,
|
||||
attrs = [].slice.call(arguments, 1),
|
||||
ltGet = luajs.types.LuaTable.prototype.get,
|
||||
node;
|
||||
|
||||
if (LAYERS[type]) {
|
||||
@@ -165,6 +177,16 @@ define([
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override get
|
||||
res.get = function noNilGet(value) {
|
||||
var result = ltGet.call(this, value);
|
||||
if (!result && !isSetting) {
|
||||
throw Error(`"${value}" is not supported for ${type}`);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
Arquivo binário não exibido.
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b3c5a08313ef9d966fde9893f89ea7b4a6f8d00f6629e1234fdca71911acfe6a
|
||||
size 455762
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0f7228f775590d69e1215d25afc299809755a6e820fbd5cfdcd1e9bb3abdaf3a
|
||||
size 23753
|
||||
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
@@ -5,24 +5,12 @@
|
||||
"panel": "panels/AutoViz/AutoVizPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "EasyDAG",
|
||||
"title": "EasyDAG",
|
||||
"panel": "panels/EasyDAG/EasyDAGPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "ArchEditor",
|
||||
"title": "ArchEditor",
|
||||
"panel": "panels/ArchEditor/ArchEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "TextEditor",
|
||||
"title": "TextEditor",
|
||||
"panel": "panels/TextEditor/TextEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "OperationEditor",
|
||||
"title": "OperationEditor",
|
||||
@@ -43,26 +31,8 @@
|
||||
},
|
||||
{
|
||||
"id": "RootViz",
|
||||
"title": "RootViz",
|
||||
"panel": "panels/RootViz/RootVizPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "TilingViz",
|
||||
"title": "TilingViz",
|
||||
"panel": "panels/TilingViz/TilingVizPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "OperationCodeEditor",
|
||||
"title": "OperationCodeEditor",
|
||||
"panel": "panels/OperationCodeEditor/OperationCodeEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "OperationInterfaceEditor",
|
||||
"title": "OperationInterfaceEditor",
|
||||
"panel": "panels/OperationInterfaceEditor/OperationInterfaceEditorPanel",
|
||||
"title": "MainView",
|
||||
"panel": "panels/MainView/MainViewPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
@@ -72,15 +42,51 @@
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "SerializeEditor",
|
||||
"title": "SerializeEditor",
|
||||
"panel": "panels/SerializeEditor/SerializeEditorPanel",
|
||||
"id": "LogViewer",
|
||||
"title": "LogViewer",
|
||||
"panel": "panels/LogViewer/LogViewerPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "DeserializeEditor",
|
||||
"title": "DeserializeEditor",
|
||||
"panel": "panels/DeserializeEditor/DeserializeEditorPanel",
|
||||
"id": "LayerEditor",
|
||||
"title": "LayerEditor",
|
||||
"panel": "panels/LayerEditor/LayerEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "ClassCodeEditor",
|
||||
"title": "ClassCodeEditor",
|
||||
"panel": "panels/ClassCodeEditor/ClassCodeEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "ClassEditor",
|
||||
"title": "ClassEditor",
|
||||
"panel": "panels/ClassEditor/ClassEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "PipelineIndex",
|
||||
"title": "PipelineIndex",
|
||||
"panel": "panels/PipelineIndex/PipelineIndexPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "JobEditor",
|
||||
"title": "JobEditor",
|
||||
"panel": "panels/JobEditor/JobEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "OutputViewer",
|
||||
"title": "OutputViewer",
|
||||
"panel": "panels/OutputViewer/OutputViewerPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "LineGraph",
|
||||
"title": "LineGraph",
|
||||
"panel": "panels/LineGraph/LineGraphPanel",
|
||||
"DEBUG_ONLY": false
|
||||
}
|
||||
]
|
||||
@@ -1,15 +1,14 @@
|
||||
/*globals define */
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Tue May 17 2016 11:25:46 GMT-0400 (EDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'deepforge/globals',
|
||||
'panels/EasyDAG/EasyDAGControl',
|
||||
'js/NodePropertyNames',
|
||||
'js/Utils/ComponentSettings',
|
||||
'underscore'
|
||||
], function (
|
||||
DeepForge,
|
||||
EasyDAGControl,
|
||||
nodePropertyNames,
|
||||
ComponentSettings,
|
||||
@@ -23,12 +22,10 @@ define([
|
||||
DefaultColor: '#ffb74d',
|
||||
LayerColors: {
|
||||
Containers: '#ffb74d',
|
||||
Module: '#ba68c8',
|
||||
ConvLayer: '#2196f3',
|
||||
SimpleLayer: '#ff9100',
|
||||
TransferLayer: '#80deea',
|
||||
MiscLayers: '#ce93d8',
|
||||
Criterion: '#7e57c2'
|
||||
Convolution: '#2196f3',
|
||||
Simple: '#ff9100',
|
||||
Transfer: '#80deea',
|
||||
Misc: '#ce93d8'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,6 +42,16 @@ define([
|
||||
return 'ArchEditor';
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype.selectedObjectChanged = function(id) {
|
||||
EasyDAGControl.prototype.selectedObjectChanged.call(this, id);
|
||||
|
||||
DeepForge.last.Architecture = id;
|
||||
if (typeof id === 'string') {
|
||||
var name = this._client.getNode(id).getAttribute('name');
|
||||
this._widget.setTitle(name);
|
||||
}
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype._getObjectDescriptor = function(id) {
|
||||
var desc = EasyDAGControl.prototype._getObjectDescriptor.call(this, id);
|
||||
|
||||
@@ -86,6 +93,7 @@ define([
|
||||
return desc;
|
||||
};
|
||||
|
||||
////////////////////////// Layer Selection Logic //////////////////////////
|
||||
ArchEditorControl.prototype._getValidInitialNodes = function() {
|
||||
return this._client.getChildrenMeta(this._currentNodeId).items
|
||||
// For now, anything is possible!
|
||||
@@ -97,7 +105,67 @@ define([
|
||||
return !this._client.getNode(nodeId).isAbstract();
|
||||
})
|
||||
.map(id => this._getObjectDescriptor(id))
|
||||
.filter(obj => !obj.isConnection && obj.name !== 'Connection');
|
||||
.filter(obj => !obj.isConnection && obj.name !== 'Connection')
|
||||
.filter(layer => layer.layerType !== 'Criterion');
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype._getValidSuccessorNodes =
|
||||
ArchEditorControl.prototype._getValidInitialNodes =
|
||||
ArchEditorControl.prototype.getNonCriterionLayers = function() {
|
||||
// Return all (non-criterion) layer types
|
||||
var metanodes = this._client.getAllMetaNodes(),
|
||||
layerId,
|
||||
criterionId,
|
||||
allLayerIds = [],
|
||||
layers = [],
|
||||
i;
|
||||
|
||||
for (i = metanodes.length; i--;) {
|
||||
if (metanodes[i].getAttribute('name') === 'Layer') {
|
||||
layerId = metanodes[i].getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = metanodes.length; i--;) {
|
||||
if (layerId) {
|
||||
if (!metanodes[i].isAbstract() &&
|
||||
this._client.isTypeOf(metanodes[i].getId(), layerId)) {
|
||||
|
||||
if (metanodes[i].getAttribute('name') === 'Criterion') {
|
||||
criterionId = metanodes[i].getId();
|
||||
} else {
|
||||
allLayerIds.push(metanodes[i].getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all criterion layers and abstract layers
|
||||
for (i = allLayerIds.length; i--;) {
|
||||
if (!this._client.isTypeOf(allLayerIds[i], criterionId)) {
|
||||
layers.push({node: this._getObjectDescriptor(allLayerIds[i])});
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype._isValidTerminalNode = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Widget extensions
|
||||
ArchEditorControl.prototype._initWidgetEventHandlers = function() {
|
||||
EasyDAGControl.prototype._initWidgetEventHandlers.call(this);
|
||||
this._widget.getCreateNewDecorator = this.getCreateNewDecorator.bind(this);
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype.getCreateNewDecorator = function() {
|
||||
return this._client.decoratorManager.getDecoratorForWidget(
|
||||
'EllipseDecorator',
|
||||
'EasyDAG'
|
||||
);
|
||||
};
|
||||
|
||||
return ArchEditorControl;
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'deepforge/viz/RenameablePanel',
|
||||
'widgets/ArchEditor/ArchEditorWidget',
|
||||
'./ArchEditorControl'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
RenameablePanel,
|
||||
ArchEditorWidget,
|
||||
ArchEditorControl
|
||||
) {
|
||||
@@ -22,11 +20,11 @@ define([
|
||||
ArchEditorPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'ArchEditorPanel';
|
||||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
|
||||
options[RenameablePanel.OPTIONS.LOGGER_INSTANCE_NAME] = 'ArchEditorPanel';
|
||||
options[RenameablePanel.OPTIONS.FLOATING_TITLE] = true;
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
RenameablePanel.apply(this, [options, layoutManager]);
|
||||
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
@@ -37,9 +35,7 @@ define([
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(ArchEditorPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(ArchEditorPanel.prototype, IActivePanel.prototype);
|
||||
_.extend(ArchEditorPanel.prototype, RenameablePanel.prototype);
|
||||
|
||||
ArchEditorPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
@@ -60,6 +56,7 @@ define([
|
||||
widget: this.widget
|
||||
});
|
||||
|
||||
this.initializeRenameable();
|
||||
this.onActivate();
|
||||
};
|
||||
|
||||
@@ -67,7 +64,7 @@ define([
|
||||
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
|
||||
ArchEditorPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
RenameablePanel.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
@@ -81,7 +78,7 @@ define([
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
RenameablePanel.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*globals define */
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'panels/TextEditor/TextEditorControl',
|
||||
'underscore',
|
||||
'text!./DefaultCodeTemplate.ejs'
|
||||
], function (
|
||||
TextEditorControl,
|
||||
_,
|
||||
CODE_TEMPLATE
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var ClassCodeEditorControl,
|
||||
getBoilerplate = _.template(CODE_TEMPLATE);
|
||||
|
||||
ClassCodeEditorControl = function (options) {
|
||||
options.attributeName = 'code';
|
||||
TextEditorControl.call(this, options);
|
||||
};
|
||||
|
||||
_.extend(
|
||||
ClassCodeEditorControl.prototype,
|
||||
TextEditorControl.prototype
|
||||
);
|
||||
|
||||
// input/output updates are actually activeNode updates
|
||||
ClassCodeEditorControl.prototype._onUpdate = function (id) {
|
||||
if (id === this._currentNodeId) {
|
||||
TextEditorControl.prototype._onUpdate.call(this, id);
|
||||
}
|
||||
};
|
||||
|
||||
ClassCodeEditorControl.prototype._getObjectDescriptor = function (nodeId) {
|
||||
var desc = TextEditorControl.prototype._getObjectDescriptor.call(this, nodeId),
|
||||
node = this._client.getNode(nodeId),
|
||||
ownCode = node.getOwnAttribute(this.ATTRIBUTE_NAME);
|
||||
|
||||
// If the 'text' attribute is not set, and it's not inheriting anything
|
||||
if (!desc.text && ownCode === undefined) {
|
||||
desc.text = getBoilerplate(desc);
|
||||
}
|
||||
return desc;
|
||||
};
|
||||
|
||||
ClassCodeEditorControl.prototype.saveTextFor = function (id, text) {
|
||||
// On save, update the node's name
|
||||
// For now, simply use regex to grab the returned name
|
||||
var i = text.lastIndexOf('return') + 7,
|
||||
returned = text.substring(i),
|
||||
match = returned.match(/[a-zA-Z0-9_]+/),
|
||||
node = this._client.getNode(id),
|
||||
nodeName = node.getAttribute('name'),
|
||||
name;
|
||||
|
||||
if (match) {
|
||||
name = match[0];
|
||||
}
|
||||
|
||||
this._client.startTransaction(`Updating class "${name || nodeName}"`);
|
||||
if (name) {
|
||||
this._client.setAttributes(id, 'name', name);
|
||||
}
|
||||
TextEditorControl.prototype.saveTextFor.call(this, id, text);
|
||||
this._client.completeTransaction();
|
||||
};
|
||||
|
||||
return ClassCodeEditorControl;
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/ClassCodeEditor/ClassCodeEditorWidget',
|
||||
'./ClassCodeEditorControl'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
ClassCodeEditorWidget,
|
||||
ClassCodeEditorControl
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var ClassCodeEditorPanel;
|
||||
|
||||
ClassCodeEditorPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'ClassCodeEditorPanel';
|
||||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
|
||||
//initialize UI
|
||||
this._initialize();
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(ClassCodeEditorPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(ClassCodeEditorPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
ClassCodeEditorPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new ClassCodeEditorWidget(this.logger, this.$el);
|
||||
|
||||
this.widget.setTitle = function (title) {
|
||||
self.setTitle(title);
|
||||
};
|
||||
|
||||
this.control = new ClassCodeEditorControl({
|
||||
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 */
|
||||
ClassCodeEditorPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
ClassCodeEditorPanel.prototype.onResize = function (width, height) {
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.widget.onWidgetContainerResize(width, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
ClassCodeEditorPanel.prototype.destroy = function () {
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
ClassCodeEditorPanel.prototype.onActivate = function () {
|
||||
this.widget.onActivate();
|
||||
this.control.onActivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(this.widget);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
ClassCodeEditorPanel.prototype.onDeactivate = function () {
|
||||
this.widget.onDeactivate();
|
||||
this.control.onDeactivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
return ClassCodeEditorPanel;
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
torch.class('<%= name %>')
|
||||
|
||||
function <%= name %>:__init()
|
||||
-- TODO: Set constructor behavior
|
||||
end
|
||||
|
||||
function <%= name %>:serialize(filename)
|
||||
torch.save(filename, self)
|
||||
end
|
||||
|
||||
function <%= name %>.deserialize(filename)
|
||||
-- TODO: Load (and return) a '<%= name %>' instance saved to the given file
|
||||
return torch.load(filename)
|
||||
end
|
||||
|
||||
return <%= name %>
|
||||
@@ -0,0 +1,29 @@
|
||||
/*globals define */
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'panels/TilingViz/TilingVizPanel',
|
||||
'panels/ClassCodeEditor/ClassCodeEditorPanel',
|
||||
'underscore'
|
||||
], function (
|
||||
TilingViz,
|
||||
ClassCodeEditor,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var ClassEditorPanel;
|
||||
|
||||
ClassEditorPanel = function (layoutManager, params) {
|
||||
TilingViz.call(this, layoutManager, params);
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(ClassEditorPanel.prototype, TilingViz.prototype);
|
||||
|
||||
ClassEditorPanel.prototype.getPanels = function () {
|
||||
return [ClassCodeEditor];
|
||||
};
|
||||
|
||||
return ClassEditorPanel;
|
||||
});
|
||||
@@ -2,9 +2,11 @@
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'panels/SerializeEditor/SerializeEditorControl',
|
||||
'panels/TextEditor/TextEditorControl',
|
||||
'underscore'
|
||||
], function (
|
||||
SerializeEditorControl,
|
||||
TextEditorControl,
|
||||
_
|
||||
) {
|
||||
@@ -20,15 +22,8 @@ define([
|
||||
|
||||
_.extend(
|
||||
DeserializeEditorControl.prototype,
|
||||
TextEditorControl.prototype
|
||||
SerializeEditorControl.prototype
|
||||
);
|
||||
|
||||
// input/output updates are actually activeNode updates
|
||||
DeserializeEditorControl.prototype._onUpdate = function (id) {
|
||||
if (id === this._currentNodeId) {
|
||||
TextEditorControl.prototype._onUpdate.call(this, this._currentNodeId);
|
||||
}
|
||||
};
|
||||
|
||||
return DeserializeEditorControl;
|
||||
});
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
/*globals define */
|
||||
/*globals define, WebGMEGlobal */
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Tue May 24 2016 10:15:19 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/Constants',
|
||||
'panels/EasyDAG/EasyDAGControl',
|
||||
'deepforge/viz/PipelineControl',
|
||||
'underscore'
|
||||
], function (
|
||||
CONSTANTS,
|
||||
EasyDAGControl,
|
||||
PipelineControl,
|
||||
_
|
||||
@@ -21,6 +23,8 @@ define([
|
||||
ExecutionViewControl = function (options) {
|
||||
EasyDAGControl.call(this, options);
|
||||
this.addedNodes = {};
|
||||
this.originTerritory = {};
|
||||
this.originTerritoryId = null;
|
||||
};
|
||||
|
||||
_.extend(
|
||||
@@ -33,8 +37,62 @@ define([
|
||||
ExecutionViewControl.prototype.TERRITORY_RULE = {children: 4};
|
||||
ExecutionViewControl.prototype.DEFAULT_DECORATOR = 'JobDecorator';
|
||||
|
||||
ExecutionViewControl.prototype.selectedObjectChanged = function(id) {
|
||||
EasyDAGControl.prototype.selectedObjectChanged.call(this, id);
|
||||
|
||||
if (this._currentNodeId) {
|
||||
var desc = this.getExecDesc(this._currentNodeId);
|
||||
|
||||
this._widget.setExecutionNode(desc);
|
||||
this.originId = desc.originId;
|
||||
|
||||
// Add the originId to the territory and update it!
|
||||
if (this.originId) {
|
||||
if (this.originTerritoryId) {
|
||||
this._client.removeUI(this.originTerritoryId);
|
||||
this.originTerritory = {};
|
||||
}
|
||||
|
||||
this.originTerritory[this.originId] = {children: 0};
|
||||
this.originTerritoryId = this._client.addUI(this, events => {
|
||||
var event = events.find(event => event.eid !== null &&
|
||||
event.eid === this.originId);
|
||||
|
||||
if (!event) { // no relevant events
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.etype === CONSTANTS.TERRITORY_EVENT_UNLOAD) {
|
||||
this.originId = null;
|
||||
this._widget.onOriginDeleted();
|
||||
} else {
|
||||
var name = this._client.getNode(this.originId).getAttribute('name');
|
||||
this._widget.setOriginPipeline(name);
|
||||
}
|
||||
});
|
||||
this._client.updateTerritory(this.originTerritoryId, this.originTerritory);
|
||||
} else {
|
||||
this._widget.onOriginDeleted();
|
||||
if (this.originTerritoryId) {
|
||||
this._client.removeUI(this.originTerritoryId);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ExecutionViewControl.prototype.getExecDesc = function(id) {
|
||||
var node = this._client.getNode(id);
|
||||
|
||||
return {
|
||||
isSnapshot: node.getAttribute('snapshot'),
|
||||
createdAt: node.getAttribute('createdAt'),
|
||||
originId: node.getPointer('origin').to
|
||||
};
|
||||
};
|
||||
|
||||
ExecutionViewControl.prototype._onLoad = function(id) {
|
||||
var desc = this._getObjectDescriptor(id);
|
||||
|
||||
if (desc.parentId === this._currentNodeId) {
|
||||
this.addedNodes[id] = true;
|
||||
EasyDAGControl.prototype._onLoad.call(this, id);
|
||||
@@ -54,6 +112,11 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
// Add the connection detection
|
||||
ExecutionViewControl.prototype.onOriginClicked = function() {
|
||||
if (this.originId) {
|
||||
WebGMEGlobal.State.registerActiveObject(this.originId);
|
||||
}
|
||||
};
|
||||
|
||||
return ExecutionViewControl;
|
||||
});
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*globals define, $, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Tue May 24 2016 10:15:19 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define(['js/PanelBase/PanelBaseWithHeader',
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/ExecutionView/ExecutionViewWidget',
|
||||
'./ExecutionViewControl'
|
||||
], function (PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
ExecutionViewWidget,
|
||||
ExecutionViewControl) {
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
ExecutionViewWidget,
|
||||
ExecutionViewControl
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var ExecutionViewPanel;
|
||||
@@ -39,17 +39,24 @@ define(['js/PanelBase/PanelBaseWithHeader',
|
||||
_.extend(ExecutionViewPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
ExecutionViewPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
var self = this,
|
||||
footer = $('<div>', {class: 'footer-caption-container'});
|
||||
|
||||
this.$_el.append(footer);
|
||||
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new ExecutionViewWidget(this.logger, this.$el);
|
||||
|
||||
this.widget.setTitle = function (title) {
|
||||
this.widget._setTitle = function (title) {
|
||||
self.setTitle(title);
|
||||
};
|
||||
|
||||
this.widget.getFooterContainer = function () {
|
||||
return footer;
|
||||
};
|
||||
|
||||
this.control = new ExecutionViewControl({
|
||||
logger: this.logger,
|
||||
client: this._client,
|
||||
@@ -57,6 +64,7 @@ define(['js/PanelBase/PanelBaseWithHeader',
|
||||
widget: this.widget
|
||||
});
|
||||
|
||||
footer.on('click', this.control.onOriginClicked.bind(this.control));
|
||||
this.onActivate();
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*globals define, _, WebGMEGlobal, $ */
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* @author rkereskenyi / https://github.com/rkereskenyi
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBase',
|
||||
'js/Widgets/NetworkStatus/NetworkStatusWidget',
|
||||
'js/Widgets/BranchStatus/BranchStatusWidget',
|
||||
'js/Widgets/BranchSelector/BranchSelectorWidget',
|
||||
'js/Widgets/KeyboardManager/KeyboardManagerWidget',
|
||||
'js/Widgets/Notification/NotificationWidget'
|
||||
], function (PanelBase,
|
||||
NetworkStatusWidget,
|
||||
BranchStatusWidget,
|
||||
BranchSelectorWidget,
|
||||
KeyboardManagerWidget,
|
||||
NotificationWidget) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var FooterPanel,
|
||||
__parent__ = PanelBase;
|
||||
|
||||
FooterPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBase.OPTIONS.LOGGER_INSTANCE_NAME] = 'FooterPanel';
|
||||
|
||||
//call parent's constructor
|
||||
__parent__.apply(this, [options]);
|
||||
|
||||
this._client = params.client;
|
||||
|
||||
//initialize UI
|
||||
this._initialize();
|
||||
|
||||
this.logger.debug('FooterPanel ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(FooterPanel.prototype, __parent__.prototype);
|
||||
|
||||
FooterPanel.prototype._initialize = function () {
|
||||
//main container
|
||||
var navBar = $('<div/>', {class: 'navbar navbar-inverse navbar-fixed-bottom'}),
|
||||
navBarInner = $('<div/>', {class: 'navbar-inner'}),
|
||||
separator = $('<div class="spacer pull-right"></div>'),
|
||||
widgetPlaceHolder = $('<div class="pull-right"></div>'),
|
||||
keyBoardManagerEl,
|
||||
networkStatusEl,
|
||||
branchStatusEl,
|
||||
notificationEl;
|
||||
|
||||
navBar.append(navBarInner);
|
||||
this.$el.append(navBar);
|
||||
|
||||
//padding from screen right edge
|
||||
navBarInner.append(separator.clone());
|
||||
|
||||
//keyboard enable/disbale widget (NOTE: only on non touch device)
|
||||
if (WebGMEGlobal.SUPPORTS_TOUCH !== true) {
|
||||
keyBoardManagerEl = widgetPlaceHolder.clone();
|
||||
new KeyboardManagerWidget(keyBoardManagerEl);
|
||||
navBarInner.append(keyBoardManagerEl).append(separator.clone());
|
||||
}
|
||||
|
||||
networkStatusEl = widgetPlaceHolder.clone();
|
||||
new NetworkStatusWidget(networkStatusEl, this._client);
|
||||
navBarInner.append(networkStatusEl).append(separator.clone());
|
||||
|
||||
notificationEl = widgetPlaceHolder.clone();
|
||||
new NotificationWidget(notificationEl, this._client);
|
||||
navBarInner.append(notificationEl).append(separator.clone());
|
||||
|
||||
branchStatusEl = widgetPlaceHolder.clone();
|
||||
new BranchStatusWidget(branchStatusEl, this._client);
|
||||
navBarInner.append(branchStatusEl).append(separator.clone());
|
||||
};
|
||||
|
||||
return FooterPanel;
|
||||
});
|
||||
@@ -1,124 +1,66 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*globals DeepForge, define, $, Materialize, WebGMEGlobal*/
|
||||
// These are actions defined for specific meta types. They are evaluated from
|
||||
// the context of the ForgeActionButton
|
||||
define([
|
||||
'q',
|
||||
'js/RegistryKeys',
|
||||
'js/Panels/MetaEditor/MetaEditorConstants',
|
||||
'js/Constants'
|
||||
'deepforge/globals'
|
||||
], function(
|
||||
REGISTRY_KEYS,
|
||||
META_CONSTANTS,
|
||||
CONSTANTS
|
||||
Q,
|
||||
REGISTRY_KEYS
|
||||
) {
|
||||
var instances = [
|
||||
'Architecture',
|
||||
'Pipeline'
|
||||
],
|
||||
metaNodes = [
|
||||
'Operation',
|
||||
'Data'
|
||||
],
|
||||
create = {};
|
||||
var FILE_UPLOAD_INPUT = $('<input type="file" />');
|
||||
|
||||
var getUniqueName = function(parentId, basename) {
|
||||
var pNode = this.client.getNode(parentId),
|
||||
children = pNode.getChildrenIds().map(id => this.client.getNode(id)),
|
||||
name = basename,
|
||||
exists = {},
|
||||
i = 2;
|
||||
var createLayer = function() {
|
||||
// Prompt the base type
|
||||
this.promptLayerType().then(selected => {
|
||||
var baseId = selected.node.id,
|
||||
typeName = this.client.getNode(baseId).getAttribute('name'),
|
||||
metanodes = this.client.getAllMetaNodes(),
|
||||
msg = `Created new custom ${typeName} layer`,
|
||||
newId,
|
||||
customLayerId,
|
||||
name;
|
||||
|
||||
children.forEach(child => exists[child.getAttribute('name')] = true);
|
||||
for (var i = metanodes.length; i--;) {
|
||||
name = metanodes[i].getAttribute('name');
|
||||
if (name === 'CustomLayer') {
|
||||
customLayerId = metanodes[i].getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (exists[name]) {
|
||||
name = basename + '_' + i;
|
||||
i++;
|
||||
}
|
||||
this.client.startTransaction(msg);
|
||||
|
||||
return name;
|
||||
newId = this.createNamedNode(baseId, true);
|
||||
this.addToMetaSheet(newId, 'CustomLayers');
|
||||
this.client.addMixin(newId, customLayerId);
|
||||
this.client.setRegistry(newId, REGISTRY_KEYS.IS_ABSTRACT, false);
|
||||
|
||||
this.client.completeTransaction();
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(newId);
|
||||
});
|
||||
};
|
||||
|
||||
var createNew = function(type, metasheetName) {
|
||||
// Create CNN node in the current dir
|
||||
// Get CNN node type
|
||||
var parentId = this._currentNodeId,
|
||||
newId,
|
||||
baseId;
|
||||
////////////// Downloading files //////////////
|
||||
var downloadAttrs = [
|
||||
'data',
|
||||
'execFiles'
|
||||
],
|
||||
download = {};
|
||||
|
||||
baseId = this.client.getAllMetaNodes()
|
||||
.find(node => node.getAttribute('name') === type)
|
||||
.getId();
|
||||
|
||||
this.client.startTransaction('Created new operation prototype');
|
||||
newId = this.client.createChild({parentId, baseId});
|
||||
|
||||
// Name the new node
|
||||
var basename = 'New' + this.client.getNode(baseId).getAttribute('name'),
|
||||
newName = getUniqueName.call(this, parentId, basename);
|
||||
|
||||
// If instance, make the first char lowercase
|
||||
if (!metasheetName) {
|
||||
newName = newName.substring(0, 1).toLowerCase() + newName.substring(1);
|
||||
}
|
||||
|
||||
this.client.setAttributes(newId, 'name', newName);
|
||||
if (metasheetName) { // Add to metasheet
|
||||
var root = this.client.getNode(CONSTANTS.PROJECT_ROOT_ID),
|
||||
metatabs = root.getRegistry(REGISTRY_KEYS.META_SHEETS),
|
||||
metatab = metatabs.find(tab => tab.title === metasheetName) || metatabs[0],
|
||||
metatabId = metatab.SetID;
|
||||
|
||||
// Add to the general meta
|
||||
this.client.addMember(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
newId,
|
||||
META_CONSTANTS.META_ASPECT_SET_NAME
|
||||
);
|
||||
this.client.setMemberRegistry(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
newId,
|
||||
META_CONSTANTS.META_ASPECT_SET_NAME,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
{
|
||||
x: 100,
|
||||
y: 100
|
||||
}
|
||||
);
|
||||
|
||||
// Add to the specific sheet
|
||||
this.client.addMember(CONSTANTS.PROJECT_ROOT_ID, newId, metatabId);
|
||||
this.client.setMemberRegistry(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
newId,
|
||||
metatabId,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
{
|
||||
x: 100,
|
||||
y: 100
|
||||
}
|
||||
);
|
||||
}
|
||||
this.client.completeTransaction();
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(newId);
|
||||
};
|
||||
|
||||
instances.forEach(type => {
|
||||
create[type] = function() {
|
||||
return createNew.call(this, type);
|
||||
};
|
||||
});
|
||||
|
||||
metaNodes.forEach(type => {
|
||||
create[type] = function() {
|
||||
return createNew.call(this, type, type);
|
||||
downloadAttrs.forEach(attr => {
|
||||
download[attr] = function() {
|
||||
return downloadButton.call(this, attr);
|
||||
};
|
||||
});
|
||||
|
||||
// Add download model button
|
||||
var downloadButton = function() {
|
||||
var downloadButton = function(attr) {
|
||||
var id = this._currentNodeId,
|
||||
node = this.client.getNode(id),
|
||||
hash = node.getAttribute('data');
|
||||
hash = node.getAttribute(attr);
|
||||
|
||||
if (hash) {
|
||||
return '/rest/blob/download/' + hash;
|
||||
@@ -126,52 +68,172 @@ define([
|
||||
return null;
|
||||
};
|
||||
|
||||
return {
|
||||
Data: [
|
||||
{
|
||||
name: 'Download',
|
||||
icon: 'play_for_work',
|
||||
href: downloadButton // function to create href url
|
||||
}
|
||||
],
|
||||
var importTorch = function() {
|
||||
var pluginId = 'ImportTorch',
|
||||
context = this.client.getCurrentPluginContext(pluginId),
|
||||
fileInput = FILE_UPLOAD_INPUT.clone();
|
||||
|
||||
MyPipelines_META: [
|
||||
// Prompt for the file
|
||||
fileInput.on('change', event => this.uploadFile(event)
|
||||
.then(hash => {
|
||||
// Run the plugin in the browser (set namespace)
|
||||
context.managerConfig.namespace = 'nn';
|
||||
context.pluginConfig = {
|
||||
srcHash: hash
|
||||
};
|
||||
return Q.ninvoke(this.client, 'runBrowserPlugin', pluginId, context);
|
||||
})
|
||||
.then(res => {
|
||||
Materialize.toast(res.messages[0].message, 2000);
|
||||
})
|
||||
.fail(err => Materialize.toast(`Import failed: ${err}`, 2000))
|
||||
|
||||
);
|
||||
fileInput.click();
|
||||
};
|
||||
|
||||
var returnToLast = (place) => {
|
||||
var returnId = DeepForge.last[place];
|
||||
WebGMEGlobal.State.registerActiveObject(returnId);
|
||||
};
|
||||
|
||||
var prototypeButtons = function(type, fromType) {
|
||||
return [
|
||||
{
|
||||
name: 'Create new pipeline',
|
||||
icon: 'queue',
|
||||
action: create.Pipeline
|
||||
name: `Return to ${fromType}`,
|
||||
icon: 'input',
|
||||
priority: 2,
|
||||
filter: () => {
|
||||
return DeepForge.last[fromType];
|
||||
},
|
||||
action: returnToLast.bind(null, fromType)
|
||||
},
|
||||
{
|
||||
name: `Delete ${type} Definition`,
|
||||
icon: 'delete',
|
||||
priority: 1,
|
||||
action: function() {
|
||||
// Delete and go to the last pipeline?
|
||||
var node = this.client.getNode(this._currentNodeId),
|
||||
name = node.getAttribute('name'),
|
||||
msg = `Deleted ${type} Definition for "${name}"`;
|
||||
|
||||
this.deleteCurrentNode(msg);
|
||||
setTimeout(() => Materialize.toast(msg, 2000), 10);
|
||||
returnToLast(fromType);
|
||||
}
|
||||
}
|
||||
],
|
||||
];
|
||||
};
|
||||
|
||||
var MyPipelinesButtons = [
|
||||
{
|
||||
name: 'Create new pipeline',
|
||||
icon: 'queue',
|
||||
action: DeepForge.create.Pipeline
|
||||
}
|
||||
];
|
||||
|
||||
var makeRestartButton = function(name, pluginId) {
|
||||
return {
|
||||
name: 'Restart ' + name,
|
||||
icon: 'replay',
|
||||
priority: 1000,
|
||||
action: function(event) {
|
||||
this.runExecutionPlugin(pluginId, event.shiftKey);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
HOME: MyPipelinesButtons,
|
||||
MyPipelines_META: MyPipelinesButtons,
|
||||
MyArchitectures_META: [
|
||||
{
|
||||
name: 'Create new architecture',
|
||||
icon: 'queue',
|
||||
action: create.Architecture
|
||||
action: DeepForge.create.Architecture
|
||||
},
|
||||
{
|
||||
name: 'Import Torch Architecture',
|
||||
icon: 'swap_vert',
|
||||
action: importTorch
|
||||
}
|
||||
],
|
||||
MyDataTypes_META: [
|
||||
{
|
||||
name: 'Create new data type',
|
||||
name: 'Create new primitive data type',
|
||||
icon: 'queue',
|
||||
action: create.Data
|
||||
action: DeepForge.create.Primitive
|
||||
},
|
||||
{
|
||||
name: 'Create new class',
|
||||
icon: 'queue',
|
||||
action: DeepForge.create.Complex
|
||||
}
|
||||
],
|
||||
MyLayers_META: [
|
||||
{
|
||||
name: 'Create new layer',
|
||||
icon: 'queue',
|
||||
action: createLayer
|
||||
}
|
||||
],
|
||||
MyOperations_META: [
|
||||
{
|
||||
name: 'Create new operation',
|
||||
icon: 'queue',
|
||||
action: create.Operation
|
||||
action: DeepForge.create.Operation
|
||||
}
|
||||
],
|
||||
MyArtifacts_META: [
|
||||
{
|
||||
name: 'Upload artifact',
|
||||
icon: 'swap_vert',
|
||||
action: DeepForge.create.Artifact
|
||||
}
|
||||
],
|
||||
|
||||
// Creating prototypes
|
||||
Operation_META: prototypeButtons('Operation', 'Pipeline'),
|
||||
Layer_META: prototypeButtons('Layer', 'Architecture'),
|
||||
Complex_META: prototypeButtons('Class', 'Operation'),
|
||||
Primitive_META: prototypeButtons('Primitive Type', 'Operation'),
|
||||
|
||||
// Instances
|
||||
Data: [
|
||||
{
|
||||
name: 'Download',
|
||||
icon: 'play_for_work',
|
||||
href: download.data // function to create href url
|
||||
}
|
||||
],
|
||||
Job: [
|
||||
makeRestartButton('Job', 'ExecuteJob'),
|
||||
{
|
||||
name: 'Download Execution Files',
|
||||
icon: 'play_for_work',
|
||||
priority: 1,
|
||||
href: download.execFiles
|
||||
}
|
||||
],
|
||||
Execution: [makeRestartButton('Execution', 'ExecutePipeline')],
|
||||
Pipeline: [
|
||||
{
|
||||
name: 'Create new node',
|
||||
icon: 'queue',
|
||||
priority: 2,
|
||||
action: function() {
|
||||
this.onCreateInitialNode();
|
||||
this.addOperation();
|
||||
}
|
||||
}
|
||||
],
|
||||
Architecture: [
|
||||
{
|
||||
name: 'Import Torch Architecture',
|
||||
icon: 'swap_vert',
|
||||
action: importTorch
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
/*globals define, _ */
|
||||
/*globals DeepForge, $, Materialize, define, _ */
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'blob/BlobClient',
|
||||
'js/Constants',
|
||||
'panel/FloatingActionButton/FloatingActionButton',
|
||||
'deepforge/viz/PipelineControl',
|
||||
'deepforge/viz/NodePrompter',
|
||||
'./Actions',
|
||||
'text!./PluginConfig.json'
|
||||
'widgets/EasyDAG/AddNodeDialog',
|
||||
'js/RegistryKeys',
|
||||
'js/Panels/MetaEditor/MetaEditorConstants',
|
||||
'q',
|
||||
'text!./PluginConfig.json',
|
||||
'deepforge/globals'
|
||||
], function (
|
||||
BlobClient,
|
||||
CONSTANTS,
|
||||
PluginButton,
|
||||
PipelineControl,
|
||||
NodePrompter,
|
||||
ACTIONS,
|
||||
AddNodeDialog,
|
||||
REGISTRY_KEYS,
|
||||
META_CONSTANTS,
|
||||
Q,
|
||||
PluginConfig
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var NEW_OPERATION_ID = '__NEW_OPERATION__';
|
||||
var ForgeActionButton= function (layoutManager, params) {
|
||||
PluginButton.call(this, layoutManager, params);
|
||||
this._pluginConfig = JSON.parse(PluginConfig);
|
||||
this._client = this.client;
|
||||
this._actions = [];
|
||||
this._blobClient = new BlobClient({
|
||||
logger: this.logger.fork('BlobClient')
|
||||
});
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
@@ -37,14 +54,26 @@ define([
|
||||
base = this.client.getNode(node.getMetaTypeId()),
|
||||
isMeta = base && base.getId() === node.getId(),
|
||||
suffix = isMeta ? '_META' : '',
|
||||
actions,
|
||||
basename;
|
||||
|
||||
while (base && !ACTIONS[basename]) {
|
||||
basename = base.getAttribute('name') + suffix;
|
||||
base = this.client.getNode(base.getBaseId());
|
||||
if (!base) { // must be ROOT or FCO
|
||||
basename = node.getAttribute('name') || 'ROOT_NODE';
|
||||
actions = (ACTIONS[basename] || [])
|
||||
.filter(action => !action.filter || action.filter());
|
||||
return actions;
|
||||
}
|
||||
|
||||
return ACTIONS[basename] || [];
|
||||
while (base && !(actions && actions.length)) {
|
||||
basename = base.getAttribute('name') + suffix;
|
||||
base = this.client.getNode(base.getBaseId());
|
||||
actions = ACTIONS[basename];
|
||||
if (actions) {
|
||||
actions = actions.filter(action => !action.filter || action.filter());
|
||||
}
|
||||
}
|
||||
|
||||
return actions || [];
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.onNodeLoad = function(nodeId) {
|
||||
@@ -70,5 +99,243 @@ define([
|
||||
this.update();
|
||||
};
|
||||
|
||||
// Helper functions REMOVE! FIXME
|
||||
ForgeActionButton.prototype.addToMetaSheet = function(nodeId, metasheetName) {
|
||||
var root = this.client.getNode(CONSTANTS.PROJECT_ROOT_ID),
|
||||
metatabs = root.getRegistry(REGISTRY_KEYS.META_SHEETS),
|
||||
metatab = metatabs.find(tab => tab.title === metasheetName) || metatabs[0],
|
||||
metatabId = metatab.SetID;
|
||||
|
||||
// Add to the general meta
|
||||
this.client.addMember(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
nodeId,
|
||||
META_CONSTANTS.META_ASPECT_SET_NAME
|
||||
);
|
||||
this.client.setMemberRegistry(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
nodeId,
|
||||
META_CONSTANTS.META_ASPECT_SET_NAME,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
{
|
||||
x: 100,
|
||||
y: 100
|
||||
}
|
||||
);
|
||||
|
||||
// Add to the specific sheet
|
||||
this.client.addMember(CONSTANTS.PROJECT_ROOT_ID, nodeId, metatabId);
|
||||
this.client.setMemberRegistry(
|
||||
CONSTANTS.PROJECT_ROOT_ID,
|
||||
nodeId,
|
||||
metatabId,
|
||||
REGISTRY_KEYS.POSITION,
|
||||
{
|
||||
x: 100,
|
||||
y: 100
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.createNamedNode = function(baseId, isMeta) {
|
||||
var parentId = this._currentNodeId,
|
||||
newId = this.client.createChild({parentId, baseId}),
|
||||
basename = 'New' + this.client.getNode(baseId).getAttribute('name'),
|
||||
newName = this.getUniqueName(parentId, basename);
|
||||
|
||||
// If instance, make the first char lowercase
|
||||
if (!isMeta) {
|
||||
newName = newName.substring(0, 1).toLowerCase() + newName.substring(1);
|
||||
}
|
||||
this.client.setAttributes(newId, 'name', newName);
|
||||
return newId;
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.getUniqueName = function(parentId, basename) {
|
||||
var pNode = this.client.getNode(parentId),
|
||||
children = pNode.getChildrenIds().map(id => this.client.getNode(id)),
|
||||
name = basename,
|
||||
exists = {},
|
||||
i = 2;
|
||||
|
||||
children.forEach(child => exists[child.getAttribute('name')] = true);
|
||||
|
||||
while (exists[name]) {
|
||||
name = basename + '_' + i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.getLayerTypeDesc = function(node) {
|
||||
var decManager = this.client.decoratorManager,
|
||||
desc = {};
|
||||
|
||||
desc.id = node.getId();
|
||||
desc.name = node.getAttribute('name');
|
||||
desc.baseName = desc.name;
|
||||
desc.attributes = {};
|
||||
desc.pointers = {};
|
||||
|
||||
// Get the decorator
|
||||
desc.Decorator = decManager.getDecoratorForWidget('EllipseDecorator', 'EasyDAG');
|
||||
|
||||
// Set the color
|
||||
desc.color = '#9e9e9e';
|
||||
return desc;
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.promptLayerType = function() {
|
||||
// Prompt for the new custom layer's base type
|
||||
var metanodes = this.client.getAllMetaNodes(),
|
||||
baseLayerId = metanodes.find(n => n.getAttribute('name') === 'Layer').getId(),
|
||||
layerType,
|
||||
types;
|
||||
|
||||
// PoA:
|
||||
// - Get the layer type ids
|
||||
// - Create the descriptors
|
||||
// - Get the color for the given types
|
||||
// - Move colors to a constants dir?
|
||||
|
||||
// Get the layer type ids
|
||||
layerType = metanodes
|
||||
.filter(node => node.getBaseId() === baseLayerId);
|
||||
|
||||
// - Create the descriptors
|
||||
types = layerType.map(node => {
|
||||
return {
|
||||
node: this.getLayerTypeDesc(node)
|
||||
};
|
||||
});
|
||||
|
||||
return AddNodeDialog.prompt(types);
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.uploadFile = function(event) {
|
||||
var deferred = Q.defer(),
|
||||
file,
|
||||
|
||||
files,
|
||||
afName,
|
||||
artifact;
|
||||
|
||||
// cancel event and hover styling
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
// fetch FileList object
|
||||
files = event.target.files || event.dataTransfer.files;
|
||||
|
||||
// should only receive one file
|
||||
if (files && files.length > 0) {
|
||||
if (files.length > 1) {
|
||||
this.logger.warn('Received multiple files. Using only the first');
|
||||
}
|
||||
|
||||
afName = 'imported-architecture';
|
||||
artifact = this._blobClient.createArtifact(afName);
|
||||
|
||||
file = files[0];
|
||||
artifact.addFileAsSoftLink(file.name, file, (err, hash) => {
|
||||
if (err) {
|
||||
deferred.reject(err);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(hash);
|
||||
});
|
||||
}
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/////////////// Expanding containers ///////////////
|
||||
ForgeActionButton.prototype.addOperation = function() {
|
||||
var ops = this.getValidInitialNodes(),
|
||||
newOperation = this.getNewOpNode();
|
||||
|
||||
// Add the 'New op button'
|
||||
ops.push(newOperation);
|
||||
|
||||
this.promptNode(ops, (selected, prompter) => {
|
||||
if (selected.id === NEW_OPERATION_ID) {
|
||||
prompter.destroy();
|
||||
DeepForge.create.Operation();
|
||||
} else {
|
||||
this.createNode(selected.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.getNewOpNode = function() {
|
||||
var Decorator = this.client.decoratorManager.getDecoratorForWidget(
|
||||
'OperationDecorator', 'EasyDAG');
|
||||
|
||||
return {
|
||||
id: NEW_OPERATION_ID,
|
||||
class: 'create-node',
|
||||
name: 'New Operation...',
|
||||
Decorator: Decorator,
|
||||
attributes: {}
|
||||
};
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.promptNode = function(nodes, selectFn) {
|
||||
// Get the absolute location of the given button
|
||||
var mainBtn = this.$el[0].children[0],
|
||||
rect = mainBtn.getBoundingClientRect(),
|
||||
panelRect,
|
||||
panelWidth = 400,
|
||||
panelHeight = 400,
|
||||
btns = this.$el.find('.tooltipped'),
|
||||
ids;
|
||||
|
||||
this.$el.closeFAB();
|
||||
|
||||
// Hide the tooltip
|
||||
ids = Array.prototype.map.call(btns, el => el.getAttribute('data-tooltip-id'));
|
||||
ids.map(id => $('#' + id))
|
||||
.filter(matches => matches.length)
|
||||
.forEach(tooltip => tooltip.hide());
|
||||
|
||||
panelRect = {
|
||||
left: rect.right-panelWidth,
|
||||
top: rect.bottom-panelHeight,
|
||||
width: panelWidth,
|
||||
height: panelHeight
|
||||
};
|
||||
|
||||
var cx = panelWidth-rect.width/2,
|
||||
cy = panelHeight-rect.width/2,
|
||||
prompter = new NodePrompter(panelRect, {cx, cy, padding: 5});
|
||||
|
||||
return prompter.prompt(nodes, selectFn);
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.deleteCurrentNode = function(msg) {
|
||||
var nodeId = this._currentNodeId;
|
||||
if (nodeId) {
|
||||
this.client.startTransaction(msg);
|
||||
this.client.delMoreNodes([nodeId]);
|
||||
this.client.completeTransaction(msg);
|
||||
}
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.runExecutionPlugin = function(pluginId, useSecondary) {
|
||||
var context = this.client.getCurrentPluginContext(pluginId),
|
||||
name = this.client.getNode(this._currentNodeId).getAttribute('name'),
|
||||
method;
|
||||
|
||||
context.managerConfig.namespace = 'pipeline';
|
||||
method = useSecondary ? 'runBrowserPlugin' : 'runServerPlugin';
|
||||
this.client[method](pluginId, context, err => {
|
||||
if (err) {
|
||||
return Materialize.toast(`${name} failed!`, 4000);
|
||||
}
|
||||
|
||||
Materialize.toast(`${name} executed successfully!`, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
return ForgeActionButton;
|
||||
});
|
||||
|
||||
@@ -10,5 +10,9 @@
|
||||
"ImportTorch": {
|
||||
"icon": "import_export",
|
||||
"priority": -1
|
||||
},
|
||||
"GenerateExecFile": {
|
||||
"icon": "play_for_work",
|
||||
"priority": -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*globals define, _ */
|
||||
/*jshint browser: true*/
|
||||
|
||||
// This editor will be a split screen view w/ the operation code on the left
|
||||
// and the terminal on the right.
|
||||
//
|
||||
// However, if the job is contained in a "snapshotted" execution, then it will
|
||||
// be considered read only and only show the terminal output
|
||||
define([
|
||||
'panels/TilingViz/TilingVizPanel',
|
||||
'panels/OutputViewer/OutputViewerPanel',
|
||||
'panels/OperationCodeEditor/OperationCodeEditorPanel',
|
||||
'js/Constants'
|
||||
], function (
|
||||
TilingViz,
|
||||
OutputViewer,
|
||||
OperationCodeEditor,
|
||||
CONSTANTS
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var JobEditorPanel;
|
||||
|
||||
JobEditorPanel = function (layoutManager, params) {
|
||||
TilingViz.call(this, layoutManager, params);
|
||||
this.readOnly = false;
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(JobEditorPanel.prototype, TilingViz.prototype);
|
||||
|
||||
JobEditorPanel.prototype.getPanels = function () {
|
||||
if (this.readOnly) {
|
||||
return [OutputViewer];
|
||||
} else {
|
||||
return [OperationCodeEditor, OutputViewer];
|
||||
}
|
||||
};
|
||||
|
||||
JobEditorPanel.prototype.selectedObjectChanged = function (nodeId) {
|
||||
var node = this._client.getNode(nodeId),
|
||||
typeId,
|
||||
type,
|
||||
typeName,
|
||||
executionId,
|
||||
execution;
|
||||
|
||||
if (typeof nodeId === 'string') {
|
||||
typeId = node.getMetaTypeId();
|
||||
type = this._client.getNode(typeId);
|
||||
typeName = type.getAttribute('name');
|
||||
|
||||
if (typeName !== 'Job') {
|
||||
this.logger.error(`Invalid node type for JobEditor: ${typeName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
executionId = node.getParentId();
|
||||
execution = this._client.getNode(executionId);
|
||||
|
||||
// If the current node is in a snapshotted execution, only show the log
|
||||
// viewer
|
||||
if (this.readOnly !== execution.getAttribute('snapshot')) {
|
||||
this.readOnly = execution.getAttribute('snapshot');
|
||||
this.logger.info(`readonly set to ${this.readOnly}`);
|
||||
this.updatePanels();
|
||||
}
|
||||
|
||||
// The OperationCodeEditor should receive the
|
||||
if (!this.readOnly) {
|
||||
// Get the operation base node id and pass it to OpCodeEditor selObjChanged
|
||||
if (this._territoryId) {
|
||||
this._client.removeUI(this._territoryId);
|
||||
}
|
||||
this._territoryId = this._client.addUI(this,
|
||||
this.onOperationEvents.bind(this));
|
||||
|
||||
// Update the territory
|
||||
this._territory = {};
|
||||
this._territory[nodeId] = {children: 1};
|
||||
|
||||
this._client.updateTerritory(this._territoryId, this._territory);
|
||||
}
|
||||
|
||||
// update the OutputViewer controller
|
||||
var i = this._panels.length;
|
||||
this._panels[i-1].control.selectedObjectChanged(nodeId);
|
||||
}
|
||||
};
|
||||
|
||||
JobEditorPanel.prototype.onOperationEvents = function (events) {
|
||||
var event = events.find(event => {
|
||||
if (event.etype === CONSTANTS.TERRITORY_EVENT_LOAD) {
|
||||
// Check if the eid is an Operation
|
||||
var typeId = this._client.getNode(event.eid).getMetaTypeId(),
|
||||
type = this._client.getNode(typeId),
|
||||
metaBaseId = type && type.getBaseId(),
|
||||
typeName;
|
||||
|
||||
if (metaBaseId) {
|
||||
typeName = this._client.getNode(metaBaseId).getAttribute('name');
|
||||
}
|
||||
|
||||
return typeName === 'Operation';
|
||||
}
|
||||
});
|
||||
|
||||
if (event && !this.readOnly) {
|
||||
var opNode = this._client.getNode(event.eid),
|
||||
opDefId = opNode.getMetaTypeId();
|
||||
|
||||
this._panels[0].control.selectedObjectChanged(opDefId);
|
||||
this._panels[0].control.offsetNodeChanged(event.eid);
|
||||
}
|
||||
};
|
||||
|
||||
return JobEditorPanel;
|
||||
});
|
||||
@@ -0,0 +1,115 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'panels/TextEditor/TextEditorControl'
|
||||
], function (
|
||||
TextEditorControl
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var NO_CODE_MESSAGE = '-- <%= name %> is not an editable layer!',
|
||||
LayerEditorControl;
|
||||
|
||||
LayerEditorControl = function (options) {
|
||||
TextEditorControl.call(this, options);
|
||||
};
|
||||
|
||||
_.extend(LayerEditorControl.prototype, TextEditorControl.prototype);
|
||||
|
||||
LayerEditorControl.prototype.loadMetaNodes = function () {
|
||||
return this._client.getAllMetaNodes();
|
||||
};
|
||||
|
||||
// This next function retrieves the relevant node information for the widget
|
||||
LayerEditorControl.prototype._getObjectDescriptor = function (nodeId) {
|
||||
var desc = TextEditorControl.prototype._getObjectDescriptor.call(this, nodeId),
|
||||
node = this._client.getNode(nodeId),
|
||||
hasCode = node.getValidAttributeNames().indexOf('code') > -1,
|
||||
template;
|
||||
|
||||
// Get own attribute, if set. Otherwise, set the text to the parent's populated
|
||||
// template
|
||||
this.loadMetaNodes();
|
||||
if (hasCode) { // is a custom layer
|
||||
if (!node.getOwnAttribute('code')) {
|
||||
// Retrieve the template from the mixin
|
||||
template = node.getMixinPaths()
|
||||
.map(id => this._client.getNode(id).getAttribute('code'))
|
||||
.find(code => !!code) || NO_CODE_MESSAGE;
|
||||
}
|
||||
} else {
|
||||
template = NO_CODE_MESSAGE;
|
||||
}
|
||||
|
||||
if (template) {
|
||||
desc.text = _.template(template)(desc);
|
||||
}
|
||||
return desc;
|
||||
};
|
||||
|
||||
LayerEditorControl.prototype.saveTextFor = function (id, text) {
|
||||
var r = /:__init\((.*)\)/,
|
||||
match = text.match(r),
|
||||
textMatch = match && match[1],
|
||||
node = this._client.getNode(id),
|
||||
currentAttrs = node.getValidAttributeNames(),
|
||||
attributes = [],
|
||||
msg = `Updating layer definition for ${node.getAttribute('name')}`;
|
||||
|
||||
// Parse the attributes and update the node!
|
||||
if (textMatch) {
|
||||
attributes = textMatch.split(',')
|
||||
.map(arg => arg.replace(/\s+/g, '')) // trim white space
|
||||
.filter(arg => !!arg); // no empty strings!
|
||||
} else { // inheriting __init
|
||||
attributes = this.getInheritedAttrs(text);
|
||||
}
|
||||
|
||||
this._client.startTransaction(msg);
|
||||
|
||||
TextEditorControl.prototype.saveTextFor.call(this, id, text);
|
||||
|
||||
// Remove old attributes
|
||||
_.difference(currentAttrs, attributes)
|
||||
.forEach(attr => this._client.removeAttributeSchema(id, attr));
|
||||
|
||||
attributes.forEach((attr, i) =>
|
||||
this._client.setAttributeSchema(id, attr, {type: 'string', argindex: i}));
|
||||
|
||||
this._client.completeTransaction();
|
||||
};
|
||||
|
||||
LayerEditorControl.prototype.getInheritedAttrs = function (code) {
|
||||
// Get the base class
|
||||
var r = /torch.class\((.*)\)/,
|
||||
match = code.match(r),
|
||||
baseType,
|
||||
metanode,
|
||||
textMatch = match && match[1];
|
||||
|
||||
if (textMatch) {
|
||||
baseType = textMatch.split(',')[1]
|
||||
.replace(/^\s*['"]nn\./, '')
|
||||
.replace(/['"]\s*$/, '');
|
||||
|
||||
this._logger.debug(`inheriting the attributes from ${baseType}`);
|
||||
|
||||
// Get the meta node and valid attribute names
|
||||
metanode = this._client.getAllMetaNodes()
|
||||
.find(node => node.getAttribute('name') === baseType);
|
||||
|
||||
if (metanode) {
|
||||
return metanode.getValidAttributeNames()
|
||||
.filter(attr => attr !== 'name');
|
||||
} else {
|
||||
// Check if the type is known by torch
|
||||
this._logger.warn(`Unknown base type ${baseType}. Assuming attributes are []`);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
return LayerEditorControl;
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/TextEditor/TextEditorWidget',
|
||||
'./LayerEditorControl'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
TextEditorWidget,
|
||||
LayerEditorControl
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var LayerEditorPanel;
|
||||
|
||||
LayerEditorPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'LayerEditorPanel';
|
||||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
|
||||
//initialize UI
|
||||
this._initialize();
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(LayerEditorPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(LayerEditorPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
LayerEditorPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new TextEditorWidget(this.logger, this.$el);
|
||||
|
||||
this.widget.setTitle = function (title) {
|
||||
self.setTitle(title);
|
||||
};
|
||||
|
||||
this.control = new LayerEditorControl({
|
||||
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 */
|
||||
LayerEditorPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
LayerEditorPanel.prototype.onResize = function (width, height) {
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.widget.onWidgetContainerResize(width, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
LayerEditorPanel.prototype.destroy = function () {
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LayerEditorPanel.prototype.onActivate = function () {
|
||||
this.widget.onActivate();
|
||||
this.control.onActivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(this.widget);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LayerEditorPanel.prototype.onDeactivate = function () {
|
||||
this.widget.onDeactivate();
|
||||
this.control.onDeactivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
return LayerEditorPanel;
|
||||
});
|
||||
@@ -0,0 +1,184 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'js/Constants',
|
||||
'js/Utils/GMEConcepts',
|
||||
'js/NodePropertyNames'
|
||||
], function (
|
||||
CONSTANTS,
|
||||
GMEConcepts,
|
||||
nodePropertyNames
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var LineGraphControl;
|
||||
|
||||
LineGraphControl = 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._currentNodeParentId = undefined;
|
||||
|
||||
this._logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer content update callbacks * * * * * * * */
|
||||
// One major concept here is with managing the territory. The territory
|
||||
// defines the parts of the project that the visualizer is interested in
|
||||
// (this allows the browser to then only load those relevant parts).
|
||||
LineGraphControl.prototype.selectedObjectChanged = function (nodeId) {
|
||||
var desc = this._getObjectDescriptor(nodeId),
|
||||
self = this;
|
||||
|
||||
self._logger.debug('activeObject nodeId \'' + nodeId + '\'');
|
||||
|
||||
// Remove current territory patterns
|
||||
if (self._currentNodeId) {
|
||||
self._client.removeUI(self._territoryId);
|
||||
}
|
||||
|
||||
self._currentNodeId = nodeId;
|
||||
self._currentNodeParentId = undefined;
|
||||
|
||||
if (typeof self._currentNodeId === 'string') {
|
||||
// Put new node's info into territory rules
|
||||
self._selfPatterns = {};
|
||||
self._selfPatterns[nodeId] = {children: 0}; // Territory "rule"
|
||||
|
||||
self._widget.setTitle(desc.name.toUpperCase());
|
||||
|
||||
self._currentNodeParentId = desc.parentId;
|
||||
|
||||
self._territoryId = self._client.addUI(self, function (events) {
|
||||
self._eventCallback(events);
|
||||
});
|
||||
|
||||
// Update the territory
|
||||
self._client.updateTerritory(self._territoryId, self._selfPatterns);
|
||||
|
||||
self._selfPatterns[nodeId] = {children: 1};
|
||||
self._client.updateTerritory(self._territoryId, self._selfPatterns);
|
||||
}
|
||||
};
|
||||
|
||||
// This next function retrieves the relevant node information for the widget
|
||||
LineGraphControl.prototype._getObjectDescriptor = function (nodeId) {
|
||||
var node = this._client.getNode(nodeId),
|
||||
desc;
|
||||
|
||||
if (node) {
|
||||
desc = {
|
||||
id: node.getId(),
|
||||
name: node.getAttribute(nodePropertyNames.Attributes.name)
|
||||
};
|
||||
|
||||
// Check if it is a line
|
||||
if (desc.id !== this._currentNodeId) {
|
||||
var points = (node.getAttribute('points') || '').split(';')
|
||||
.map(pair => {
|
||||
var nums = pair.split(','),
|
||||
x = +nums[0],
|
||||
y = +nums[1];
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y:y
|
||||
};
|
||||
});
|
||||
desc.type = 'line';
|
||||
desc.points = points;
|
||||
}
|
||||
}
|
||||
|
||||
return desc;
|
||||
};
|
||||
|
||||
/* * * * * * * * Node Event Handling * * * * * * * */
|
||||
LineGraphControl.prototype._eventCallback = function (events) {
|
||||
var i = events ? events.length : 0,
|
||||
event;
|
||||
|
||||
this._logger.debug('_eventCallback \'' + i + '\' items');
|
||||
|
||||
while (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('_eventCallback \'' + events.length + '\' items - DONE');
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._onLoad = function (gmeId) {
|
||||
var description = this._getObjectDescriptor(gmeId);
|
||||
this._widget.addNode(description);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._onUpdate = function (gmeId) {
|
||||
var description = this._getObjectDescriptor(gmeId);
|
||||
this._widget.updateNode(description);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._onUnload = function (gmeId) {
|
||||
this._widget.removeNode(gmeId);
|
||||
};
|
||||
|
||||
LineGraphControl.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 * * * * * * * */
|
||||
LineGraphControl.prototype.destroy = function () {
|
||||
this._detachClientEventListeners();
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._attachClientEventListeners = function () {
|
||||
this._detachClientEventListeners();
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged, this);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._detachClientEventListeners = function () {
|
||||
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype.onActivate = function () {
|
||||
this._attachClientEventListeners();
|
||||
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(true);
|
||||
WebGMEGlobal.State.registerActiveObject(this._currentNodeId);
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(false);
|
||||
}
|
||||
};
|
||||
|
||||
LineGraphControl.prototype.onDeactivate = function () {
|
||||
this._detachClientEventListeners();
|
||||
};
|
||||
|
||||
return LineGraphControl;
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/LineGraph/LineGraphWidget',
|
||||
'./LineGraphControl'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
LineGraphWidget,
|
||||
LineGraphControl
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var LineGraphPanel;
|
||||
|
||||
LineGraphPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'LineGraphPanel';
|
||||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
|
||||
//initialize UI
|
||||
this._initialize();
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(LineGraphPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(LineGraphPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
LineGraphPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new LineGraphWidget(this.logger, this.$el);
|
||||
|
||||
this.widget.setTitle = function (title) {
|
||||
self.setTitle(title);
|
||||
};
|
||||
|
||||
this.control = new LineGraphControl({
|
||||
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 */
|
||||
LineGraphPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
LineGraphPanel.prototype.onResize = function (width, height) {
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.widget.onWidgetContainerResize(width, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
LineGraphPanel.prototype.destroy = function () {
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LineGraphPanel.prototype.onActivate = function () {
|
||||
this.widget.onActivate();
|
||||
this.control.onActivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(this.widget);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LineGraphPanel.prototype.onDeactivate = function () {
|
||||
this.widget.onDeactivate();
|
||||
this.control.onDeactivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
return LineGraphPanel;
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
// This is a read-only view of the 'stdout' attribute for a Job node
|
||||
define([
|
||||
'panels/TextEditor/TextEditorControl'
|
||||
], function (
|
||||
TextEditorControl
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var LogViewerControl;
|
||||
|
||||
LogViewerControl = function (options) {
|
||||
options.attributeName = 'stdout';
|
||||
TextEditorControl.call(this, options);
|
||||
};
|
||||
|
||||
_.extend(LogViewerControl.prototype, TextEditorControl.prototype);
|
||||
|
||||
LogViewerControl.prototype._onUpdate = function (id) {
|
||||
if (id === this._currentNodeId) {
|
||||
TextEditorControl.prototype._onUpdate.call(this, id);
|
||||
}
|
||||
};
|
||||
|
||||
return LogViewerControl;
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Wed Jun 15 2016 14:06:10 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/LogViewer/LogViewerWidget',
|
||||
'./LogViewerControl'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
LogViewerWidget,
|
||||
LogViewerControl
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var LogViewerPanel;
|
||||
|
||||
LogViewerPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'LogViewerPanel';
|
||||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
|
||||
//initialize UI
|
||||
this._initialize();
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(LogViewerPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(LogViewerPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
LogViewerPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new LogViewerWidget(this.logger, this.$el);
|
||||
|
||||
this.widget.setTitle = function (title) {
|
||||
self.setTitle(title);
|
||||
};
|
||||
|
||||
this.control = new LogViewerControl({
|
||||
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 */
|
||||
LogViewerPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
LogViewerPanel.prototype.onResize = function (width, height) {
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.widget.onWidgetContainerResize(width, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
LogViewerPanel.prototype.destroy = function () {
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LogViewerPanel.prototype.onActivate = function () {
|
||||
this.widget.onActivate();
|
||||
this.control.onActivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(this.widget);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LogViewerPanel.prototype.onDeactivate = function () {
|
||||
this.widget.onDeactivate();
|
||||
this.control.onDeactivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
return LogViewerPanel;
|
||||
});
|
||||
@@ -0,0 +1,225 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'blob/BlobClient',
|
||||
'js/Constants',
|
||||
'js/Utils/GMEConcepts',
|
||||
'js/NodePropertyNames',
|
||||
'deepforge/globals'
|
||||
], function (
|
||||
BlobClient,
|
||||
CONSTANTS,
|
||||
GMEConcepts,
|
||||
nodePropertyNames,
|
||||
DeepForge
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var MainViewControl;
|
||||
|
||||
MainViewControl = function (options) {
|
||||
|
||||
this._logger = options.logger.fork('Control');
|
||||
|
||||
this._client = options.client;
|
||||
|
||||
// Initialize core collections and variables
|
||||
this._widget = options.widget;
|
||||
|
||||
this._currentNodeId = null;
|
||||
this._embedded = options.embedded;
|
||||
|
||||
this.territory = {};
|
||||
this.ui = {};
|
||||
this._blobClient = new BlobClient({
|
||||
logger: this._logger.fork('BlobClient')
|
||||
});
|
||||
|
||||
this._initWidgetEventHandlers();
|
||||
this._logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
MainViewControl.prototype._initWidgetEventHandlers = function () {
|
||||
this._widget.deleteNode = id => {
|
||||
var node = this._client.getNode(id),
|
||||
baseId = node.getBaseId(),
|
||||
base = this._client.getNode(baseId),
|
||||
baseName = base.getAttribute('name'),
|
||||
name = node.getAttribute('name'),
|
||||
msg = `Deleting ${baseName} "${name}"`;
|
||||
|
||||
this._client.startTransaction(msg);
|
||||
this._client.delMoreNodes([id]);
|
||||
this._client.completeTransaction();
|
||||
};
|
||||
|
||||
this._widget.dataUrlFor = id => {
|
||||
var node = this._client.getNode(id),
|
||||
hash = node.getAttribute('data');
|
||||
|
||||
if (hash) {
|
||||
return this._blobClient.getDownloadURL(hash);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer content update callbacks * * * * * * * */
|
||||
// One major concept here is with managing the territory. The territory
|
||||
// defines the parts of the project that the visualizer is interested in
|
||||
// (this allows the browser to then only load those relevant parts).
|
||||
MainViewControl.prototype.selectedObjectChanged = function (nodeId) {
|
||||
this._logger.debug('activeObject nodeId \'' + nodeId + '\'');
|
||||
|
||||
// Remove current territory patterns
|
||||
this.clearTerritoryRules();
|
||||
|
||||
this._currentNodeId = nodeId;
|
||||
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
var terrTypes = [
|
||||
/* [type, root dir] */
|
||||
['arch', 'MyArchitectures'],
|
||||
['artifact', 'MyArtifacts']
|
||||
];
|
||||
|
||||
terrTypes.forEach(pair => {
|
||||
var type = pair[0],
|
||||
dirname = pair[1];
|
||||
|
||||
// Update the territory
|
||||
this.territory[type] = {};
|
||||
this.territory[type][DeepForge.places[dirname]] = {children: 1};
|
||||
this.ui[type] = this._client.addUI(this, this.handleEvents.bind(this, type));
|
||||
this._client.updateTerritory(this.ui[type], this.territory[type]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.handleEvents = function (type, events) {
|
||||
var event;
|
||||
|
||||
// Remove the containing dir
|
||||
events = events.filter(e => !this.territory[type][e.eid]);
|
||||
this._logger.debug('_eventCallback \'' + i + '\' items');
|
||||
|
||||
for (var i = events.length; i--;) {
|
||||
event = events[i];
|
||||
switch (event.etype) {
|
||||
|
||||
case CONSTANTS.TERRITORY_EVENT_LOAD:
|
||||
this.onLoad(type, event.eid);
|
||||
break;
|
||||
case CONSTANTS.TERRITORY_EVENT_UPDATE:
|
||||
this._onUpdate(type, event.eid);
|
||||
break;
|
||||
case CONSTANTS.TERRITORY_EVENT_UNLOAD:
|
||||
this._onUnload(event.eid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._logger.debug('_eventCallback \'' + events.length + '\' items - DONE');
|
||||
};
|
||||
|
||||
MainViewControl.prototype.onLoad = function(type, id) {
|
||||
// Load a node of the given type
|
||||
var desc = this._getObjectDescriptor(type, id);
|
||||
if (type === 'arch') {
|
||||
this._widget.addArch(desc);
|
||||
} else { // artifacts
|
||||
this._widget.addArtifact(desc);
|
||||
}
|
||||
};
|
||||
|
||||
// This next function retrieves the relevant node information for the widget
|
||||
MainViewControl.prototype._getArtifactDesc = function (id) {
|
||||
var node = this._client.getNode(id),
|
||||
data = node.getAttribute('data'),
|
||||
desc = this._getBasicDesc(id);
|
||||
|
||||
desc.data = data;
|
||||
return desc;
|
||||
};
|
||||
|
||||
MainViewControl.prototype._getArchDesc =
|
||||
MainViewControl.prototype._getBasicDesc = function (id) {
|
||||
var node = this._client.getNode(id);
|
||||
|
||||
return {
|
||||
id: id,
|
||||
name: node.getAttribute('name')
|
||||
};
|
||||
};
|
||||
|
||||
MainViewControl.prototype._getObjectDescriptor = function (type, id) {
|
||||
return type === 'arch' ?
|
||||
this._getArchDesc(id) :
|
||||
this._getArtifactDesc(id);
|
||||
};
|
||||
|
||||
/* * * * * * * * Node Event Handling * * * * * * * */
|
||||
MainViewControl.prototype._onUpdate = function (type, gmeId) {
|
||||
var description = this._getObjectDescriptor(type, gmeId);
|
||||
this._widget.updateNode(description);
|
||||
};
|
||||
|
||||
MainViewControl.prototype._onUnload = function (gmeId) {
|
||||
this._widget.removeNode(gmeId);
|
||||
};
|
||||
|
||||
MainViewControl.prototype._stateActiveObjectChanged = function (model, activeObjectId) {
|
||||
if (this._currentNodeId !== activeObjectId) {
|
||||
this.selectedObjectChanged(activeObjectId);
|
||||
}
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
MainViewControl.prototype.destroy = function () {
|
||||
this._detachClientEventListeners();
|
||||
this.clearTerritoryRules();
|
||||
};
|
||||
|
||||
MainViewControl.prototype._attachClientEventListeners = function () {
|
||||
this._detachClientEventListeners();
|
||||
if (!this._embedded) {
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
|
||||
this._stateActiveObjectChanged, this);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype._detachClientEventListeners = function () {
|
||||
if (!this._embedded) {
|
||||
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
|
||||
this._stateActiveObjectChanged);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.onActivate = function () {
|
||||
this._attachClientEventListeners();
|
||||
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(true);
|
||||
WebGMEGlobal.State.registerActiveObject(this._currentNodeId);
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(false);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.clearTerritoryRules = function () {
|
||||
if (Object.keys(this.ui).length) {
|
||||
Object.keys(this.ui).forEach(id =>
|
||||
this._client.removeUI(this.ui[id]));
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.onDeactivate = function () {
|
||||
this._detachClientEventListeners();
|
||||
};
|
||||
|
||||
return MainViewControl;
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
/*globals define, $, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
// The main panel shows the PipelineIndex w/ a bar on the left for viewing architectures
|
||||
// and pipelines
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/MainView/MainViewWidget',
|
||||
'./MainViewControl',
|
||||
'panels/PipelineIndex/PipelineIndexPanel',
|
||||
'deepforge/globals'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
MainViewWidget,
|
||||
MainViewControl,
|
||||
PipelineIndexPanel,
|
||||
DeepForge
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var MainViewPanel;
|
||||
|
||||
MainViewPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'MainViewPanel';
|
||||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
|
||||
//initialize UI
|
||||
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._initialize();
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(MainViewPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(MainViewPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
MainViewPanel.prototype._initialize = function () {
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new MainViewWidget(this.logger, this.$nav);
|
||||
|
||||
this.control = new MainViewControl({
|
||||
logger: this.logger,
|
||||
client: this._client,
|
||||
embedded: this._embedded,
|
||||
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.onActivate();
|
||||
};
|
||||
|
||||
/* OVERRIDE FROM WIDGET-WITH-HEADER */
|
||||
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
|
||||
MainViewPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
MainViewPanel.prototype.onResize = function (width, height) {
|
||||
var navWidth,
|
||||
embeddedWidth;
|
||||
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.widget.onWidgetContainerResize(width, height);
|
||||
navWidth = this.widget.width();
|
||||
embeddedWidth = width-navWidth;
|
||||
this.$embedded.css({
|
||||
width: embeddedWidth,
|
||||
height: height,
|
||||
left: navWidth,
|
||||
margin: 'inherit'
|
||||
});
|
||||
this.embeddedPanel.onResize(embeddedWidth, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
MainViewPanel.prototype.destroy = function () {
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
MainViewPanel.prototype.onActivate = function () {
|
||||
this.widget.onActivate();
|
||||
this.control.onActivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(this.widget);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
MainViewPanel.prototype.onDeactivate = function () {
|
||||
this.widget.onDeactivate();
|
||||
this.control.onDeactivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
return MainViewPanel;
|
||||
});
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário