Comparar commits
257 Commits
v0.6.0
...
code-coverage
| Autor | SHA1 | Data | |
|---|---|---|---|
| 3676d4c340 | |||
| 16668468f4 | |||
| ffae88a168 | |||
| 25f5759c17 | |||
| 7a0eae386f | |||
| af5d74483a | |||
| 9bf7632aba | |||
| d7f3544bb3 | |||
| 19a7b2a8fa | |||
| aadd581189 | |||
| a5d00efd39 | |||
| a239c7bd44 | |||
| 5871b5058e | |||
| 3a8108b321 | |||
| b18a3b2747 | |||
| 884ba2557f | |||
| e2d3058de1 | |||
| 02739bfd62 | |||
| 89e2e387c7 | |||
| 0ac2b1b6ab | |||
| a36e8df404 | |||
| 93a89ddaa1 | |||
| 7281dcefc6 | |||
| bfc1f08c7e | |||
| ad08fce281 | |||
| bb2a67e3f6 | |||
| 3f0b7e4720 | |||
| 728c56261d | |||
| a3dd12386e | |||
| c017f9a1d7 | |||
| 6d95f21550 | |||
| 11cb9259d8 | |||
| 71f575c857 | |||
| cf4404eaf7 | |||
| 6ef0cb0bdc | |||
| e128cc408e | |||
| 1477421f1a | |||
| 671a8af458 | |||
| a4fcc73e9f | |||
| e88fb302d4 | |||
| ff1f29955a | |||
| 0b71ff6b9d | |||
| 4f3b6c5e39 | |||
| fe0c95116f | |||
| 4429cb11d4 | |||
| 56b20ab429 | |||
| 4d0f4c3609 | |||
| 209f46adb0 | |||
| 2ac374a56e | |||
| fc4c5db0ce | |||
| 1578886584 | |||
| 0927c2c270 | |||
| bd329bdfe3 | |||
| e3a499f409 | |||
| 63c78426d3 | |||
| 6ec2f69268 | |||
| 1ccd193ddd | |||
| a5d52dce33 | |||
| ca358ae7b9 | |||
| 0a1177c299 | |||
| 46bf346c5c | |||
| 8a94496e01 | |||
| d3cf339856 | |||
| 5c0c58c3be | |||
| ef607e0e76 | |||
| 305503ac7a | |||
| c76e62b976 | |||
| 554065ee11 | |||
| 7fba52ad97 | |||
| c56de24e7d | |||
| 6e16087fc3 | |||
| 8133acbb46 | |||
| d974cb8215 | |||
| 68021c1903 | |||
| 7178b89578 | |||
| 0935abe858 | |||
| 22225922e5 | |||
| 343f2ffa61 | |||
| 477d38d313 | |||
| 84e5377b8a | |||
| 1abbecc54c | |||
| af2f34545b | |||
| f7499c4599 | |||
| ad52fe7d70 | |||
| 5ddeb6f331 | |||
| 7cd3d961cf | |||
| c38b38b4a1 | |||
| 6ae73ece70 | |||
| b5512d8228 | |||
| 89c871412a | |||
| 33e28aa3f1 | |||
| 3899c3cb16 | |||
| eb4f97e9b5 | |||
| 16e37043f4 | |||
| 261ffd1eba | |||
| 9167b33e18 | |||
| 6f7f0d01e5 | |||
| c11b1fe812 | |||
| 306425cae1 | |||
| 6d70728b54 | |||
| fadb654883 | |||
| 6528bbdbc6 | |||
| 23852de607 | |||
| 9cf66a0e02 | |||
| ad19e0fb57 | |||
| 14f222bf6f | |||
| 95141d1a42 | |||
| 93aaf72372 | |||
| 47a6612ed0 | |||
| fe48af8bf4 | |||
| 78ca4f8762 | |||
| a7e08aa279 | |||
| e2980d616f | |||
| 96f2090d9e | |||
| 8a86a114db | |||
| cb757da118 | |||
| 31711e079a | |||
| 16ebb83ae6 | |||
| 65304b2645 | |||
| b44c6a104b | |||
| a8e5876f83 | |||
| 2d9d1e71c0 | |||
| 475bdfed50 | |||
| c36f12ccb2 | |||
| 0b8b5b8adf | |||
| e981c97c71 | |||
| 748461f3c6 | |||
| a51f55dd66 | |||
| 7ba2c59265 | |||
| dcc833a957 | |||
| 8c35af6554 | |||
| 70fe60a45d | |||
| 98c64fc37a | |||
| efa88194a1 | |||
| f1a5fb18e9 | |||
| 084f79c51a | |||
| 2561b1c43a | |||
| a0b20408fd | |||
| 5f3bf23a6a | |||
| 4badd0d001 | |||
| 383eda1f2b | |||
| 96edd6c825 | |||
| 4c4b2b02e2 | |||
| 428a0ec92e | |||
| b0679ae5d8 | |||
| bf3fe1ffcd | |||
| 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 |
@@ -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
|
||||
@@ -33,3 +33,5 @@ node_modules
|
||||
tmp/
|
||||
test-tmp/
|
||||
blob-local-storage/
|
||||
src/seeds/nn/hash.txt
|
||||
src/seeds/pipeline/hash.txt
|
||||
|
||||
+1
-2
@@ -2,5 +2,4 @@ language: node_js
|
||||
services: mongodb
|
||||
sudo: false
|
||||
node_js:
|
||||
- "4.1.1"
|
||||
- "4.2.5"
|
||||
- "6.2.1"
|
||||
|
||||
+22
-6
@@ -1,10 +1,19 @@
|
||||
[](https://img.shields.io/badge/state-alpha-orange.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.
|
||||
DeepForge is an open-source visual development environment for deep learning. Currently, it supports Convolutional Neural Networks, RNNs and LSTMs as well as the creation of custom layers. Additional features include:
|
||||
- Graphical architecture editor
|
||||
- Training/testing pipeline creation
|
||||
- Distributed pipeline execution
|
||||
- Real-time pipeline feedback
|
||||
- Collaborative editing
|
||||
- Automatic version control.
|
||||
|
||||
## Quick Start
|
||||
Simply run the following command to install deepforge with its dependencies:
|
||||
@@ -13,12 +22,19 @@ Simply run the following command to install deepforge with its dependencies:
|
||||
curl -o- https://raw.githubusercontent.com/dfst/deepforge/master/install.sh | bash
|
||||
```
|
||||
|
||||
Next, follow the postinstall instructions to start MongoDB and DeepForge!
|
||||
Or, if you already have NodeJS (v6) installed, simply run
|
||||
|
||||
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).
|
||||
```
|
||||
npm install -g deepforge
|
||||
```
|
||||
|
||||
## 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).
|
||||
Next, start deepforge with `deepforge start`!
|
||||
|
||||
Finally, navigate to [http://localhost:8888](http://localhost:8888) to start using DeepForge! For more, detailed instructions, check out the [wiki](https://github.com/dfst/deepforge/wiki/Installation-Guide).
|
||||
|
||||
Also, be sure to check out the other available features of the `deepforge` cli; it can be used to update, manage your torch installation, uninstall deepforge and run individual components!
|
||||
|
||||
## 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/)
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
|
||||
var gmeConfig = require('./config'),
|
||||
webgme = require('webgme'),
|
||||
path = require('path'),
|
||||
rm_rf = require('rimraf'),
|
||||
myServer;
|
||||
|
||||
webgme.addToRequireJsPaths(gmeConfig);
|
||||
|
||||
// Clear seed hash info
|
||||
['nn', 'pipeline'].map(lib => path.join(__dirname, 'src', 'seeds', lib, 'hash.txt'))
|
||||
.forEach(file => rm_rf.sync(file));
|
||||
|
||||
myServer = new webgme.standaloneServer(gmeConfig);
|
||||
myServer.start(function (err) {
|
||||
if (err) {
|
||||
|
||||
@@ -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,18 @@
|
||||
{
|
||||
"torch": {
|
||||
"dir": "~/.deepforge/torch"
|
||||
},
|
||||
"blob": {
|
||||
"dir": "~/.deepforge/blob"
|
||||
},
|
||||
"worker": {
|
||||
"cache": {
|
||||
"useBlob": true,
|
||||
"dir": "~/.deepforge/worker/cache"
|
||||
},
|
||||
"dir": "~/.deepforge/worker"
|
||||
},
|
||||
"mongo": {
|
||||
"dir": "~/.deepforge/data"
|
||||
}
|
||||
}
|
||||
Arquivo executável
+459
@@ -0,0 +1,459 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var Command = require('commander').Command,
|
||||
program = new Command(),
|
||||
childProcess = require('child_process'),
|
||||
rawSpawn = childProcess.spawn,
|
||||
Q = require('q'),
|
||||
execSync = childProcess.execSync,
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
version = require('../package.json').version,
|
||||
exists = require('exists-file'),
|
||||
DEFAULT_CONFIG = require('./config.json'),
|
||||
merge = require('lodash.merge'),
|
||||
config,
|
||||
|
||||
configDir = path.join(process.env.HOME, '.deepforge'),
|
||||
configPath = path.join(configDir, 'config.json'),
|
||||
dataPath = path.join(configDir, 'data'),
|
||||
|
||||
localConfig,
|
||||
rm_rf = require('rimraf'),
|
||||
p = dir => {
|
||||
if (typeof dir === 'string') {
|
||||
return dir.replace(/^~/, process.env.HOME); // resolve '~' to '$HOME'
|
||||
}
|
||||
return dir;
|
||||
};
|
||||
|
||||
// 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 = merge({}, DEFAULT_CONFIG, localConfig);
|
||||
|
||||
var getConfigValue = function(id, srcConfig) {
|
||||
var keys = id.split('.'),
|
||||
value = srcConfig || config;
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (!value.hasOwnProperty(keys[i])) {
|
||||
return null;
|
||||
}
|
||||
value = value[keys[i]];
|
||||
}
|
||||
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),
|
||||
expType = typeof getConfigValue(id, DEFAULT_CONFIG);
|
||||
|
||||
// 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]];
|
||||
}
|
||||
|
||||
if (expType !== 'string') {
|
||||
try { // try to downcast
|
||||
value = JSON.parse(value);
|
||||
} catch (e) {
|
||||
console.log(`Invalid value: "${value}" (expected ${expType})`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
currentObj[lastKey] = value;
|
||||
fs.writeFileSync(configPath, JSON.stringify(localConfig, null, 2));
|
||||
return true;
|
||||
};
|
||||
|
||||
(function() { // Load config to env
|
||||
var envToConf = require('./envConfig.json');
|
||||
Object.keys(envToConf).forEach(env => {
|
||||
var cKey = envToConf[env];
|
||||
process.env[env] = process.env[env] || p(getConfigValue(cKey));
|
||||
});
|
||||
|
||||
// Special cases
|
||||
if (process.env.DEEPFORGE_WORKER_USE_BLOB === 'true' &&
|
||||
exists.sync(process.env.DEEPFORGE_BLOB_DIR)) {
|
||||
|
||||
process.env.DEEPFORGE_WORKER_CACHE = process.env.DEEPFORGE_BLOB_DIR + '/wg-content';
|
||||
}
|
||||
})();
|
||||
|
||||
program
|
||||
.version('v' + version)
|
||||
.description('Command line interface for managing deepforge');
|
||||
|
||||
var isLocalUri = function(protocol, uri) {
|
||||
return uri.indexOf(protocol + '://localhost') === 0 ||
|
||||
uri.indexOf(protocol + '://127.0.0.1') === 0;
|
||||
};
|
||||
|
||||
var checkMongo = function(args, notSilent) {
|
||||
// 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...');
|
||||
var match = mongoUri.match(/:([0-9]+)/),
|
||||
port = '80';
|
||||
|
||||
if (match) {
|
||||
port = match[1];
|
||||
}
|
||||
|
||||
startMongo(args, port, !notSilent);
|
||||
}
|
||||
} else if (notSilent) {
|
||||
console.log(`Cannot start remote mongo locally: ${mongoUri}`);
|
||||
} else {
|
||||
console.log(`Using remote mongo: ${mongoUri}`);
|
||||
}
|
||||
};
|
||||
|
||||
var startMongo = function(args, port, silent) {
|
||||
var opts = ['--dbpath', p(config.mongo.dir), '--port', port],
|
||||
job = rawSpawn('mongod', opts, {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 hasTorch = function() {
|
||||
var result = childProcess.spawnSync('th', ['--help']);
|
||||
return !result.error;
|
||||
};
|
||||
|
||||
var installTorchExtras = function() {
|
||||
// Check if rnn is installed
|
||||
var result = childProcess.spawnSync('luarocks', ['list', '--porcelain']),
|
||||
pkgs = result.stdout.toString().split('\n')
|
||||
.map(line => line.match(/^[a-zA-Z0-9]+/g))
|
||||
.map(m => m && m[0]);
|
||||
|
||||
if (pkgs.indexOf('rnn') === -1) {
|
||||
return spawn('luarocks', ['install', 'rnn']);
|
||||
} else {
|
||||
return Q();
|
||||
}
|
||||
};
|
||||
|
||||
var installTorch = function() {
|
||||
var tgtDir = p(config.torch.dir),
|
||||
args;
|
||||
|
||||
if (!hasTorch()) {
|
||||
// Try to install torch
|
||||
console.log(`Torch7 not found. Installing to ${tgtDir}...`);
|
||||
args = `clone https://github.com/torch/distro.git ${tgtDir} --recursive`.split(' ');
|
||||
|
||||
return spawn('git', args)
|
||||
.then(code => {
|
||||
if (code !== 0) {
|
||||
if (code === 128) {
|
||||
console.error(`${tgtDir} is not empty. ` +
|
||||
'Please empty it or change the torch directory:\n' +
|
||||
'\n deepforge config torch.dir NEW/TORCH/PATH\n');
|
||||
|
||||
}
|
||||
|
||||
throw `Torch install Failed with exit code ${code}`;
|
||||
} else { // continue installation
|
||||
process.chdir(tgtDir);
|
||||
return spawn('bash', ['install-deps'])
|
||||
.then(() => spawn('bash', ['install.sh'], true))
|
||||
.then(() => {
|
||||
storeConfig('torch.dir', tgtDir);
|
||||
console.log('Installed torch. Please close and ' +
|
||||
're-open your terminal to use DeepForge w/ ' +
|
||||
'torch support!');
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Q();
|
||||
}
|
||||
};
|
||||
|
||||
var spawn = function(cmd, args, opts) {
|
||||
var deferred = Q.defer(),
|
||||
job,
|
||||
spawnOpts = typeof opts === 'object' ? opts : null,
|
||||
forwardStdin = opts === true,
|
||||
isOpen = true,
|
||||
err;
|
||||
|
||||
args = args || [];
|
||||
job = spawnOpts ? rawSpawn(cmd, args, spawnOpts) : rawSpawn(cmd, args);
|
||||
job.stdout.on('data', data => process.stdout.write(data));
|
||||
job.stderr.on('data', data => process.stderr.write(data));
|
||||
job.on('close', code => {
|
||||
isOpen = false;
|
||||
if (err) {
|
||||
deferred.reject(err, code);
|
||||
} else {
|
||||
deferred.resolve(code);
|
||||
}
|
||||
});
|
||||
job.on('error', e => err = e);
|
||||
|
||||
if (forwardStdin) {
|
||||
process.stdin.on('data', data => {
|
||||
if (isOpen) {
|
||||
job.stdin.write(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
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');
|
||||
|
||||
if (args.port) {
|
||||
process.env.PORT = args.port;
|
||||
}
|
||||
|
||||
if (args.server) {
|
||||
checkMongo(args);
|
||||
main = path.join(__dirname, '..', 'app.js');
|
||||
spawn('node', [main]);
|
||||
}
|
||||
|
||||
if (args.worker) {
|
||||
if (hasTorch()) {
|
||||
installTorchExtras().then(() => {
|
||||
main = path.join(__dirname, 'start-worker.js');
|
||||
if (args.worker !== true) {
|
||||
spawn('node', [main, args.worker]);
|
||||
} else {
|
||||
spawn('node', [main]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
installTorch();
|
||||
}
|
||||
}
|
||||
|
||||
if (args.mongo) {
|
||||
checkMongo(args, true);
|
||||
}
|
||||
|
||||
if (!args.server && !args.worker && !args.mongo) {
|
||||
// Starting everything
|
||||
checkMongo(args);
|
||||
if (hasTorch()) {
|
||||
installTorchExtras().then(() => spawn('node', [main]));
|
||||
} else {
|
||||
installTorch();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// 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',
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
spawn('npm', ['install', '-g', pkg])
|
||||
.then(() => {
|
||||
console.log('Upgrade successful!');
|
||||
})
|
||||
.catch(code => console.log('Upgrade failed w/ error code: ' + code));
|
||||
}
|
||||
|
||||
if (args.torch || !args.server) {
|
||||
// Update torch
|
||||
if (hasTorch()) {
|
||||
// Upgrade torch
|
||||
console.log('Upgrading torch...');
|
||||
console.log(`Checking for torch in ${config.torch.dir}`);
|
||||
// Verify that torch is installed in the config's location
|
||||
if (!exists.sync(path.join(config.torch.dir, 'update.sh'))) {
|
||||
// config is incorrect!
|
||||
console.log('Could not find torch installation. Please update the deepforge config with:');
|
||||
console.log('');
|
||||
console.log(' deepforge config torch.dir ~/path/to/torch/install');
|
||||
console.log('');
|
||||
return;
|
||||
}
|
||||
|
||||
spawn('bash', ['./update.sh'], {cwd: p(config.torch.dir)})
|
||||
.catch(err => console.log('Upgrade failed w/ error code: ' + err.code))
|
||||
.then(() => {
|
||||
console.log('About to update rnn package...');
|
||||
// Update rnn
|
||||
return spawn('luarocks', ['install', 'rnn']);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Upgrade successful!');
|
||||
})
|
||||
.catch(code => console.log('Upgrade failed w/ error code: ' + code));
|
||||
} else {
|
||||
installTorch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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)}`);
|
||||
}
|
||||
rm_rf.sync(p(config.torch.dir));
|
||||
}
|
||||
|
||||
if (opts.clean) { // remove the .deepforge directory
|
||||
console.log('removing config and data files...');
|
||||
rm_rf.sync(p(config.mongo.dir));
|
||||
rm_rf.sync(p(configDir));
|
||||
}
|
||||
|
||||
if (!opts.torch || opts.clean) { // uninstall deepforge
|
||||
spawn('npm', ['uninstall', '-g', 'deepforge'])
|
||||
.then(() => console.log('deepforge has been uninstalled!'))
|
||||
.catch(() => 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('./bin/deepforge');
|
||||
cmds.unshift('node');
|
||||
program.parse(cmds);
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
program.parse(process.argv);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"DEEPFORGE_BLOB_DIR": "blob.dir",
|
||||
"DEEPFORGE_WORKER_CACHE": "worker.cache.dir",
|
||||
"DEEPFORGE_WORKER_DIR": "worker.dir",
|
||||
"DEEPFORGE_WORKER_USE_BLOB": "worker.cache.useBlob"
|
||||
}
|
||||
+11
-3
@@ -2,12 +2,20 @@
|
||||
var spawn = require('child_process').spawn,
|
||||
stdout = '',
|
||||
execJob,
|
||||
workerJob = null;
|
||||
path = require('path'),
|
||||
env = {cwd: path.join(__dirname, '..')},
|
||||
workerJob = null,
|
||||
gmeConfig = require(__dirname + '/../config');
|
||||
|
||||
// Set the cache to the blob
|
||||
if (gmeConfig.blob.type === 'FS') {
|
||||
process.env.DEEPFORGE_WORKER_CACHE = path.resolve(gmeConfig.blob.fsDir + '/wg-content');
|
||||
}
|
||||
|
||||
process.env.NODE_ENV = 'local';
|
||||
execJob = spawn('npm', [
|
||||
'start'
|
||||
]);
|
||||
], env);
|
||||
execJob.stdout.pipe(process.stdout);
|
||||
execJob.stderr.pipe(process.stderr);
|
||||
|
||||
@@ -15,7 +23,7 @@ execJob.stdout.on('data', function(chunk) {
|
||||
if (!workerJob) {
|
||||
stdout += chunk;
|
||||
if (stdout.indexOf('DeepForge') > -1) {
|
||||
workerJob = spawn('npm', ['run', 'worker']);
|
||||
workerJob = spawn('npm', ['run', 'worker'], env);
|
||||
workerJob.stdout.pipe(process.stdout);
|
||||
workerJob.stderr.pipe(process.stderr);
|
||||
workerJob.on('close', code => code && process.exit(code));
|
||||
|
||||
+36
-2
@@ -4,15 +4,42 @@ var path = require('path'),
|
||||
fs = require('fs'),
|
||||
childProcess = require('child_process'),
|
||||
spawn = childProcess.spawn,
|
||||
rm_rf = require('rimraf'),
|
||||
projectConfig = require(__dirname + '/../config'),
|
||||
executorSrc = path.join(__dirname, '..', 'node_modules', 'webgme', 'src',
|
||||
'server', 'middleware', 'executor', 'worker'),
|
||||
workerPath = path.join(__dirname, '..', 'src', 'worker'),
|
||||
id = Date.now(),
|
||||
workerRootPath = process.env.DEEPFORGE_WORKER_DIR || path.join(__dirname, '..', 'src', 'worker'),
|
||||
workerPath = path.join(workerRootPath, `worker_${id}`),
|
||||
workerConfigPath = path.join(workerPath, 'config.json'),
|
||||
workerTmp = path.join(workerPath, 'tmp'),
|
||||
address,
|
||||
config = {};
|
||||
|
||||
var createDir = function(dir) {
|
||||
try {
|
||||
fs.statSync(dir);
|
||||
} catch (e) {
|
||||
// Create dir
|
||||
fs.mkdirSync(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
createDir(workerRootPath);
|
||||
createDir(workerPath);
|
||||
createDir(workerTmp);
|
||||
|
||||
// Create sym link to the node_modules
|
||||
var modules = path.join(workerRootPath, 'node_modules');
|
||||
try {
|
||||
fs.statSync(modules);
|
||||
} catch (e) {
|
||||
// Create dir
|
||||
childProcess.spawnSync('ln', ['-s', `${__dirname}/../node_modules`, modules]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check torch support
|
||||
var result = childProcess.spawnSync('th', ['--help']);
|
||||
if (result.error) {
|
||||
@@ -22,7 +49,15 @@ if (result.error) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var cleanUp = function() {
|
||||
console.log('removing worker directory ', workerPath);
|
||||
rm_rf.sync(workerPath);
|
||||
};
|
||||
|
||||
var startExecutor = function() {
|
||||
process.on('SIGINT', cleanUp);
|
||||
process.on('uncaughtException', cleanUp);
|
||||
|
||||
// Start the executor
|
||||
var execJob = spawn('node', [
|
||||
'node_worker.js',
|
||||
@@ -42,7 +77,6 @@ var createConfigJson = function() {
|
||||
}
|
||||
|
||||
config[address] = {};
|
||||
// TODO: Check if the config already exists
|
||||
fs.writeFile(workerConfigPath, JSON.stringify(config), startExecutor);
|
||||
};
|
||||
|
||||
|
||||
+39
-28
@@ -1,10 +1,43 @@
|
||||
{
|
||||
"AutoViz": {
|
||||
"preloadIds": [
|
||||
"ArchEditor",
|
||||
"PipelineEditor",
|
||||
"OperationEditor",
|
||||
"ExecutionView"
|
||||
]
|
||||
},
|
||||
"ArchEditor": {
|
||||
"hotkeys": "none",
|
||||
"LayerColors": {}
|
||||
},
|
||||
"BreadcrumbHeader": {
|
||||
"pathRule": "history",
|
||||
"cachePrefix": "deepforge-header"
|
||||
},
|
||||
"FloatingActionButton": {
|
||||
"hideOnEmpty": true
|
||||
"hideOnEmpty": true,
|
||||
"pluginUIConfigs": {
|
||||
"GenerateArchitecture": {
|
||||
"icon": "description",
|
||||
"hotkey": "shift enter",
|
||||
"priority": -1
|
||||
},
|
||||
"ExecutePipeline": {
|
||||
"icon": "play_arrow",
|
||||
"hotkey": "shift enter",
|
||||
"color": "green",
|
||||
"priority": 1
|
||||
},
|
||||
"ImportTorch": {
|
||||
"icon": "import_export",
|
||||
"priority": -1
|
||||
},
|
||||
"GenerateExecFile": {
|
||||
"icon": "play_for_work",
|
||||
"priority": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericUIProjectNavigatorController": {
|
||||
"rootMenuClass": "deepforge-logo",
|
||||
@@ -43,48 +76,26 @@
|
||||
"nodes": [
|
||||
{
|
||||
"nodeName": "MyArchitectures",
|
||||
"title": "Architectures",
|
||||
"icon": "shuffle",
|
||||
"rank": 1,
|
||||
"description": "Neural network architectures are stored here and can be used in pipelines."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyOperations",
|
||||
"icon": "mode_edit",
|
||||
"rank": 2,
|
||||
"color": "blue-grey",
|
||||
"description": "Operations are the building blocks of pipelines. Custom operations can be created and stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyPipelines",
|
||||
"title": "Pipelines",
|
||||
"color": "blue-grey",
|
||||
"icon": "input",
|
||||
"rank": 3,
|
||||
"description": "Pipelines compose operations together to effectively train, test and/or ensemble models."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyLayers",
|
||||
"icon": "clear_all",
|
||||
"rank": 4,
|
||||
"color": "blue-grey",
|
||||
"description": "Custom torch layers can be created and stored here for use in neural network architectures."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyArtifacts",
|
||||
"title": "Artifacts",
|
||||
"icon": "view_quilt",
|
||||
"color": "blue-grey",
|
||||
"rank": 5,
|
||||
"description": "Artifacts from pipeline executions are stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyExecutions",
|
||||
"icon": "list",
|
||||
"rank": 6,
|
||||
"color": "blue-grey",
|
||||
"description": "Executions are read-only snapshots of pipelines that have been executed. Past and current executing pipelines are stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyDataTypes",
|
||||
"icon": "settings",
|
||||
"rank": 7,
|
||||
"description": "Custom defined data types are stored here."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,9 +7,12 @@ var config = require('./config.webgme'),
|
||||
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.blob.fsDir = process.env.DEEPFORGE_BLOB_DIR || config.blob.fsDir;
|
||||
|
||||
config.requirejsPaths.deepforge = './src/common';
|
||||
config.requirejsPaths.ace = './src/visualizers/widgets/TextEditor/lib/ace';
|
||||
config.seedProjects.defaultProject = 'project';
|
||||
|
||||
config.plugin.allowBrowserExecution = true;
|
||||
@@ -19,5 +22,7 @@ config.executor.enable = true;
|
||||
|
||||
config.visualization.extraCss.push('deepforge/styles/global.css');
|
||||
|
||||
config.storage.autoMerge.enable = true;
|
||||
|
||||
validateConfig(config);
|
||||
module.exports = config;
|
||||
|
||||
@@ -9,7 +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 = ['src/seeds/project'];
|
||||
config.seedProjects.basePaths = ['src/seeds/project', 'src/seeds/cifar10'];
|
||||
|
||||
validateConfig(config);
|
||||
module.exports = config;
|
||||
|
||||
@@ -7,7 +7,6 @@ var config = require('./config.default'),
|
||||
|
||||
// Turn up the worker polling rate
|
||||
config.executor.workerRefreshInterval = 150;
|
||||
config.executor.clearOldDataAtStartUp = true,
|
||||
|
||||
validateConfig(config);
|
||||
module.exports = config;
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var config = require('./config.default');
|
||||
var config = require('./config.default'),
|
||||
path = require('path');
|
||||
|
||||
config.server.port = 9001;
|
||||
config.mongo.uri = 'mongodb://127.0.0.1:27017/webgme_tests';
|
||||
config.blob.fsDir = path.join(__dirname, '..', 'test-tmp', 'blob');
|
||||
|
||||
module.exports = config;
|
||||
module.exports = config;
|
||||
|
||||
+22
-19
@@ -6,32 +6,35 @@
|
||||
var config = require('webgme/config/config.default'),
|
||||
validateConfig = require('webgme/config/validator');
|
||||
|
||||
|
||||
// The paths can be loaded from the webgme-setup.json
|
||||
config.plugin.basePaths.push('src/plugins');
|
||||
config.plugin.basePaths.push('node_modules/webgme-simple-nodes/src/plugins');
|
||||
config.visualization.layout.basePaths.push('node_modules/webgme-chflayout/src/layouts');
|
||||
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/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.plugin.basePaths.push(__dirname + '/../src/plugins');
|
||||
config.plugin.basePaths.push(__dirname + '/../node_modules/webgme-simple-nodes/src/plugins');
|
||||
config.visualization.layout.basePaths.push(__dirname + '/../node_modules/webgme-chflayout/src/layouts');
|
||||
config.visualization.decoratorPaths.push(__dirname + '/../src/decorators');
|
||||
config.visualization.decoratorPaths.push(__dirname + '/../node_modules/webgme-easydag/src/decorators');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/nn');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/devTests');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/devUtilTests');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/pipeline');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/devPipelineTests');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/project');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/cifar10');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/xor');
|
||||
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/devProject');
|
||||
|
||||
|
||||
|
||||
config.visualization.panelPaths.push('node_modules/webgme-fab/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push('node_modules/webgme-breadcrumbheader/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push('node_modules/webgme-autoviz/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push('node_modules/webgme-easydag/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push('src/visualizers/panels');
|
||||
config.visualization.panelPaths.push(__dirname + '/../node_modules/webgme-fab/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push(__dirname + '/../node_modules/webgme-breadcrumbheader/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push(__dirname + '/../node_modules/webgme-autoviz/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push(__dirname + '/../node_modules/webgme-easydag/src/visualizers/panels');
|
||||
config.visualization.panelPaths.push(__dirname + '/../src/visualizers/panels');
|
||||
|
||||
|
||||
config.rest.components['execution/logs'] = __dirname + '/../src/routers/JobLogsAPI/JobLogsAPI.js';
|
||||
|
||||
// Visualizer descriptors
|
||||
config.visualization.visualizerDescriptors.push('./src/visualizers/Visualizers.json');
|
||||
config.visualization.visualizerDescriptors.push(__dirname + '/../src/visualizers/Visualizers.json');
|
||||
// Add requirejs paths
|
||||
config.requirejsPaths = {
|
||||
'EllipseDecorator': 'node_modules/webgme-easydag/src/decorators/EllipseDecorator',
|
||||
|
||||
+33
-28
@@ -5,13 +5,6 @@
|
||||
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() {
|
||||
@@ -48,39 +41,51 @@ detect_profile() {
|
||||
}
|
||||
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
|
||||
|
||||
set_node_version() {
|
||||
# Install nodejs v6.2.0
|
||||
echo "Installing 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
|
||||
fi
|
||||
# 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
|
||||
|
||||
set_node_version
|
||||
}
|
||||
|
||||
# Check node version supports arrow fns and string templates
|
||||
node -e '() => console.log(`print "3": ${1+2}`)' >/dev/null 2>&1 || {
|
||||
echo "Unsupported version of NodeJS."
|
||||
echo ""
|
||||
echo "Please update NodeJS to version 4.x.x or later (6.x.x recommended)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo >&2 "Installing DeepForge...";
|
||||
|
||||
# Clone deepforge into ~/deepforge
|
||||
git clone https://github.com/dfst/deepforge ~/deepforge
|
||||
cd ~/deepforge
|
||||
npm install
|
||||
npm install -g deepforge
|
||||
|
||||
mkdir ~/deepforge/data 2> /dev/null
|
||||
echo "DeepForge is installed! To run it:"
|
||||
echo " 1) make sure MongoDB is running locally"
|
||||
echo " (start mongo w/ \"mongod --dbpath ~/deepforge/data\")"
|
||||
echo " 2) Run \"npm run local\" from ~/deepforge"
|
||||
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)"
|
||||
echo " Note: for custom installs, this may not be required"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Then run DeepForge!"
|
||||
echo " 1) run \"deepforge start\""
|
||||
echo " 2) open a browser to http://localhost:8888"
|
||||
echo " 3) start building neural nets!"
|
||||
|
||||
+18
-7
@@ -1,31 +1,42 @@
|
||||
{
|
||||
"name": "deepforge",
|
||||
"bin": {
|
||||
"deepforge": "./bin/deepforge"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"start-dev": "NODE_ENV=dev node app.js",
|
||||
"local": "node ./bin/start-local.js",
|
||||
"worker": "node ./bin/start-worker.js",
|
||||
"test": "node ./node_modules/mocha/bin/mocha --recursive test",
|
||||
"test": "mkdir ./test-tmp; node ./node_modules/mocha/bin/mocha --recursive test",
|
||||
"test-cover": "node node_modules/istanbul/lib/cli.js --hook-run-in-context cover node_modules/mocha/bin/_mocha -- -R spec 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.6.0",
|
||||
"version": "0.16.0",
|
||||
"dependencies": {
|
||||
"commander": "^2.9.0",
|
||||
"dotenv": "^2.0.0",
|
||||
"exists-file": "^2.1.0",
|
||||
"express": "^4.14.0",
|
||||
"lodash.difference": "^4.1.2",
|
||||
"lodash.merge": "^4.5.1",
|
||||
"nodemon": "^1.9.2",
|
||||
"q": "1.4.1",
|
||||
"rimraf": "^2.4.0",
|
||||
"webgme": "^2.0.0",
|
||||
"webgme-autoviz": "^2.0.3",
|
||||
"webgme-breadcrumbheader": "^2.0.0",
|
||||
"webgme-autoviz": "dfst/webgme-autoviz",
|
||||
"webgme-breadcrumbheader": "^2.1.1",
|
||||
"webgme-chflayout": "^2.0.0",
|
||||
"webgme-easydag": "dfst/webgme-easydag",
|
||||
"webgme-fab": "dfst/webgme-fab",
|
||||
"webgme-simple-nodes": "^2.0.0"
|
||||
"webgme-simple-nodes": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.0.0",
|
||||
"jszip": "^2.5.0",
|
||||
"mocha": "^2.2.5",
|
||||
"rimraf": "^2.4.0",
|
||||
"chai": "^3.0.0"
|
||||
"istanbul": "^0.4.5",
|
||||
"mockery": "^1.7.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* globals define */
|
||||
define({
|
||||
LINE_OFFSET: 'lineOffset',
|
||||
|
||||
// DeepForge metadata creation in dist execution
|
||||
START_CMD: 'deepforge-cmd',
|
||||
|
||||
IMAGE: { // all prefixed w/ 'IMG' for simple upload detection
|
||||
PREFIX: 'IMG',
|
||||
BASIC: 'IMG-B',
|
||||
CREATE: 'IMG-C',
|
||||
UPDATE: 'IMG-U',
|
||||
NAME: 'IMAGE-N' // No upload required
|
||||
},
|
||||
|
||||
GRAPH_CREATE: 'GRAPH',
|
||||
GRAPH_PLOT: 'PLOT',
|
||||
GRAPH_CREATE_LINE: 'LINE',
|
||||
|
||||
// Code Generation Constants
|
||||
CTOR_ARGS_ATTR: 'ctor_arg_order',
|
||||
|
||||
// Operation types
|
||||
OP: {
|
||||
INPUT: 'Input',
|
||||
OUTPUT: 'Output'
|
||||
},
|
||||
|
||||
// Job stdout update
|
||||
STDOUT_UPDATE: 'stdout_update'
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
/* globals define */
|
||||
define([
|
||||
'q',
|
||||
'superagent'
|
||||
], function(
|
||||
Q,
|
||||
superagent
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
// Wrap the ability to read, update, and delete logs using the JobLogsAPI
|
||||
var JobLogsClient = function(params) {
|
||||
params = params || {};
|
||||
|
||||
this.logger = params.logger.fork('JobLogsClient');
|
||||
|
||||
// Get the server url
|
||||
this.token = params.token;
|
||||
this.origin = this._getServerUrl(params);
|
||||
this.relativeUrl = '/execution/logs';
|
||||
this.url = this.origin + this.relativeUrl;
|
||||
|
||||
this.logger.debug(`Setting url to ${this.url}`);
|
||||
|
||||
// Get the project, branch name
|
||||
if (!(params.branchName && params.projectId)) {
|
||||
throw Error('"branchName" and "projectId" required');
|
||||
}
|
||||
this.branch = params.branchName;
|
||||
this.project = params.projectId;
|
||||
this._modifiedJobs = [];
|
||||
|
||||
this.logger.debug(`Using <project>:<branch>: "${this.project}"/"${this.branch}"`);
|
||||
this.logger.info('ctor finished');
|
||||
};
|
||||
|
||||
JobLogsClient.prototype._getServerUrl = function(params) {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
// If not in browser, set using the params
|
||||
var server = params.server || '127.0.0.1',
|
||||
port = params.port || '80',
|
||||
protocol = params.httpsecure ? 'https' : 'http'; // default is http
|
||||
|
||||
return params.origin || `${protocol}://${server}:${port}`;
|
||||
};
|
||||
|
||||
// This method could be optimized - it could make a log of requests
|
||||
JobLogsClient.prototype.fork = function(forkName) {
|
||||
var jobIds = this._modifiedJobs,
|
||||
deferred = Q.defer(),
|
||||
url = [
|
||||
this.url,
|
||||
'migrate',
|
||||
encodeURIComponent(this.project),
|
||||
encodeURIComponent(this.branch),
|
||||
encodeURIComponent(forkName)
|
||||
].join('/'),
|
||||
req = superagent.post(url);
|
||||
|
||||
this.logger.info(`migrating ${jobIds.length} jobs from ${this.branch} to ${forkName} in ${this.project}`);
|
||||
if (this.token) {
|
||||
req.set('Authorization', 'Bearer ' + this.token);
|
||||
}
|
||||
|
||||
req.send({jobs: jobIds})
|
||||
.end((err, res) => {
|
||||
if (err || res.status > 399) {
|
||||
return deferred.reject(err || res.status);
|
||||
}
|
||||
|
||||
return deferred.resolve(res);
|
||||
});
|
||||
|
||||
this.branch = forkName;
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
JobLogsClient.prototype.getUrl = function(jobId) {
|
||||
return [
|
||||
this.url,
|
||||
encodeURIComponent(this.project),
|
||||
encodeURIComponent(this.branch),
|
||||
encodeURIComponent(jobId)
|
||||
].join('/');
|
||||
};
|
||||
|
||||
JobLogsClient.prototype._logRequest = function(method, jobId, content) {
|
||||
var deferred = Q.defer(),
|
||||
req = superagent[method](this.getUrl(jobId));
|
||||
|
||||
this.logger.info(`sending ${method} request to ${this.getUrl(jobId)}`);
|
||||
if (this.token) {
|
||||
req.set('Authorization', 'Bearer ' + this.token);
|
||||
}
|
||||
|
||||
if (content) {
|
||||
req = req.send(content);
|
||||
}
|
||||
|
||||
req.end((err, res) => {
|
||||
if (err || res.status > 399) {
|
||||
return deferred.reject(err || res.status);
|
||||
}
|
||||
|
||||
return deferred.resolve(res);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
JobLogsClient.prototype.appendTo = function(jobId, text) {
|
||||
this._modifiedJobs.push(jobId);
|
||||
this.logger.info(`Appending logs to ${jobId}`);
|
||||
return this._logRequest('patch', jobId, {patch: text});
|
||||
};
|
||||
|
||||
JobLogsClient.prototype.getLog = function(jobId) {
|
||||
this.logger.info(`Getting logs for ${jobId}`);
|
||||
return this._logRequest('get', jobId)
|
||||
.then(res => res.text);
|
||||
};
|
||||
|
||||
JobLogsClient.prototype.deleteLog = function(jobId) {
|
||||
this.logger.info(`Deleting logs for ${jobId}`);
|
||||
return this._logRequest('delete', jobId);
|
||||
};
|
||||
|
||||
return JobLogsClient;
|
||||
});
|
||||
@@ -0,0 +1,380 @@
|
||||
/* globals define*/
|
||||
(function(root, factory){
|
||||
if(typeof define === 'function' && define.amd) {
|
||||
define(['./lua'], function(luajs){
|
||||
return (root.LayerParser = factory(luajs));
|
||||
});
|
||||
} else if(typeof module === 'object' && module.exports) {
|
||||
var luajs = require('./lua');
|
||||
module.exports = (root.LayerParser = factory(luajs));
|
||||
}
|
||||
}(this, function(luajs) {
|
||||
var LayerParser = {};
|
||||
|
||||
//////////////////////// Setters ////////////////////////
|
||||
var returnsSelf = function(fnNode){
|
||||
var stats = fnNode.block.stats,
|
||||
last = stats[stats.length-1];
|
||||
|
||||
if (last.type === 'stat.return') {
|
||||
return last.nret[0].type === 'variable' && last.nret[0].val === 'self';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var isAttrSetter = function(node){
|
||||
if (node.type === 'stat.assignment' && node.lefts.length === 1) {
|
||||
var left = node.lefts[0];
|
||||
return left.type === 'expr.index' && left.self.val === 'self';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var getSettingAttrName = function(node){
|
||||
if (isAttrSetter(node)) {
|
||||
var left = node.lefts[0];
|
||||
return left.key.val;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var getSettingAttrValue = function(node){
|
||||
if (isAttrSetter(node)) {
|
||||
return node.right;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var isSetterMethod = function(curr, parent, className){
|
||||
if (parent && parent.type === 'stat.method') {
|
||||
// is it a fn w/ two statements (stats)
|
||||
if (parent.self.val === className && curr.type === 'function' &&
|
||||
curr.block.stats.length === 2) {
|
||||
// Is the first statement setting a value?
|
||||
return returnsSelf(curr) && getSettingAttrName(curr.block.stats[0]); // does it return itself?
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var isFnArg = function(method, name) {
|
||||
return method.args.indexOf(name) !== -1;
|
||||
};
|
||||
|
||||
var getSetterSchema = function(node, method) {
|
||||
var setterType,
|
||||
setterFn,
|
||||
value = getSettingAttrValue(node);
|
||||
|
||||
if (value[0].type === 'variable' && isFnArg(method.func, value[0].val)) {
|
||||
setterType = 'arg';
|
||||
setterFn = method.key.val;
|
||||
} else {
|
||||
setterType = 'const';
|
||||
setterFn = {};
|
||||
setterFn[value[0].val] = method.key.val;
|
||||
}
|
||||
|
||||
return {
|
||||
setterType,
|
||||
setterFn
|
||||
};
|
||||
};
|
||||
|
||||
//////////////////////// Setters END ////////////////////////
|
||||
|
||||
var isInitFn = function(node, className) {
|
||||
if (node.type === 'stat.method' && node.self.val === className) {
|
||||
return node.key.val === '__init';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var getClassAttrDefs = function(method) {
|
||||
var fn = method.func,
|
||||
dict = {},
|
||||
attr,
|
||||
right,
|
||||
value;
|
||||
|
||||
luajs.codegen.traverse(curr => {
|
||||
if (isAttrSetter(curr)) {
|
||||
// Store the value if it is set to a constant
|
||||
attr = curr.lefts[0].key.val;
|
||||
right = curr.right[0];
|
||||
if (right.type.indexOf('const.') !== -1) {
|
||||
value = right.val;
|
||||
|
||||
if (right.type === 'const.nil') {
|
||||
value = null;
|
||||
}
|
||||
|
||||
dict[attr] = value;
|
||||
}
|
||||
}
|
||||
})(fn);
|
||||
|
||||
return dict;
|
||||
};
|
||||
|
||||
var getAttrsAndVals = function(method) {
|
||||
// Given a method, get the 'self' attributes and the default values
|
||||
var fn = method.func,
|
||||
dict = {},
|
||||
varName,
|
||||
value,
|
||||
varUsageCnt = {};
|
||||
|
||||
// Get the variables that are used only once (or updating themselves)
|
||||
luajs.codegen.traverse(curr => {
|
||||
if (curr.type === 'variable') {
|
||||
varUsageCnt[curr.val] = varUsageCnt[curr.val] ?
|
||||
varUsageCnt[curr.val] + 1 : 1;
|
||||
}
|
||||
})(method);
|
||||
|
||||
luajs.codegen.traverse(curr => {
|
||||
// If the variable is only used once and is 'or'-ed w/ a constant
|
||||
// during this use, we can infer that this is the default value
|
||||
if (curr.type === 'expr.op' && curr.op === 'op.or' &&
|
||||
curr.left.type === 'variable' && curr.right.type.indexOf('const') !== -1) {
|
||||
varName = curr.left.val;
|
||||
if (varUsageCnt[varName] === 1) {
|
||||
value = curr.right.type === 'const.nil' ? null : curr.right;
|
||||
dict[varName] = value;
|
||||
}
|
||||
}
|
||||
})(fn);
|
||||
|
||||
return dict;
|
||||
};
|
||||
|
||||
var copyNodeValues = function(attrs, from, to) {
|
||||
var value;
|
||||
for (var i = attrs.length; i--;) {
|
||||
value = from[attrs[i]] || null;
|
||||
if (value) {
|
||||
value = (value && value.hasOwnProperty('val')) ? value.val : value;
|
||||
to[attrs[i]] = value;
|
||||
}
|
||||
}
|
||||
return to;
|
||||
};
|
||||
|
||||
var getTypeCheckInfo = function(cond) {
|
||||
var caller,
|
||||
method,
|
||||
target,
|
||||
expType;
|
||||
|
||||
// Check for torch.isTypeOf:
|
||||
if (cond.type === 'expr.call' && cond.func.type === 'expr.index') {
|
||||
caller = cond.func.self.val;
|
||||
method = cond.func.key.val;
|
||||
|
||||
if (cond.type === 'expr.call' && caller === 'torch') {
|
||||
target = cond.args[0].val;
|
||||
if (method === 'isTypeOf' && target) {
|
||||
expType = cond.args[1].val;
|
||||
return {
|
||||
target,
|
||||
type: expType
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (cond.type === 'expr.op') { // torch.type() === ''
|
||||
// Check right side, too!
|
||||
var sides = [cond.left, cond.right],
|
||||
side,
|
||||
otherSide;
|
||||
|
||||
for (var i = sides.length; i--;) {
|
||||
side = sides[i];
|
||||
otherSide = sides[(i+1)%2];
|
||||
if (side.type === 'expr.call' && side.func.type === 'expr.index') {
|
||||
// Is it torch?
|
||||
caller = side.func.self.val;
|
||||
method = side.func.key.val;
|
||||
if (caller === 'torch' && method === 'type') {
|
||||
if (side.args[0].type === 'variable') {
|
||||
target = side.args[0].val;
|
||||
if (otherSide.type === 'const.string') {
|
||||
expType = otherSide.val;
|
||||
|
||||
return {
|
||||
target: target,
|
||||
type: expType
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var isError = function(stat) {
|
||||
var fn;
|
||||
if (stat.type === 'stat.expr' && stat.expr.type === 'expr.call') {
|
||||
fn = stat.expr.func.val;
|
||||
return fn === 'error';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var inferParamTypes = function(node, paramDefs) {
|
||||
var types = {},
|
||||
check,
|
||||
cond;
|
||||
|
||||
// Infer from assertions
|
||||
luajs.codegen.traverse(curr => {
|
||||
// check for 'assert's that check type
|
||||
if (curr.type === 'expr.call' && curr.func.val === 'assert') {
|
||||
cond = curr.args[0];
|
||||
check = getTypeCheckInfo(cond);
|
||||
if (check) {
|
||||
types[check.target] = check.type;
|
||||
}
|
||||
} else if (curr.type === 'stat.if' && curr.cond.op === 'uop.not') {
|
||||
// if statements throwing errors on type mismatch
|
||||
cond = curr.cond.operand; // non-negated version
|
||||
// Check that it throws an error on true
|
||||
if (curr.tblock.stats.some(isError)) {
|
||||
check = getTypeCheckInfo(cond);
|
||||
if (check) {
|
||||
types[check.target] = check.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
})(node);
|
||||
|
||||
// Infer from defaults
|
||||
Object.keys(paramDefs).forEach(param => {
|
||||
var val = paramDefs[param];
|
||||
if (val) { // initialized to 'null' doesn't help us...
|
||||
types[param] = val.type.replace('const.', '');
|
||||
}
|
||||
});
|
||||
|
||||
return types;
|
||||
};
|
||||
|
||||
var findTorchClass = function(ast){
|
||||
var torchClassArgs, // args for `torch.class(...)`
|
||||
name = '',
|
||||
baseType,
|
||||
params,
|
||||
setters = {},
|
||||
defaults = {},
|
||||
paramDefs,
|
||||
attrDefs;
|
||||
|
||||
if(ast.type == 'function'){
|
||||
ast.block.stats.forEach(function(func){
|
||||
if(func.type == 'stat.local' && func.right && func.right[0] &&
|
||||
func.right[0].func && func.right[0].func.self &&
|
||||
func.right[0].func.self.val == 'torch' &&
|
||||
func.right[0].func.key.val == 'class'){
|
||||
|
||||
torchClassArgs = func.right[0].args.map(arg => arg.val);
|
||||
name = torchClassArgs[0];
|
||||
if(name !== ''){
|
||||
name = name.replace('nn.', '');
|
||||
if (torchClassArgs.length > 1) {
|
||||
baseType = torchClassArgs[1].replace('nn.', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get the setters, defaults and type info (inferred)
|
||||
var setterNames,
|
||||
schema,
|
||||
types,
|
||||
values;
|
||||
|
||||
luajs.codegen.traverse((curr, parent) => {
|
||||
var firstLine,
|
||||
attrName;
|
||||
|
||||
// Record the setter functions
|
||||
if (isSetterMethod(curr, parent, name)) {
|
||||
firstLine = curr.block.stats[0];
|
||||
// just use the attribute attrName for now...
|
||||
attrName = getSettingAttrName(firstLine);
|
||||
|
||||
// merge schemas
|
||||
schema = getSetterSchema(firstLine, parent);
|
||||
if (setters[attrName] && setters[attrName].setterType === 'const') { // merge
|
||||
for (var val in schema.setterFn) {
|
||||
setters[attrName].setterFn[val] = schema.setterFn[val];
|
||||
}
|
||||
} else {
|
||||
setters[attrName] = schema;
|
||||
}
|
||||
} else if (isInitFn(curr, name)) { // Record the defaults
|
||||
paramDefs = getAttrsAndVals(curr);
|
||||
attrDefs = getClassAttrDefs(curr);
|
||||
types = inferParamTypes(curr, paramDefs);
|
||||
|
||||
// get ctor args
|
||||
params = curr.func.args;
|
||||
if(params.length === 0 && curr.func.varargs){
|
||||
params.push('params');
|
||||
}
|
||||
}
|
||||
|
||||
})(ast);
|
||||
|
||||
// Get the defaults for the params from defs
|
||||
if (paramDefs && params) {
|
||||
copyNodeValues(params, paramDefs, defaults);
|
||||
}
|
||||
|
||||
// Get the defaults for the setters from attrDefs
|
||||
if (attrDefs) {
|
||||
setterNames = Object.keys(setters);
|
||||
copyNodeValues(setterNames, attrDefs, defaults);
|
||||
}
|
||||
|
||||
// Remove any const setters w/ only one value and no default
|
||||
setterNames = Object.keys(setters);
|
||||
for (var i = setterNames.length; i--;) {
|
||||
schema = setters[setterNames[i]];
|
||||
if (schema.setterType === 'const') {
|
||||
values = Object.keys(schema.setterFn);
|
||||
if (values.length === 1 &&
|
||||
// boolean setters can have the default value inferred
|
||||
values[0] !== 'true' && values[0] !== 'false' &&
|
||||
!defaults[setterNames[i]]) {
|
||||
|
||||
delete setters[setterNames[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
baseType,
|
||||
params,
|
||||
setters,
|
||||
types,
|
||||
defaults
|
||||
};
|
||||
};
|
||||
|
||||
LayerParser.parse = function(text) {
|
||||
try {
|
||||
var ast = luajs.parser.parse(text);
|
||||
return findTorchClass(ast);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return LayerParser;
|
||||
}));
|
||||
@@ -0,0 +1,325 @@
|
||||
/* globals WebGMEGlobal, define*/
|
||||
// This file creates the DeepForge namespace and defines basic actions
|
||||
define([
|
||||
'panel/FloatingActionButton/styles/Materialize',
|
||||
'js/RegistryKeys',
|
||||
'js/Panels/MetaEditor/MetaEditorConstants',
|
||||
'js/Constants',
|
||||
'q'
|
||||
], function(
|
||||
Materialize,
|
||||
REGISTRY_KEYS,
|
||||
META_CONSTANTS,
|
||||
CONSTANTS,
|
||||
Q
|
||||
) {
|
||||
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 ////////////////////
|
||||
DeepForge.places = {};
|
||||
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 placePromises = {},
|
||||
setPlaceId = {},
|
||||
firstProject = true;
|
||||
|
||||
var getPlace = function(name) {
|
||||
return placePromises[name];
|
||||
};
|
||||
|
||||
var initializePlaces = function() {
|
||||
PLACE_NAMES.forEach(name => {
|
||||
var deferred = Q.defer();
|
||||
placePromises[name] = deferred.promise;
|
||||
setPlaceId[name] = deferred.resolve;
|
||||
});
|
||||
};
|
||||
|
||||
var updateDeepForgeNamespace = function() {
|
||||
var territory = {};
|
||||
|
||||
if (!firstProject) {
|
||||
initializePlaces();
|
||||
}
|
||||
firstProject = false;
|
||||
|
||||
// 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 => setPlaceId[name](nodeIdsByName[name]));
|
||||
|
||||
// Remove the territory
|
||||
client.removeUI(placesTerritoryId);
|
||||
placesTerritoryId = null;
|
||||
};
|
||||
|
||||
initializePlaces();
|
||||
PLACE_NAMES.forEach(name => DeepForge.places[name] = getPlace.bind(null, name));
|
||||
|
||||
//////////////////// DeepForge creation actions ////////////////////
|
||||
var instances = [
|
||||
'Architecture',
|
||||
'Pipeline'
|
||||
],
|
||||
metaNodes = [
|
||||
'Operation',
|
||||
'Primitive',
|
||||
'Complex'
|
||||
];
|
||||
|
||||
var createNew = function(type, metasheetName) {
|
||||
var 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
|
||||
DeepForge.places[placeName]().then(parentId => {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
return DeepForge.places.MyLayers()
|
||||
.then(id => {
|
||||
|
||||
client.startTransaction(msg);
|
||||
|
||||
newId = createNamedNode(baseId, id, 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;
|
||||
});
|
||||
+30
-11
@@ -1,6 +1,8 @@
|
||||
/* globals define*/
|
||||
define([
|
||||
'deepforge/Constants'
|
||||
], function(
|
||||
Constants
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
@@ -15,32 +17,49 @@ define([
|
||||
return result;
|
||||
};
|
||||
|
||||
var isArgument = function(arg) {
|
||||
return arg.hasOwnProperty('argindex');
|
||||
};
|
||||
|
||||
var sortByIndex = function(a, b) {
|
||||
return a.argindex > b.argindex;
|
||||
var isSetter = function(arg) {
|
||||
return arg.hasOwnProperty('setterType');
|
||||
};
|
||||
|
||||
var createLayerDict = function(core, meta) {
|
||||
var node,
|
||||
names = Object.keys(meta),
|
||||
layers = {};
|
||||
layers = {},
|
||||
setters,
|
||||
ctorData,
|
||||
ctorArgs,
|
||||
attrs;
|
||||
|
||||
for (var i = names.length; i--;) {
|
||||
node = meta[names[i]];
|
||||
layers[names[i]] = core.getValidAttributeNames(node)
|
||||
ctorData = core.getAttribute(node, Constants.CTOR_ARGS_ATTR);
|
||||
attrs = core.getValidAttributeNames(node);
|
||||
|
||||
layers[names[i]] = {};
|
||||
if (ctorData) {
|
||||
ctorArgs = ctorData.split(',')
|
||||
.map(attr => prepAttribute(core, node, attr));
|
||||
|
||||
// Get the constructor args
|
||||
layers[names[i]].args = ctorArgs;
|
||||
} else {
|
||||
layers[names[i]].args = [];
|
||||
}
|
||||
|
||||
layers[names[i]].setters = {};
|
||||
setters = attrs
|
||||
.map(attr => prepAttribute(core, node, attr))
|
||||
.filter(isArgument)
|
||||
.sort(sortByIndex);
|
||||
.filter(isSetter);
|
||||
for (var j = setters.length; j--;) {
|
||||
layers[names[i]].setters[setters[j].name] = setters[j];
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
};
|
||||
|
||||
// When provided with the META, create the given LayerDict object
|
||||
// - Sort (and filter) by argindex
|
||||
// - Filter out the ctor args (in order)
|
||||
// - add name attribute to schema
|
||||
// - store this array under the META name
|
||||
|
||||
|
||||
@@ -1,546 +0,0 @@
|
||||
# This file should actually be an alternative way of viewing the metamodel.
|
||||
#
|
||||
# This contains metadata about the Torch nn library used for
|
||||
# creating the metamodel
|
||||
#
|
||||
# By default...
|
||||
# - all attributes are a number
|
||||
# - default values are optional
|
||||
# - all booleans default to false
|
||||
# - list attributes are specified with WORD...WORD
|
||||
# - if `ignore` is set, the attribute is not added to the metamodel
|
||||
|
||||
# This should have tests to verify that this document is up to date...
|
||||
# TODO
|
||||
|
||||
Containers:
|
||||
- Concat:
|
||||
- dim:
|
||||
min: 1 # TODO: Figure out exactly how this works
|
||||
|
||||
Module:
|
||||
- SpatialBatchNormalization:
|
||||
- input:
|
||||
infer: dimensionality # change this to `infer: 'dimensionality'`
|
||||
- eps:
|
||||
default: 0.00001
|
||||
- momentum:
|
||||
default: 0.1
|
||||
- affine:
|
||||
default: true
|
||||
|
||||
- BatchNormalization:
|
||||
- input:
|
||||
infer: dimensionality # change this to `infer: 'dimensionality'`
|
||||
- eps:
|
||||
default: 0.00001
|
||||
- momentum:
|
||||
default: 0.1
|
||||
- affine:
|
||||
default: true
|
||||
|
||||
- Threshold:
|
||||
- threshold:
|
||||
type: float
|
||||
default: 1e-6
|
||||
- value:
|
||||
type: float
|
||||
default: 0
|
||||
- inplace:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
ConvLayer:
|
||||
- TemporalConvolution:
|
||||
- inputFrameSize:
|
||||
min: 1
|
||||
- outputFrameSize:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- step:
|
||||
default: 1
|
||||
|
||||
- TemporalMaxPooling:
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- step: # FIXME: defaults to 'kernelWidth'
|
||||
min: 1
|
||||
|
||||
- TemporalSubSampling:
|
||||
- inputFrameSize:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- step:
|
||||
min: 1
|
||||
# TODO: What is the default?
|
||||
|
||||
- LookupTable:
|
||||
- nIndex:
|
||||
min: 1
|
||||
- sizes:
|
||||
min: 1
|
||||
|
||||
# Spatial Modules
|
||||
- SpatialConvolutionMM:
|
||||
- nInputPlane: # TODO: Infer this
|
||||
min: 1
|
||||
- nOutputPlane:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
- padWidth:
|
||||
min: 0
|
||||
default: 0
|
||||
- padHeight: # FIXME: this defaults to padWidth - not 0
|
||||
min: 0
|
||||
default: 0
|
||||
|
||||
- SpatialConvolution:
|
||||
- nInputPlane:
|
||||
min: 1
|
||||
- nOutputPlane:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
|
||||
- SpatialConvolutionMap:
|
||||
- connectionMatrix:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
|
||||
- SpatialLPPooling:
|
||||
- nInputPlane:
|
||||
min: 1
|
||||
- norm:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
|
||||
- SpatialMaxPooling:
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
|
||||
- SpatialAveragePooling:
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
|
||||
- SpatialAdaptiveMaxPooling: # output is width x height
|
||||
- width:
|
||||
min: 1
|
||||
- height:
|
||||
min: 1
|
||||
|
||||
- SpatialSubSampling:
|
||||
- nInputPlane:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
|
||||
- SpatialUpSamplingNearest:
|
||||
- scale: # upscale ratio
|
||||
min: 1
|
||||
|
||||
- SpatialZeroPadding:
|
||||
- left:
|
||||
min: 0
|
||||
- right:
|
||||
min: 0
|
||||
- top:
|
||||
min: 0
|
||||
- bottom:
|
||||
min: 0
|
||||
|
||||
- SpatialSubtractiveNormalization:
|
||||
- nInputPlane:
|
||||
min: 1
|
||||
- kernel:
|
||||
min: 1
|
||||
|
||||
- SpatialCrossMapLRN:
|
||||
- size:
|
||||
min: 1
|
||||
- alpha:
|
||||
default: 0.0001
|
||||
- beta:
|
||||
default: 0.75
|
||||
- k:
|
||||
default: 1
|
||||
|
||||
- SpatialConvolutionLocal:
|
||||
- nInputPlane:
|
||||
min: 1
|
||||
- nOutputPlane:
|
||||
min: 1
|
||||
- inputWidth: # TODO: infer this
|
||||
min: 1
|
||||
- inputHeight: # TODO: infer this
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
- padWidth:
|
||||
min: 0
|
||||
default: 0
|
||||
- padHeight: # FIXME: this defaults to padWidth - not 0
|
||||
min: 0
|
||||
default: 0
|
||||
|
||||
- SpatialDropout:
|
||||
- probability:
|
||||
default: 0.5
|
||||
|
||||
- SpatialFractionalMaxPooling:
|
||||
- poolWidth:
|
||||
- min: 2
|
||||
- poolHeight:
|
||||
- min: 2
|
||||
- outWidth: # Optionally, these could be ratioW/H FIXME
|
||||
- min: 1
|
||||
- outHeight:
|
||||
- min: 1
|
||||
|
||||
- SpatialDivisiveNormalization:
|
||||
- nInputPlane: # TODO: infer this
|
||||
- default: 1
|
||||
- kernel: # TODO: this is a tensor type...
|
||||
- threshold:
|
||||
- default: 0.0001
|
||||
- thresval:
|
||||
- default: 0.0001 # FIXME: this defaults to "threshold"
|
||||
|
||||
- SpatialContrastiveNormalization:
|
||||
- nInputPlane: # TODO: infer this
|
||||
- default: 1
|
||||
- kernel: # TODO: this is a tensor type...
|
||||
- threshold:
|
||||
- default: 0.0001
|
||||
- thresval:
|
||||
- default: 0.0001 # FIXME: this defaults to "threshold"
|
||||
|
||||
- SpatialFullConvolution:
|
||||
- nInputPlane: # TODO: should infer this
|
||||
min: 1
|
||||
- nOutputPlane:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
- padWidth:
|
||||
min: 0
|
||||
default: 0
|
||||
- padHeight:
|
||||
min: 0
|
||||
default: 0
|
||||
- adjWidth:
|
||||
min: 0
|
||||
default: 0
|
||||
- adjHeight:
|
||||
min: 0
|
||||
default: 0
|
||||
# Additional constraint:
|
||||
|
||||
# Volumetric Modules
|
||||
- VolumetricConvolution:
|
||||
- nInputPlane:
|
||||
min: 1
|
||||
- nOutputPlane:
|
||||
min: 1
|
||||
- kernelTime:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideTime:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
- VolumetricMaxPooling:
|
||||
- kernelTime:
|
||||
min: 1
|
||||
- kernelWidth:
|
||||
min: 1
|
||||
- kernelHeight:
|
||||
min: 1
|
||||
- strideTime:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideWidth:
|
||||
min: 1
|
||||
default: 1
|
||||
- strideHeight:
|
||||
min: 1
|
||||
default: 1
|
||||
|
||||
SimpleLayer:
|
||||
- Linear: # FIXME: These should contain the actual args
|
||||
- input:
|
||||
infer: dimensionality
|
||||
- output:
|
||||
min: 1
|
||||
- SparseLinear:
|
||||
- input:
|
||||
infer: dimensionality
|
||||
- output:
|
||||
min: 1
|
||||
|
||||
- Dropout:
|
||||
- probability:
|
||||
type: float
|
||||
- Abs:
|
||||
- Add:
|
||||
- isScalar:
|
||||
type: boolean
|
||||
- Mul:
|
||||
- CMul:
|
||||
- size: null
|
||||
- Max:
|
||||
- dimension:
|
||||
min: 0
|
||||
- Min:
|
||||
- dimension:
|
||||
min: 0
|
||||
- Mean:
|
||||
- dimension:
|
||||
min: 0
|
||||
- Sum:
|
||||
- dimension:
|
||||
min: 0
|
||||
|
||||
- Euclidean:
|
||||
- output:
|
||||
min: 0
|
||||
- WeightedEuclidean:
|
||||
- output:
|
||||
min: 0
|
||||
- Identity:
|
||||
- Copy: # Casts types
|
||||
- inputType:
|
||||
type: string
|
||||
- outputType:
|
||||
type: string
|
||||
- forceCopy:
|
||||
type: boolean
|
||||
- Narrow:
|
||||
- dimension:
|
||||
min: 0
|
||||
- offset:
|
||||
min: 0
|
||||
- length:
|
||||
min: 0
|
||||
- Replicate:
|
||||
- nFeature:
|
||||
min: 0
|
||||
- Reshape:
|
||||
- dimensions:
|
||||
type: list
|
||||
- View:
|
||||
- sizes: # list
|
||||
type: list
|
||||
min: 0
|
||||
- Select:
|
||||
- dimensions:
|
||||
type: list
|
||||
- Exp
|
||||
- Square
|
||||
- Sqrt
|
||||
- Power:
|
||||
- p: null
|
||||
- MM:
|
||||
- transA:
|
||||
type: boolean
|
||||
- transB:
|
||||
type: boolean
|
||||
|
||||
TransferLayer:
|
||||
- HardTanh
|
||||
- HardShrink:
|
||||
- lambda:
|
||||
type: float
|
||||
- SoftShrink:
|
||||
- lambda:
|
||||
type: float
|
||||
- SoftMax
|
||||
- SoftMin
|
||||
- SoftPlus
|
||||
- SoftSign # Typo in the docs on this one
|
||||
- LogSigmoid
|
||||
- LogSoftMax # Also in Criterion?
|
||||
- Sigmoid
|
||||
- Tanh
|
||||
- ReLU
|
||||
- PReLU # Missing from docs
|
||||
- RReLU # Missing from docs
|
||||
- LeakyReLU # Missing from docs
|
||||
- AddConstant:
|
||||
- scalar:
|
||||
type: float
|
||||
- MulConstant:
|
||||
- scalar:
|
||||
type: float
|
||||
min: 1
|
||||
- inplace:
|
||||
default: false
|
||||
|
||||
Criterion:
|
||||
- BCECriterion
|
||||
- WeightedMSECriterion
|
||||
- SmoothL1Criterion
|
||||
- MSECriterion
|
||||
- AbsCriterion
|
||||
- MultiCriterion
|
||||
- DistKLDivCriterion
|
||||
- HingeEmbeddingCriterion
|
||||
- CriterionTable
|
||||
- MultiMarginCriterion
|
||||
- MultiLabelMarginCriterion
|
||||
- L1HingeEmbeddingCriterion
|
||||
- CosineEmbeddingCriterion
|
||||
- MarginRankingCriterion
|
||||
- CrossEntropyCriterion
|
||||
- MarginCriterion
|
||||
- ClassNLLCriterion
|
||||
- ParallelCriterion
|
||||
|
||||
MiscLayers:
|
||||
- Jacobian
|
||||
- ConcatTable
|
||||
- CMulTable
|
||||
- CAddTable
|
||||
- TanhShrink
|
||||
- Padding:
|
||||
- dim:
|
||||
- pad:
|
||||
min: 0
|
||||
- nInputDim: # TODO: infer?
|
||||
min: 1
|
||||
- value:
|
||||
min: 0
|
||||
default: 0
|
||||
|
||||
# TODO: Add the following layers
|
||||
#VolumetricMaxUnpooling
|
||||
# Takes a poolingModule as an arg...
|
||||
|
||||
#MixtureTable
|
||||
#NarrowTable
|
||||
#SplitTable
|
||||
#DotProduct
|
||||
#DepthConcat
|
||||
#Parallel
|
||||
#Log
|
||||
#hessian
|
||||
#ELU
|
||||
#CSubTable
|
||||
#VolumetricAveragePooling
|
||||
#StochasticGradient
|
||||
#Bilinear
|
||||
#VolumetricFullConvolution
|
||||
#SparseJacobian
|
||||
#Contiguous
|
||||
#L1Cost
|
||||
#JoinTable
|
||||
#CosineDistance
|
||||
#Index
|
||||
#L1Penalty
|
||||
#Cosine
|
||||
#Clamp
|
||||
#SpatialConvolutionMM
|
||||
#LogSigmoid
|
||||
#ParallelTable
|
||||
#CDivTable
|
||||
#SpatialFullConvolutionMap
|
||||
#GradientReversal
|
||||
#SpatialMaxUnpooling
|
||||
#Transpose
|
||||
#Normalize
|
||||
#SpatialSoftMax
|
||||
#SelectTable
|
||||
#FlattenTable
|
||||
|
||||
# CONTAINERS and TableLayouts
|
||||
# Some of these are captured by the visual structure of the architecture and are not needed
|
||||
# as explicit layers in the metamodel
|
||||
#TableLayer:
|
||||
#- ConcatTable
|
||||
#Container:
|
||||
|
||||
@@ -2687,6 +2687,7 @@ function LuaContext(){
|
||||
}
|
||||
exports.stdlib(_G, helpers)();
|
||||
}
|
||||
this.__helpers = helpers;
|
||||
}
|
||||
|
||||
LuaContext.prototype = {}
|
||||
|
||||
+68
-31
@@ -2,14 +2,16 @@
|
||||
// This is an 'executor' containing the implementations of all local operations
|
||||
// These are all primitives in DeepForge
|
||||
define([
|
||||
'deepforge/Constants'
|
||||
], function(
|
||||
CONSTANTS
|
||||
) {
|
||||
'use strict';
|
||||
var LocalExecutor = function() {
|
||||
};
|
||||
|
||||
// Should these be in lua?
|
||||
LocalExecutor.prototype.ArtifactLoader = function(node) {
|
||||
LocalExecutor.prototype[CONSTANTS.OP.INPUT] = function(node) {
|
||||
// Get the hash from the output node
|
||||
var hash;
|
||||
return this.core.loadChildren(node)
|
||||
@@ -18,13 +20,13 @@ define([
|
||||
var output = cntrs
|
||||
.find(cntr => {
|
||||
var metaNode = this.core.getMetaType(cntr),
|
||||
metaName = this.core.getAttribute(metaNode, 'name');
|
||||
metaName = this.getAttribute(metaNode, 'name');
|
||||
return metaName === 'Outputs';
|
||||
});
|
||||
return this.core.loadChildren(output);
|
||||
})
|
||||
.then(dataNodes => {
|
||||
hash = this.core.getAttribute(dataNodes[0], 'data');
|
||||
hash = this.getAttribute(dataNodes[0], 'data');
|
||||
return this.getOutputs(node);
|
||||
})
|
||||
.then(outputTuples => {
|
||||
@@ -46,7 +48,7 @@ define([
|
||||
var hash,
|
||||
typeId = this.core.getPointerPath(node, 'type'),
|
||||
type,
|
||||
artifactName = this.core.getAttribute(node, 'artifactName');
|
||||
artifactName = this.getAttribute(node, 'artifactName');
|
||||
|
||||
return this.core.loadByPath(this.rootNode, typeId)
|
||||
.then(_type => {
|
||||
@@ -56,25 +58,20 @@ define([
|
||||
.then(saveDir => this.core.loadChildren(saveDir))
|
||||
.then(artifacts => {
|
||||
return artifacts.find(artifact =>
|
||||
this.core.getAttribute(artifact, 'name') === artifactName &&
|
||||
this.getAttribute(artifact, 'name') === artifactName &&
|
||||
this.isMetaTypeOf(artifact, type));
|
||||
})
|
||||
.then(matchingArtifact => {
|
||||
hash = matchingArtifact && this.core.getAttribute(matchingArtifact, 'data');
|
||||
// If no hash, just
|
||||
hash = matchingArtifact && this.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(outputTuples => {
|
||||
var outputs = outputTuples.map(tuple => tuple[2]),
|
||||
paths;
|
||||
|
||||
paths = outputs.map(output => this.core.getPath(output));
|
||||
.then(outputPairs => {
|
||||
var outputs = outputPairs.map(pair => pair[2]);
|
||||
// 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));
|
||||
outputs.forEach(output => this.setAttribute(output, 'data', hash));
|
||||
|
||||
this.onOperationComplete(node);
|
||||
});
|
||||
@@ -97,7 +94,7 @@ define([
|
||||
|
||||
if (containers.length > 1) {
|
||||
saveDir = containers.find(c =>
|
||||
this.core.getAttribute(c, 'name').toLowerCase().indexOf('artifacts') > -1
|
||||
this.getAttribute(c, 'name').toLowerCase().indexOf('artifacts') > -1
|
||||
) || containers[0];
|
||||
}
|
||||
|
||||
@@ -105,22 +102,31 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
LocalExecutor.prototype.Save = function(node) {
|
||||
var nodeId = this.core.getPath(node),
|
||||
parentNode;
|
||||
LocalExecutor.prototype[CONSTANTS.OP.OUTPUT] = function(node) {
|
||||
var parentNode,
|
||||
currNameHashPairs;
|
||||
|
||||
// Get the input node
|
||||
this.logger.info('Calling save operation!');
|
||||
return this._getSaveDir()
|
||||
.then(_saveDir => {
|
||||
parentNode = _saveDir;
|
||||
return this.core.loadChildren(_saveDir);
|
||||
})
|
||||
.then(artifacts => {
|
||||
currNameHashPairs = artifacts
|
||||
.map(node => [
|
||||
this.getAttribute(node, 'name'),
|
||||
this.getAttribute(node, 'data')
|
||||
]);
|
||||
return this.getInputs(node);
|
||||
})
|
||||
.then(inputs => {
|
||||
var ids = inputs.map(i => this.core.getPath(i[2])),
|
||||
allDataNodes,
|
||||
dataNodes;
|
||||
|
||||
dataNodes = Object.keys(this.nodes)
|
||||
allDataNodes = Object.keys(this.nodes)
|
||||
.map(id => this.nodes[id])
|
||||
.filter(node => this.isMetaTypeOf(node, this.META.Transporter))
|
||||
.filter(node =>
|
||||
@@ -129,29 +135,60 @@ define([
|
||||
.map(node => this.core.getPointerPath(node, 'src'))
|
||||
.map(id => this.nodes[id]);
|
||||
|
||||
// Remove nodes that already exist
|
||||
dataNodes = allDataNodes.filter(dataNode => {
|
||||
var hash = this.getAttribute(dataNode, 'data'),
|
||||
name = this.core.getOwnAttribute(node, 'saveName') ||
|
||||
this.getAttribute(dataNode, 'name');
|
||||
|
||||
return !(currNameHashPairs
|
||||
.find(pair => pair[0] === name && pair[1] === hash));
|
||||
});
|
||||
|
||||
// get the input node
|
||||
if (dataNodes.length === 0) {
|
||||
this.logger.error(`Could not find data to save! ${nodeId}`);
|
||||
} else {
|
||||
if (dataNodes.length !== 0) {
|
||||
var newNodes = this.core.copyNodes(dataNodes, parentNode),
|
||||
newName = this.core.getOwnAttribute(node, 'saveName');
|
||||
if (newName) {
|
||||
newNodes.forEach(node =>
|
||||
this.core.setAttribute(node, 'name', newName)
|
||||
this.setAttribute(node, 'name', newName)
|
||||
);
|
||||
}
|
||||
var hashes = dataNodes.map(n => this.getAttribute(n, 'data'));
|
||||
this.logger.info(`saving hashes: ${hashes.map(h => `"${h}"`)}`);
|
||||
} else if (allDataNodes.length === 0) {
|
||||
this.logger.warn('No data nodes found!');
|
||||
} else {
|
||||
this.logger.info('Using cached artifact(s)');
|
||||
}
|
||||
var hashes = dataNodes.map(n => this.core.getAttribute(n, 'data'));
|
||||
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;
|
||||
});
|
||||
@@ -1,7 +1,9 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*globals define, requirejs*/
|
||||
define([
|
||||
'plugin/util',
|
||||
'q'
|
||||
], function(
|
||||
PluginUtils,
|
||||
Q
|
||||
) {
|
||||
var PtrCodeGen = function() {
|
||||
@@ -14,34 +16,67 @@ define([
|
||||
var metanode = this.core.getMetaType(ptrNode),
|
||||
pluginId;
|
||||
|
||||
this.logger.debug(`loaded pointer target of ${ptrId}: ${ptrNode}`);
|
||||
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);
|
||||
var context = {
|
||||
namespace: this.core.getNamespace(metanode),
|
||||
activeNode: this.core.getPath(ptrNode)
|
||||
};
|
||||
|
||||
// Load and run the plugin
|
||||
return Q.nfcall(this.executePlugin.bind(this), pluginId, context);
|
||||
return this.executePlugin(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);
|
||||
PtrCodeGen.prototype.createPlugin = function(pluginId) {
|
||||
var deferred = Q.defer(),
|
||||
pluginPath = [
|
||||
'plugin',
|
||||
pluginId,
|
||||
pluginId,
|
||||
pluginId
|
||||
].join('/');
|
||||
|
||||
requirejs([pluginPath], Plugin => {
|
||||
var plugin = new Plugin();
|
||||
deferred.resolve(plugin);
|
||||
}, err => {
|
||||
this.logger.error(`Could not load ${pluginId}: ${err}`);
|
||||
deferred.reject(err);
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
PtrCodeGen.prototype.configurePlugin = function(plugin, opts) {
|
||||
var logger = this.logger.fork(plugin.getName());
|
||||
|
||||
return PluginUtils.loadNodesAtCommitHash(
|
||||
this.project,
|
||||
this.core,
|
||||
this.commitHash,
|
||||
this.logger,
|
||||
opts
|
||||
).then(config => {
|
||||
plugin.initialize(logger, this.blobClient, this.gmeConfig);
|
||||
config.core = this.core;
|
||||
plugin.configure(config);
|
||||
return plugin;
|
||||
});
|
||||
};
|
||||
|
||||
PtrCodeGen.prototype.executePlugin = function(pluginId, config) {
|
||||
return this.createPlugin(pluginId)
|
||||
.then(plugin => this.configurePlugin(plugin, config))
|
||||
.then(plugin => {
|
||||
return Q.ninvoke(plugin, 'main');
|
||||
})
|
||||
.then(result => {
|
||||
this.logger.info('Finished calling ' + pluginId);
|
||||
return result.artifacts;
|
||||
});
|
||||
};
|
||||
|
||||
return PtrCodeGen;
|
||||
|
||||
@@ -1,20 +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;
|
||||
li.deepforge-logo {
|
||||
background-image: url(img/deepforge-logo.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
width: 100px;
|
||||
background-size: 95px;
|
||||
}
|
||||
|
||||
.deepforge-logo .item-label {
|
||||
font-family: 'Audiowide', cursive;
|
||||
li.deepforge-logo span {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
i.gme-icon {
|
||||
background-image: url(img/deepforge-icon.png);
|
||||
background-size: 15.20px 18px;
|
||||
}
|
||||
|
||||
.create-node text {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.job-canceled {
|
||||
background-color: #ffe0b2;
|
||||
}
|
||||
|
||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 2.0 KiB |
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 3.3 KiB |
@@ -0,0 +1,93 @@
|
||||
/* globals define*/
|
||||
(function(root, factory){
|
||||
if(typeof define === 'function' && define.amd) {
|
||||
define([], function(){
|
||||
return (root.utils = factory());
|
||||
});
|
||||
} else if(typeof module === 'object' && module.exports) {
|
||||
module.exports = (root.utils = factory());
|
||||
}
|
||||
}(this, function() {
|
||||
var isBoolean = txt => {
|
||||
return typeof txt === 'boolean' || (txt === 'false' || txt === 'true');
|
||||
};
|
||||
|
||||
var getSetterSchema = function(name, setters, defaults) {
|
||||
var values,
|
||||
schema = setters[name];
|
||||
|
||||
if (defaults.hasOwnProperty(name)) {
|
||||
schema.default = defaults[name];
|
||||
}
|
||||
schema.type = 'string';
|
||||
if (schema.setterType === 'const') {
|
||||
values = Object.keys(schema.setterFn);
|
||||
schema.isEnum = true;
|
||||
schema.enumValues = values;
|
||||
if (values.every(isBoolean)) {
|
||||
if (!defaults.hasOwnProperty(name) && values.length === 1) {
|
||||
// there is only a method to toggle the flag to true/false,
|
||||
// then the default must be the other one
|
||||
schema.default = values[0] === 'true' ? false : true;
|
||||
}
|
||||
|
||||
if (isBoolean(schema.default)) {
|
||||
schema.type = 'boolean';
|
||||
}
|
||||
}
|
||||
}
|
||||
return schema;
|
||||
};
|
||||
|
||||
var abbrWord = function(word) { // camelcase
|
||||
word = word.substring(0, 1).toUpperCase() + word.substring(1);
|
||||
return word.split(/[a-z]+/g).join('').toLowerCase();
|
||||
};
|
||||
|
||||
var abbrPhrase = function(words) { // dashes, spaces, underscores, etc
|
||||
return words.map(word => word[0]).join('');
|
||||
};
|
||||
|
||||
var abbr = function(phrase) {
|
||||
var words = phrase.split(/[^a-zA-Z0-9]+/g);
|
||||
if (words.length === 1) {
|
||||
return abbrWord(phrase);
|
||||
} else {
|
||||
return abbrPhrase(words);
|
||||
}
|
||||
};
|
||||
|
||||
// Resolving stdout
|
||||
var resolveCarriageReturns = function(text) {
|
||||
// resolve \r
|
||||
var lines,
|
||||
chars,
|
||||
result,
|
||||
i = 0;
|
||||
|
||||
text = text.replace(/\u0000/g, '');
|
||||
lines = text.split('\n');
|
||||
for (var l = lines.length-1; l >= 0; l--) {
|
||||
i = 0;
|
||||
chars = lines[l].split('');
|
||||
result = [];
|
||||
|
||||
for (var c = 0; c < chars.length; c++) {
|
||||
if (chars[c] === '\r') {
|
||||
i = 0;
|
||||
}
|
||||
result[i] = chars[c];
|
||||
i++;
|
||||
}
|
||||
lines[l] = result.join('');
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
getSetterSchema: getSetterSchema,
|
||||
resolveCarriageReturns: resolveCarriageReturns,
|
||||
abbr: abbr
|
||||
};
|
||||
}));
|
||||
@@ -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,191 @@
|
||||
/* globals define, WebGMEGlobal */
|
||||
// Mixin for executing jobs and pipelines
|
||||
define([
|
||||
'q',
|
||||
'executor/ExecutorClient',
|
||||
'panel/FloatingActionButton/styles/Materialize'
|
||||
], function(
|
||||
Q,
|
||||
ExecutorClient,
|
||||
Materialize
|
||||
) {
|
||||
|
||||
var Execute = function(client, logger) {
|
||||
this.client = this.client || client;
|
||||
this.logger = this.logger || logger;
|
||||
this._executor = new ExecutorClient({
|
||||
logger: this.logger.fork('ExecutorClient'),
|
||||
serverPort: WebGMEGlobal.gmeConfig.server.port,
|
||||
httpsecure: window.location.protocol === 'https:'
|
||||
});
|
||||
};
|
||||
|
||||
Execute.prototype.executeJob = function(node) {
|
||||
return this.runExecutionPlugin('ExecuteJob', {node: node});
|
||||
};
|
||||
|
||||
Execute.prototype.executePipeline = function(node) {
|
||||
return this.runExecutionPlugin('ExecutePipeline', {node: node});
|
||||
};
|
||||
|
||||
Execute.prototype.runExecutionPlugin = function(pluginId, opts) {
|
||||
var context = this.client.getCurrentPluginContext(pluginId),
|
||||
node = opts.node || this.client.getNode(this._currentNodeId),
|
||||
name = node.getAttribute('name'),
|
||||
method;
|
||||
|
||||
// Set the activeNode
|
||||
context.managerConfig.namespace = 'pipeline';
|
||||
context.managerConfig.activeNode = node.getId();
|
||||
method = opts.useSecondary ? 'runBrowserPlugin' : 'runServerPlugin';
|
||||
|
||||
if (method === 'runServerPlugin' &&
|
||||
this.client.getBranchStatus() !== this.client.CONSTANTS.BRANCH_STATUS.SYNC) {
|
||||
|
||||
Materialize.toast('Cannot execute operations when client is out-of-sync', 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
this.client[method](pluginId, context, (err, result) => {
|
||||
var msg = err ? `${name} failed!` : `${name} executed successfully!`,
|
||||
duration = err ? 4000 : 2000;
|
||||
|
||||
// Check if it was canceled - if so, show that type of message
|
||||
if (result && result.messages.length) {
|
||||
msg = result.messages[0].message;
|
||||
duration = 4000;
|
||||
}
|
||||
|
||||
Materialize.toast(msg, duration);
|
||||
});
|
||||
};
|
||||
|
||||
Execute.prototype.isRunning = function(node) {
|
||||
var baseId,
|
||||
base,
|
||||
type;
|
||||
|
||||
node = node || this.client.getNode(this._currentNodeId);
|
||||
baseId = node.getBaseId();
|
||||
base = this.client.getNode(baseId);
|
||||
type = base.getAttribute('name');
|
||||
|
||||
if (type === 'Execution') {
|
||||
return node.getAttribute('status') === 'running';
|
||||
} else if (type === 'Job') {
|
||||
return this.isRunningJob(node);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Execute.prototype.isRunningJob = function(job) {
|
||||
var status = job.getAttribute('status');
|
||||
|
||||
return (status === 'running' || status === 'pending') &&
|
||||
job.getAttribute('secret') && job.getAttribute('jobId');
|
||||
};
|
||||
|
||||
Execute.prototype.silentStopJob = function(job) {
|
||||
var jobHash,
|
||||
secret;
|
||||
|
||||
job = job || this.client.getNode(this._currentNodeId);
|
||||
jobHash = job.getAttribute('jobId');
|
||||
secret = job.getAttribute('secret');
|
||||
if (!jobHash || !secret) {
|
||||
this.logger.error('Cannot stop job. Missing jobHash or secret');
|
||||
return;
|
||||
}
|
||||
|
||||
return this._executor.cancelJob(jobHash, secret)
|
||||
.then(() => this.logger.info(`${jobHash} has been cancelled!`))
|
||||
.fail(err => this.logger.error(`Job cancel failed: ${err}`));
|
||||
};
|
||||
|
||||
Execute.prototype.stopJob = function(job, silent) {
|
||||
var jobId;
|
||||
|
||||
job = job || this.client.getNode(this._currentNodeId);
|
||||
jobId = job.getId();
|
||||
|
||||
this.silentStopJob(job);
|
||||
|
||||
if (!silent) {
|
||||
this.client.startTransaction(`Stopping "${name}" job`);
|
||||
}
|
||||
|
||||
this.client.delAttributes(jobId, 'jobId');
|
||||
this.client.delAttributes(jobId, 'secret');
|
||||
this.client.setAttributes(jobId, 'status', 'canceled');
|
||||
|
||||
if (!silent) {
|
||||
this.client.completeTransaction();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Execute.prototype.loadChildren = function(id) {
|
||||
var deferred = Q.defer(),
|
||||
execNode = this.client.getNode(id || this._currentNodeId),
|
||||
jobIds = execNode.getChildrenIds(),
|
||||
jobsLoaded = !jobIds.length || this.client.getNode(jobIds[0]);
|
||||
|
||||
// May need to load the jobs...
|
||||
if (!jobsLoaded) {
|
||||
// Create a territory and load the nodes
|
||||
var territory = {},
|
||||
ui;
|
||||
|
||||
territory[id] = {children: 1};
|
||||
ui = this.client.addUI(this, () => {
|
||||
this.client.removeUI(ui);
|
||||
deferred.resolve();
|
||||
});
|
||||
this.client.updateTerritory(ui, territory);
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
Execute.prototype.stopExecution = function(id, inTransaction) {
|
||||
var execNode = this.client.getNode(id || this._currentNodeId);
|
||||
|
||||
return this.loadChildren(id)
|
||||
.then(() => this._stopExecution(execNode, inTransaction));
|
||||
};
|
||||
|
||||
Execute.prototype.silentStopExecution = function(id) {
|
||||
var execNode = this.client.getNode(id || this._currentNodeId);
|
||||
|
||||
// Stop the execution w/o setting any attributes
|
||||
return this.loadChildren(id)
|
||||
.then(() => this._silentStopExecution(execNode));
|
||||
};
|
||||
|
||||
Execute.prototype._stopExecution = function(execNode, inTransaction) {
|
||||
var msg = `Canceling ${execNode.getAttribute('name')} execution`;
|
||||
|
||||
if (!inTransaction) {
|
||||
this.client.startTransaction(msg);
|
||||
}
|
||||
|
||||
this._silentStopExecution(execNode);
|
||||
this.client.setAttributes(execNode.getId(), 'status', 'canceled');
|
||||
|
||||
if (!inTransaction) {
|
||||
this.client.completeTransaction();
|
||||
}
|
||||
};
|
||||
|
||||
Execute.prototype._silentStopExecution = function(execNode) {
|
||||
var jobIds = execNode.getChildrenIds();
|
||||
|
||||
jobIds.map(id => this.client.getNode(id))
|
||||
.filter(job => this.isRunning(job)) // get running jobs
|
||||
.forEach(job => this.silentStopJob(job)); // stop them
|
||||
};
|
||||
|
||||
return Execute;
|
||||
});
|
||||
@@ -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('click', 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,19 @@
|
||||
/* globals define*/
|
||||
define({
|
||||
getDisplayTime: timestamp => {
|
||||
var today = new Date().toLocaleDateString(),
|
||||
date = new Date(timestamp).toLocaleDateString();
|
||||
|
||||
if (date === today) {
|
||||
date = `Today (${new Date(timestamp).toLocaleTimeString()})`;
|
||||
}
|
||||
return date;
|
||||
},
|
||||
ClassForJobStatus: {
|
||||
success: 'success',
|
||||
canceled: 'job-canceled',
|
||||
failed: 'danger',
|
||||
pending: '',
|
||||
running: 'warning'
|
||||
}
|
||||
});
|
||||
@@ -1,12 +1,8 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/Constants',
|
||||
'deepforge/Constants',
|
||||
'decorators/DcOpDecorator/EasyDAG/DcOpDecorator.EasyDAGWidget',
|
||||
'css!./ArtifactOpDecorator.EasyDAGWidget.css'
|
||||
], function (
|
||||
@@ -19,16 +15,17 @@ define([
|
||||
var ArtifactOpDecorator,
|
||||
DECORATOR_ID = 'ArtifactOpDecorator',
|
||||
CAST_OPTS = {
|
||||
ArtifactLoader: {
|
||||
ptr: 'artifact',
|
||||
metaTgt: false
|
||||
},
|
||||
ArtifactFinder: {
|
||||
ptr: 'type',
|
||||
metaTgt: true
|
||||
}
|
||||
};
|
||||
|
||||
CAST_OPTS[CONSTANTS.OP.INPUT] = {
|
||||
ptr: 'artifact',
|
||||
metaTgt: false
|
||||
};
|
||||
|
||||
// ArtifactOp nodes need to be able to...
|
||||
// - dynamically change their outputs (downcast)
|
||||
ArtifactOpDecorator = function (options) {
|
||||
@@ -53,7 +50,17 @@ define([
|
||||
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 (name === this.castOpts.ptr && (typeof to === 'string')) {
|
||||
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);
|
||||
@@ -64,7 +71,7 @@ define([
|
||||
};
|
||||
|
||||
ArtifactOpDecorator.prototype.getDisplayName = function() {
|
||||
var ptrName = this._node.baseName === 'ArtifactLoader' ? 'artifact' : 'type',
|
||||
var ptrName = this._node.baseName === CONSTANTS.OP.INPUT ? 'artifact' : 'type',
|
||||
id = this._node.pointers[ptrName],
|
||||
name = this.nameFor[id] || this._node.name;
|
||||
return name;
|
||||
@@ -81,7 +88,7 @@ define([
|
||||
ArtifactOpDecorator.prototype.updateTargetName = function(id, name) {
|
||||
DecoratorBase.prototype.updateTargetName.apply(this, arguments);
|
||||
// Update name
|
||||
var ptrName = this._node.baseName === 'ArtifactLoader' ? 'artifact' : 'type';
|
||||
var ptrName = this._node.baseName === CONSTANTS.OP.INPUT ? 'artifact' : 'type';
|
||||
if (this._node.pointers[ptrName] === id) {
|
||||
this._name = name;
|
||||
this.onResize();
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/AttributeField'
|
||||
], function(
|
||||
BaseAttributeField
|
||||
) {
|
||||
var AttributeField = function() {
|
||||
BaseAttributeField.apply(this, arguments);
|
||||
};
|
||||
|
||||
AttributeField.prototype = Object.create(BaseAttributeField.prototype);
|
||||
|
||||
AttributeField.prototype.onClick = function() {
|
||||
};
|
||||
|
||||
return AttributeField;
|
||||
});
|
||||
@@ -1,15 +1,17 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'deepforge/Constants',
|
||||
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
|
||||
'./PointerField.RO',
|
||||
'./AttributeField.RO',
|
||||
'css!./JobDecorator.EasyDAGWidget.css'
|
||||
], function (
|
||||
EllipseDecorator
|
||||
CONSTANTS,
|
||||
EllipseDecorator,
|
||||
PointerField,
|
||||
AttributeField
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
@@ -18,7 +20,9 @@ define([
|
||||
DECORATOR_ID = 'JobDecorator',
|
||||
COLORS = {
|
||||
pending: '#9e9e9e',
|
||||
queued: '#cfd8dc',
|
||||
running: '#fff59d',
|
||||
canceled: '#ffcc80',
|
||||
success: '#66bb6a',
|
||||
fail: '#e57373'
|
||||
};
|
||||
@@ -34,6 +38,8 @@ define([
|
||||
status: true,
|
||||
execFiles: true,
|
||||
stdout: true,
|
||||
secret: true,
|
||||
jobId: true,
|
||||
debug: true
|
||||
};
|
||||
EllipseDecorator.call(this, options);
|
||||
@@ -42,18 +48,46 @@ define([
|
||||
_.extend(JobDecorator.prototype, EllipseDecorator.prototype);
|
||||
|
||||
JobDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
|
||||
JobDecorator.prototype.AttributeField = AttributeField;
|
||||
JobDecorator.prototype.PointerField = PointerField;
|
||||
|
||||
JobDecorator.prototype.isInputOperation = function() {
|
||||
return this._node.name === CONSTANTS.OP.INPUT;
|
||||
};
|
||||
|
||||
JobDecorator.prototype.getDisplayName = function() {
|
||||
if (this.isInputOperation()) {
|
||||
var id = this._node.pointers.artifact;
|
||||
|
||||
// Try to look up the pointer name
|
||||
return this.nameFor[id] || this._node.name;
|
||||
}
|
||||
return this._node.name;
|
||||
};
|
||||
|
||||
JobDecorator.prototype.setAttributes = function() {
|
||||
EllipseDecorator.prototype.setAttributes.call(this);
|
||||
var attrs = this._node.attributes,
|
||||
status = attrs.status && attrs.status.value;
|
||||
status = attrs.status && attrs.status.value,
|
||||
opAttrs = Object.keys(this._node.opAttributes);
|
||||
|
||||
// Update the color based on the 'status' attr
|
||||
this.color = COLORS[status] || COLORS.fail;
|
||||
|
||||
// Set _attributes from opAttributes
|
||||
for (var i = opAttrs.length; i--;) {
|
||||
this._attributes[opAttrs[i]] = this._node.opAttributes[opAttrs[i]];
|
||||
}
|
||||
};
|
||||
|
||||
JobDecorator.prototype.updateTargetName = function() {
|
||||
EllipseDecorator.prototype.updateTargetName.apply(this, arguments);
|
||||
var name = this.getDisplayName();
|
||||
|
||||
if (name !== this.name) {
|
||||
this.name = name;
|
||||
this.onResize();
|
||||
}
|
||||
};
|
||||
|
||||
return JobDecorator;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/PointerField'
|
||||
], function(
|
||||
BasePointerField
|
||||
) {
|
||||
var PointerField = function() {
|
||||
BasePointerField.apply(this, arguments);
|
||||
};
|
||||
|
||||
PointerField.prototype = Object.create(BasePointerField.prototype);
|
||||
|
||||
PointerField.prototype.onClick = function() {
|
||||
};
|
||||
|
||||
// Remove the delete icon and adjust the text location
|
||||
PointerField.prototype.hasIcon = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
return PointerField;
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
|
||||
'deepforge/Constants',
|
||||
'./LayerField'
|
||||
], function (
|
||||
EllipseDecorator,
|
||||
Constants,
|
||||
LayerField
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var LayerDecorator,
|
||||
DECORATOR_ID = 'LayerDecorator';
|
||||
|
||||
// Layer nodes need to be able to...
|
||||
// - show their ports
|
||||
// - highlight ports
|
||||
// - unhighlight ports
|
||||
// - report the location of specific ports
|
||||
LayerDecorator = function (options) {
|
||||
options.skipAttributes = {name: true};
|
||||
options.skipAttributes[Constants.CTOR_ARGS_ATTR] = true;
|
||||
EllipseDecorator.call(this, options);
|
||||
};
|
||||
|
||||
_.extend(LayerDecorator.prototype, EllipseDecorator.prototype);
|
||||
|
||||
LayerDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
|
||||
LayerDecorator.prototype.PointerField = LayerField;
|
||||
LayerDecorator.prototype.getDisplayName = function() {
|
||||
return this._node.name;
|
||||
};
|
||||
|
||||
// Create the pointer fields and change the event handlers
|
||||
LayerDecorator.prototype.createPointerFields = function() {
|
||||
var i = this.fields.length,
|
||||
y,
|
||||
ptr;
|
||||
|
||||
// Get the fields
|
||||
y = EllipseDecorator.prototype.createPointerFields.apply(this, arguments);
|
||||
while (i < this.fields.length) {
|
||||
// Update the event handlers
|
||||
ptr = this.fields[i].name;
|
||||
// TODO: This should be changed in EasyDAG
|
||||
this.fields[i].selectTarget = this.getValidNestedLayers.bind(this, ptr);
|
||||
i++;
|
||||
}
|
||||
return y;
|
||||
};
|
||||
|
||||
LayerDecorator.prototype.getValidNestedLayers = function(ptr) {
|
||||
var tgtId = this._node.pointers[ptr];
|
||||
if (tgtId) {
|
||||
WebGMEGlobal.State.registerActiveObject(tgtId);
|
||||
} else {
|
||||
this.createLayerArg(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
LayerDecorator.prototype.createLayerArg = function(ptr) {
|
||||
// Find the Architecture node type
|
||||
var metanodes = this.client.getAllMetaNodes(),
|
||||
base = metanodes.find(node => node.getAttribute('name') === 'Architecture'),
|
||||
baseId,
|
||||
msg = `Creating layers for "${ptr}" of ${this._node.name}`,
|
||||
tgtId;
|
||||
|
||||
if (!base) {
|
||||
return this.logger.error('Could not find "Architecture" type!');
|
||||
}
|
||||
|
||||
// Create a nested "architecture" node and set the ptr target to it
|
||||
baseId = base.getId();
|
||||
this.client.startTransaction(msg);
|
||||
tgtId = this.client.createChild({
|
||||
parentId: this._node.id,
|
||||
baseId: baseId
|
||||
});
|
||||
this.client.setAttributes(tgtId, 'name', `${ptr} (${this._node.name})`);
|
||||
this.savePointer(ptr, tgtId);
|
||||
this.client.completeTransaction();
|
||||
WebGMEGlobal.State.registerActiveObject(tgtId);
|
||||
};
|
||||
|
||||
LayerDecorator.prototype.savePointer = function(ptr, to) {
|
||||
if (!to) { // delete the current target
|
||||
var currentId = this._node.pointers[ptr],
|
||||
name = this._node.name;
|
||||
|
||||
// If the target is contained in the current node, delete it!
|
||||
if (currentId.indexOf(this._node.id) === 0) {
|
||||
this.client.startTransaction(`Removing layer for ${ptr} of ${name}`);
|
||||
this.client.delMoreNodes([currentId]);
|
||||
this.client.completeTransaction();
|
||||
this.logger.info(`Removed ${ptr} and deleted target (${currentId})`);
|
||||
} else {
|
||||
this.logger.info(`Removed ${ptr} (external architecture)`);
|
||||
}
|
||||
} else { // create and set the node
|
||||
EllipseDecorator.prototype.savePointer.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
return LayerDecorator;
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
/* globals define, _ */
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/PointerField'
|
||||
], function(
|
||||
PointerField
|
||||
) {
|
||||
// The LayerField behaves the same as PointerFields but it shows "click to view"
|
||||
// if it has a value
|
||||
var LayerField = function() {
|
||||
PointerField.apply(this, arguments);
|
||||
};
|
||||
|
||||
_.extend(LayerField.prototype, PointerField.prototype);
|
||||
|
||||
LayerField.prototype.getContent = function(content) {
|
||||
return content && 'click to view';
|
||||
};
|
||||
|
||||
LayerField.prototype.createContent = function(w, y, content) {
|
||||
PointerField.prototype.createContent.call(this, w, y, this.getContent(content));
|
||||
this.$content.attr('font-style', 'italic');
|
||||
};
|
||||
|
||||
LayerField.prototype.setValue = function(content) {
|
||||
PointerField.prototype.setValue.call(this, this.getContent(content));
|
||||
};
|
||||
|
||||
return LayerField;
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
define([
|
||||
'js/Decorators/DecoratorBase',
|
||||
'./EasyDAG/LayerDecorator.EasyDAGWidget'
|
||||
], function (
|
||||
DecoratorBase,
|
||||
LayerDecoratorEasyDAGWidget
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var LayerDecorator,
|
||||
__parent__ = DecoratorBase,
|
||||
__parent_proto__ = DecoratorBase.prototype,
|
||||
DECORATOR_ID = 'LayerDecorator';
|
||||
|
||||
LayerDecorator = function (params) {
|
||||
var opts = _.extend({loggerName: this.DECORATORID}, params);
|
||||
|
||||
__parent__.apply(this, [opts]);
|
||||
|
||||
this.logger.debug('LayerDecorator ctor');
|
||||
};
|
||||
|
||||
_.extend(LayerDecorator.prototype, __parent_proto__);
|
||||
LayerDecorator.prototype.DECORATORID = DECORATOR_ID;
|
||||
|
||||
/*********************** OVERRIDE DecoratorBase MEMBERS **************************/
|
||||
|
||||
LayerDecorator.prototype.initializeSupportedWidgetMap = function () {
|
||||
this.supportedWidgetMap = {
|
||||
EasyDAG: LayerDecoratorEasyDAGWidget
|
||||
};
|
||||
};
|
||||
|
||||
return LayerDecorator;
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*globals define, $,_*/
|
||||
/*globals define, _*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
@@ -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, _, Opentip*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
|
||||
'css!./OperationDecorator.EasyDAGWidget.css'
|
||||
@@ -47,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;
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'module',
|
||||
'path',
|
||||
'fs',
|
||||
'q',
|
||||
'plugin/PluginBase'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
module,
|
||||
path,
|
||||
fs,
|
||||
Q,
|
||||
PluginBase
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
var __dirname = path.dirname(module.uri),
|
||||
SEEDS_DIR = path.join(__dirname, '..', '..', 'seeds');
|
||||
|
||||
/**
|
||||
* Initializes a new instance of CheckLibraries.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin CheckLibraries.
|
||||
* @constructor
|
||||
*/
|
||||
var CheckLibraries = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
this.libraries = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
CheckLibraries.metadata = pluginMetadata;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
CheckLibraries.prototype = Object.create(PluginBase.prototype);
|
||||
CheckLibraries.prototype.constructor = CheckLibraries;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
CheckLibraries.prototype.main = function (callback) {
|
||||
var tuples;
|
||||
|
||||
return this.getAllLibraries()
|
||||
.then(libs => {
|
||||
tuples = libs.map(lib => { // map to [name, version, dir]
|
||||
var version,
|
||||
hash,
|
||||
data,
|
||||
versionPath = this.getSeedVersionPath(lib);
|
||||
|
||||
try {
|
||||
this.logger.info(`Checking for version info at ${versionPath}`);
|
||||
version = fs.readFileSync(versionPath, 'utf8');
|
||||
this.logger.debug(`${lib} version is ${version}`);
|
||||
data = fs.readFileSync(this.getSeedHashPath(lib), 'utf8').split(' ');
|
||||
if (data[1] === version) {
|
||||
hash = data[0];
|
||||
this.logger.debug(`${lib} hash is ${hash}`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!version) {
|
||||
this.logger.warn(`Could not find library version for ${lib}`);
|
||||
} else {
|
||||
this.logger.warn(`Could not find library hash for ${lib}`);
|
||||
}
|
||||
}
|
||||
|
||||
return [lib, version, hash];
|
||||
})
|
||||
.filter(tuple => {
|
||||
var projVersion = this.getLoadedVersion(tuple[0]),
|
||||
latest = tuple[1].replace(/\s+/g, '');
|
||||
|
||||
this.logger.info(`${tuple[0]} version info:\n${projVersion} ` +
|
||||
`(project)\n${latest} (latest)`);
|
||||
return latest !== projVersion;
|
||||
});
|
||||
|
||||
return Q.all(tuples.map(tuple => this.uploadSeed.apply(this, tuple)));
|
||||
})
|
||||
.then(hashes => {
|
||||
var name;
|
||||
|
||||
for (var i = hashes.length; i--;) {
|
||||
name = tuples[i][0];
|
||||
this.createMessage(this.libraries[name], `${name} ${hashes[i]}`);
|
||||
}
|
||||
|
||||
this.logger.info(`Found ${hashes.length} out of date libraries`);
|
||||
this.result.setSuccess(true);
|
||||
callback(null, this.result);
|
||||
})
|
||||
.fail(err => {
|
||||
this.logger.error(`Could not check the libraries: ${err}`);
|
||||
callback(err, this.result);
|
||||
});
|
||||
};
|
||||
|
||||
CheckLibraries.prototype.getSeedDir = function (name) {
|
||||
return path.join(SEEDS_DIR, name);
|
||||
};
|
||||
|
||||
CheckLibraries.prototype.getSeedDataPath = function (name) {
|
||||
return path.join(this.getSeedDir(name), name + '.webgmex');
|
||||
};
|
||||
|
||||
CheckLibraries.prototype.getSeedHashPath = function (name) {
|
||||
return path.join(this.getSeedDir(name), 'hash.txt');
|
||||
};
|
||||
|
||||
CheckLibraries.prototype.getSeedVersionPath = function (name) {
|
||||
return path.join(this.getSeedDir(name), 'version.txt');
|
||||
};
|
||||
|
||||
CheckLibraries.prototype.uploadSeed = function (name, version, hash) {
|
||||
if (!hash) { // Upload the seed
|
||||
// Get the data
|
||||
return Q.nfcall(fs.readFile, this.getSeedDataPath(name))
|
||||
.then(data => {
|
||||
this.logger.info(`Uploading new version of ${name} (${version})`);
|
||||
return this.blobClient.putFile(`${name}.webgmex`, data);
|
||||
})
|
||||
.then(newHash => { // Store the new hash
|
||||
this.logger.info(`Upload of ${name} finished!`);
|
||||
hash = newHash;
|
||||
return Q.nfcall(
|
||||
fs.writeFile,
|
||||
this.getSeedHashPath(name),
|
||||
`${hash} ${version}`
|
||||
);
|
||||
}).then(() => hash);
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
CheckLibraries.prototype.getAllLibraries = function () {
|
||||
var name,
|
||||
names = [];
|
||||
|
||||
return this.core.loadChildren(this.rootNode)
|
||||
.then(children => {
|
||||
for (var i = children.length; i--;) {
|
||||
if (this.core.isLibraryRoot(children[i])) {
|
||||
name = this.core.getAttribute(children[i], 'name');
|
||||
this.libraries[name] = children[i];
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
if (names.length) {
|
||||
this.logger.debug(`Found libraries: ${names.join(', ')}`);
|
||||
} else {
|
||||
this.logger.debug('Found no libraries!');
|
||||
}
|
||||
return names;
|
||||
});
|
||||
};
|
||||
|
||||
CheckLibraries.prototype.getLoadedVersion = function (libName) {
|
||||
var node = this.libraries[libName],
|
||||
version = this.core.getAttribute(node, 'version'); // using library root hash
|
||||
|
||||
return version;
|
||||
};
|
||||
|
||||
return CheckLibraries;
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "CheckLibraries",
|
||||
"name": "CheckLibraries",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": true,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": []
|
||||
}
|
||||
@@ -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';
|
||||
@@ -81,56 +79,233 @@ define([
|
||||
};
|
||||
|
||||
CreateExecution.prototype.createExecution = function (node) {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
// Get the user supplied name
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
config = this.getCurrentConfig(),
|
||||
basename = config.name || (name + '_execution');
|
||||
|
||||
|
||||
// Given a pipeline, copy all the operations to a custom job
|
||||
// - Copy the operations
|
||||
// - Wrap the operations in "Job" boxes which contain running info
|
||||
// - eg,
|
||||
// - 'debug' the given run (download all execution files)
|
||||
// - 'console' show console output (future feature)
|
||||
// - Update the references
|
||||
var tgtNode,
|
||||
execName,
|
||||
copies,
|
||||
opTuples, // [[op, index], [op, index], ...]
|
||||
dataMapping = {};
|
||||
|
||||
return this.getExecutionDir()
|
||||
.then(execDir => {
|
||||
var execDirId = this.core.getPath(execDir),
|
||||
execTypeId = this.core.getPath(this.META.Execution);
|
||||
|
||||
this.logger.debug(`Creating execution node in ${execDirId} (type is ${execTypeId})`);
|
||||
tgtNode = this.core.createNode({
|
||||
base: this.META.Execution,
|
||||
parent: execDir
|
||||
});
|
||||
this.core.setAttribute(tgtNode, 'name', `${name} Execution`);
|
||||
return this.core.loadChildren(node);
|
||||
this.logger.debug(`New execution created w/ id: ${this.core.getPath(tgtNode)}`);
|
||||
|
||||
// Get a unique name
|
||||
this.logger.debug(`About to get a unique name starting w/ ${basename}`);
|
||||
return this.getUniqueExecName(basename);
|
||||
})
|
||||
.then(_execName => {
|
||||
var isSnapshot = !this.getCurrentConfig().debug,
|
||||
originName = this.core.getAttribute(this.activeNode, 'name'),
|
||||
oId = this.core.getPath(this.activeNode),
|
||||
tgtId = this.core.getPath(tgtNode);
|
||||
|
||||
execName = _execName;
|
||||
this.logger.debug(`Configuring execution attributes (${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.logger.debug(`Setting origin pipeline to ${originName} (${oId})`);
|
||||
this.core.setPointer(tgtNode, 'origin', this.activeNode);
|
||||
this.logger.debug(`Adding ${tgtId} to execution list of ${originName} (${oId})`);
|
||||
this.core.addMember(this.activeNode, 'executions', tgtNode);
|
||||
|
||||
this.logger.debug(`Creating tag "${execName}"`);
|
||||
})
|
||||
.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) : [];
|
||||
this.logger.debug(`Copying operations to "${execName}"`);
|
||||
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));
|
||||
|
||||
// Create a mapping of old names to new names
|
||||
this.logger.debug('Creating mapping of old->new');
|
||||
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.logger.debug('Updating references...');
|
||||
this.updateReferences(copies, dataMapping);
|
||||
this.logger.debug('Placing operations in Job containers');
|
||||
this.boxOperations(opTuples.map(o => o[0]), tgtNode);
|
||||
return this.save(`Created execution of ${name}`);
|
||||
this.logger.debug('Finished! Saving...');
|
||||
return this.save(`Created execution from ${name}`);
|
||||
})
|
||||
.then(() => this.project.createTag(execName, this.currentHash))
|
||||
.then(() => tgtNode); // return tgtNode
|
||||
};
|
||||
|
||||
CreateExecution.prototype.getExecutionsDir = function () {
|
||||
return this.rootNode;
|
||||
CreateExecution.prototype.getUniqueExecName = function (basename) {
|
||||
var taken = {},
|
||||
name,
|
||||
i = 2;
|
||||
|
||||
basename = basename.replace(/[^\da-zA-Z_]/g, '_');
|
||||
name = basename;
|
||||
|
||||
// Get a unique name wrt the tags and the other executions
|
||||
return this.project.getTags()
|
||||
.then(tags => {
|
||||
Object.keys(tags).forEach(name => taken[name] = true);
|
||||
this.logger.debug(`Existing tags are ${Object.keys(tags).join(',')}`);
|
||||
|
||||
// Get the other executions
|
||||
return this.getExecutionDir();
|
||||
})
|
||||
.then(execDir => {
|
||||
var cIds = this.core.getChildrenPaths(execDir);
|
||||
this.logger.debug(`Current executions are ${cIds.join(', ')}`);
|
||||
return Q.all(cIds.map(id => this.core.loadByPath(this.rootNode, id)));
|
||||
})
|
||||
.then(execs => {
|
||||
var names = execs.map(exec => this.core.getAttribute(exec, 'name'));
|
||||
this.logger.debug(`Existing names are ${names.join(',')}`);
|
||||
names.forEach(name => taken[name] = true);
|
||||
|
||||
while (taken[name]) {
|
||||
name = basename + '_' + (i++);
|
||||
}
|
||||
this.logger.debug(`Unique name is "${name}"`);
|
||||
return name;
|
||||
});
|
||||
};
|
||||
|
||||
CreateExecution.prototype.copyOperations = function (nodes, dst) {
|
||||
var snapshot = !this.getCurrentConfig().debug;
|
||||
|
||||
if (snapshot) {
|
||||
this.logger.debug('Execution is a snapshot -> severing the inheritance');
|
||||
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) {
|
||||
this.logger.debug('Execution is not a snapshot -> doing a simple copy');
|
||||
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 +384,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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
/**
|
||||
* Generated by PluginGenerator 0.14.0 from webgme on Tue Mar 15 2016 21:19:45 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'plugin/PluginConfig',
|
||||
'plugin/PluginBase',
|
||||
'deepforge/js-yaml.min',
|
||||
'common/util/guid',
|
||||
'deepforge/Constants',
|
||||
'deepforge/utils',
|
||||
'js/RegistryKeys',
|
||||
'js/Constants',
|
||||
'js/Panels/MetaEditor/MetaEditorConstants',
|
||||
'underscore',
|
||||
'text!deepforge/layers.json',
|
||||
'./schemas/index',
|
||||
'text!./metadata.json'
|
||||
], function (
|
||||
PluginConfig,
|
||||
PluginBase,
|
||||
yaml,
|
||||
generateGuid,
|
||||
Constants,
|
||||
utils,
|
||||
REGISTRY_KEYS,
|
||||
CONSTANTS,
|
||||
META_CONSTANTS,
|
||||
_,
|
||||
DEFAULT_LAYERS,
|
||||
Schemas,
|
||||
metadata
|
||||
) {
|
||||
'use strict';
|
||||
@@ -61,111 +55,102 @@ define([
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
CreateTorchMeta.prototype.main = function (callback) {
|
||||
// Use self to access core, project, result, logger etc from PluginBase.
|
||||
// These are all instantiated at this point.
|
||||
var self = this;
|
||||
|
||||
if (!this.META.Language) {
|
||||
callback('"Language" container required to run plugin', this.result);
|
||||
return callback('"Language" container required to run plugin', this.result);
|
||||
}
|
||||
|
||||
// Extra layer names
|
||||
this.getJsonLayers((err, text) => {
|
||||
if (err) {
|
||||
return callback(err, this.result);
|
||||
// The format is...
|
||||
// - (Abstract) CategoryLayerTypes
|
||||
// - LayerName
|
||||
// - Attributes (if exists)
|
||||
var layers,
|
||||
content = {},
|
||||
categories,
|
||||
config = this.getCurrentConfig(),
|
||||
nodes = {};
|
||||
|
||||
try {
|
||||
layers = this.getJsonLayers();
|
||||
} catch (e) {
|
||||
return callback('JSON parse error: ' + e, this.result);
|
||||
}
|
||||
layers.forEach(layer => {
|
||||
if (!content[layer.type]) {
|
||||
content[layer.type] = [];
|
||||
}
|
||||
|
||||
// The format is...
|
||||
// - (Abstract) CategoryLayerTypes
|
||||
// - LayerName
|
||||
// - Attributes (if exists)
|
||||
var content = {},
|
||||
categories,
|
||||
config = this.getCurrentConfig(),
|
||||
nodes = {},
|
||||
layers;
|
||||
|
||||
try {
|
||||
layers = JSON.parse(text)
|
||||
.filter(layer => layer.type !== 'Criterion');
|
||||
} catch (e) {
|
||||
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) {
|
||||
this.META.Layer = this.createMetaNode('Layer', this.META.FCO);
|
||||
}
|
||||
|
||||
// Create the category nodes
|
||||
categories
|
||||
.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(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);
|
||||
});
|
||||
});
|
||||
|
||||
self.save('CreateTorchMeta updated model.', function (err) {
|
||||
if (err) {
|
||||
callback(err, self.result);
|
||||
return;
|
||||
}
|
||||
self.result.setSuccess(true);
|
||||
callback(null, self.result);
|
||||
});
|
||||
content[layer.type].push(layer);
|
||||
});
|
||||
|
||||
categories = Object.keys(content);
|
||||
// Create the base class, if needed
|
||||
if (!this.META.Layer) {
|
||||
this.META.Layer = this.createMetaNode('Layer', this.META.FCO);
|
||||
}
|
||||
|
||||
// Create the category nodes
|
||||
categories
|
||||
.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.setBase(layer, 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(layer => {
|
||||
var name = layer.name,
|
||||
node;
|
||||
|
||||
node = this.createMetaNode(name, nodes[cat], cat, layer);
|
||||
// Make the node non-abstract
|
||||
if (node) {
|
||||
this.core.setRegistry(node, 'isAbstract', false);
|
||||
nodes[name] = node;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.save('CreateTorchMeta updated model.')
|
||||
.then(() => {
|
||||
this.result.setSuccess(true);
|
||||
callback(null, this.result);
|
||||
})
|
||||
.fail(err => callback(err, this.result));
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.removeFromMeta = function (nodeId) {
|
||||
@@ -207,28 +192,45 @@ define([
|
||||
return sheet.SetID;
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.getJsonLayers = function (callback) {
|
||||
var config = this.getCurrentConfig();
|
||||
CreateTorchMeta.prototype.getJsonLayers = function () {
|
||||
var config = this.getCurrentConfig(),
|
||||
schema = config.layerSchema;
|
||||
|
||||
if (config.layerNameHash) {
|
||||
this.blobClient.getObject(config.layerNameHash, (err, buffer) => {
|
||||
if (err) {
|
||||
return callback(err, this.result);
|
||||
}
|
||||
var text = String.fromCharCode.apply(null, new Uint8Array(buffer));
|
||||
return callback(null, text);
|
||||
});
|
||||
} else {
|
||||
return callback(null, DEFAULT_LAYERS);
|
||||
if (schema === 'all') {
|
||||
return Object.keys(Schemas).map(key => JSON.parse(Schemas[key]))
|
||||
.reduce((l1, l2) => l1.concat(l2), []);
|
||||
}
|
||||
|
||||
return JSON.parse(Schemas[schema]);
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.createMetaNode = function (name, base, tabName, attrs) {
|
||||
// Some helper methods w/ attribute handling
|
||||
var LUA_TO_GME = {
|
||||
boolean: 'boolean',
|
||||
number: 'float',
|
||||
string: 'string'
|
||||
};
|
||||
|
||||
var isLayerAttribute = type => type && type.substring(0, 3) === 'nn.';
|
||||
|
||||
CreateTorchMeta.prototype.createMetaNode = function (name, base, tabName, layer) {
|
||||
var node = this.META[name],
|
||||
nodeId = node && this.core.getPath(node),
|
||||
tabId = this.metaSheets[tabName],
|
||||
position = this.getPositionFor(name, tabName);
|
||||
position = this.getPositionFor(name, tabName),
|
||||
setters = {},
|
||||
defaults = {},
|
||||
types = {},
|
||||
type,
|
||||
attrs,
|
||||
desc;
|
||||
|
||||
if (layer) {
|
||||
attrs = layer.params;
|
||||
setters = layer.setters;
|
||||
defaults = layer.defaults;
|
||||
types = layer.types || types;
|
||||
}
|
||||
if (!tabId) {
|
||||
this.logger.error(`No meta sheet for ${tabName}`);
|
||||
}
|
||||
@@ -245,7 +247,7 @@ define([
|
||||
} else {
|
||||
// Remove from meta
|
||||
this.removeFromMeta(nodeId);
|
||||
this.core.setPointer(node, 'base', base);
|
||||
this.core.setBase(node, base);
|
||||
}
|
||||
|
||||
// Add it to the meta sheet
|
||||
@@ -270,14 +272,16 @@ define([
|
||||
if (attrs) { // Add the attributes
|
||||
// Remove attributes not in the given list
|
||||
var currentAttrs = this.core.getValidAttributeNames(node),
|
||||
rmAttrs;
|
||||
defVal,
|
||||
rmAttrs,
|
||||
simpleAttrs,
|
||||
rmPtrs;
|
||||
|
||||
rmAttrs = _.difference(currentAttrs, attrs) // old attribute names
|
||||
.filter(attr => attr !== 'name');
|
||||
simpleAttrs = attrs.filter(name => !isLayerAttribute(types[name]));
|
||||
rmAttrs = _.difference(currentAttrs, simpleAttrs) // old attribute names
|
||||
.filter(attr => attr !== 'name')
|
||||
.filter(attr => !setters[attr]);
|
||||
|
||||
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) {
|
||||
@@ -285,11 +289,38 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
attrs.forEach((name, index) => {
|
||||
var desc = {};
|
||||
desc.argindex = index;
|
||||
desc.default = '';
|
||||
this.addAttribute(name, node, desc);
|
||||
// Remove all old pointers
|
||||
rmPtrs = _.difference(this.core.getPointerNames(node), currentAttrs)
|
||||
.filter(ptr => ptr !== 'base');
|
||||
|
||||
if (rmPtrs.length + rmAttrs.length) {
|
||||
this.logger.debug(`Removing ${rmPtrs.concat(rmAttrs).join(', ')} from ${name}`);
|
||||
}
|
||||
rmPtrs.forEach(ptr => this.core.delPointerMeta(node, ptr));
|
||||
|
||||
attrs.forEach(name => {
|
||||
desc = {};
|
||||
defVal = defaults.hasOwnProperty(name) ? defaults[name] : '';
|
||||
type = LUA_TO_GME[types[name]];
|
||||
if (type) {
|
||||
desc.type = type;
|
||||
}
|
||||
if (isLayerAttribute(types[name])) { // Check if it is an nn layer type
|
||||
// If so, create a pointer rather than attribute
|
||||
this.addLayerAttribute(name, node);
|
||||
this.logger.debug(`${name} is a layer type attribute`);
|
||||
} else {
|
||||
this.addAttribute(name, node, desc, defVal);
|
||||
}
|
||||
});
|
||||
this.core.setAttribute(node, Constants.CTOR_ARGS_ATTR, attrs.join(','));
|
||||
|
||||
// Add the setters to the meta
|
||||
Object.keys(setters).forEach(name => {
|
||||
desc = utils.getSetterSchema(name, setters, defaults);
|
||||
defVal = desc.default;
|
||||
delete desc.default;
|
||||
this.addAttribute(name, node, desc, defVal);
|
||||
});
|
||||
}
|
||||
this.logger.debug(`added ${name} to the meta`);
|
||||
@@ -324,41 +355,34 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
CreateTorchMeta.prototype.addAttribute = function (name, node, def) {
|
||||
var initial,
|
||||
schema = {};
|
||||
CreateTorchMeta.prototype.addLayerAttribute = function (name, node) {
|
||||
// No default value support for now...
|
||||
// Create a pointer of the given type on the node
|
||||
this.core.setPointerMetaTarget(node, name, this.META.Architecture, 1, 1);
|
||||
this.core.setPointerMetaLimits(node, name, 1, 1);
|
||||
};
|
||||
|
||||
schema.type = def.type || 'string';
|
||||
CreateTorchMeta.prototype.addAttribute = function (name, node, schema, defVal) {
|
||||
schema.type = schema.type || 'string';
|
||||
if (schema.type === 'list') { // FIXME: add support for lists
|
||||
schema.type = 'string';
|
||||
}
|
||||
|
||||
if (def.min !== undefined) {
|
||||
schema.min = +def.min;
|
||||
if (schema.min !== undefined) {
|
||||
schema.min = +schema.min;
|
||||
}
|
||||
|
||||
if (def.max !== undefined) {
|
||||
if (schema.max !== undefined) {
|
||||
// Set the min, max
|
||||
schema.max = +def.max;
|
||||
schema.max = +schema.max;
|
||||
}
|
||||
|
||||
// Add the infer flag
|
||||
if (def.infer) {
|
||||
schema.infer = def.infer;
|
||||
}
|
||||
|
||||
// Add the argindex flag
|
||||
schema.argindex = def.argindex;
|
||||
|
||||
// Create the attribute and set the schema
|
||||
this.core.setAttributeMeta(node, name, schema);
|
||||
|
||||
// Determine a default value
|
||||
initial = def.hasOwnProperty('default') ? def.default : def.min || null;
|
||||
if (schema.type === 'boolean') {
|
||||
initial = initial !== null ? initial : false;
|
||||
if (defVal) {
|
||||
this.core.setAttribute(node, name, defVal);
|
||||
}
|
||||
this.core.setAttribute(node, name, initial);
|
||||
};
|
||||
|
||||
return CreateTorchMeta;
|
||||
|
||||
@@ -7,24 +7,29 @@
|
||||
"src": "",
|
||||
"class": "glyphicon glyphicon-ok-circle"
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableServerSideExecution": true,
|
||||
"disableBrowserSideExecution": false,
|
||||
"configStructure": [
|
||||
{
|
||||
"name": "layerNameHash",
|
||||
"displayName": "Torch Layers",
|
||||
"description": "Yaml file of torch layer descriptors (optional)",
|
||||
"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
|
||||
}
|
||||
{
|
||||
"name": "layerSchema",
|
||||
"displayName": "Torch Libraries",
|
||||
"description": "Torch nn libraries to create layers from",
|
||||
"value": "all",
|
||||
"valueItems": [
|
||||
"nn",
|
||||
"rnn",
|
||||
"all"
|
||||
],
|
||||
"valueType": "string",
|
||||
"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,13 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'text!./nn.json',
|
||||
'text!./rnn.json'
|
||||
], function(
|
||||
nn,
|
||||
rnn
|
||||
) {
|
||||
return {
|
||||
nn: nn,
|
||||
rnn: rnn
|
||||
};
|
||||
});
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,178 @@
|
||||
[
|
||||
{
|
||||
"name": "CopyGrad",
|
||||
"baseType": "Identity",
|
||||
"setters": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "FastLSTM",
|
||||
"baseType": "LSTM",
|
||||
"params": [
|
||||
"inputSize",
|
||||
"outputSize",
|
||||
"rho",
|
||||
"eps",
|
||||
"momentum",
|
||||
"affine"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {
|
||||
"eps": "number",
|
||||
"momentum": "number"
|
||||
},
|
||||
"defaults": {
|
||||
"momentum": 0.1,
|
||||
"eps": 0.1
|
||||
},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "LSTM",
|
||||
"baseType": "AbstractRecurrent",
|
||||
"params": [
|
||||
"inputSize",
|
||||
"outputSize",
|
||||
"rho",
|
||||
"cell2gate"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {
|
||||
"rho": "number"
|
||||
},
|
||||
"defaults": {
|
||||
"rho": 9999
|
||||
},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "LinearNoBias",
|
||||
"baseType": "Linear",
|
||||
"params": [
|
||||
"inputSize",
|
||||
"outputSize"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "Simple"
|
||||
},
|
||||
{
|
||||
"name": "LookupTableMaskZero",
|
||||
"baseType": "LookupTable",
|
||||
"params": [
|
||||
"nIndex",
|
||||
"nOutput"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "NormStabilizer",
|
||||
"baseType": "AbstractRecurrent",
|
||||
"params": [
|
||||
"beta"
|
||||
],
|
||||
"setters": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "Recurrent",
|
||||
"baseType": "AbstractRecurrent",
|
||||
"params": [
|
||||
"start",
|
||||
"input",
|
||||
"feedback",
|
||||
"transfer",
|
||||
"rho",
|
||||
"merge"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {
|
||||
"start": "nn.Module",
|
||||
"transfer": "nn.Module",
|
||||
"feedback": "nn.Module",
|
||||
"input": "nn.Module"
|
||||
},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "SAdd",
|
||||
"baseType": "Module",
|
||||
"params": [
|
||||
"addend",
|
||||
"negate"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "SeqBRNN",
|
||||
"baseType": "Container",
|
||||
"params": [
|
||||
"inputDim",
|
||||
"hiddenDim",
|
||||
"batchFirst"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "SeqGRU",
|
||||
"baseType": "Module",
|
||||
"params": [
|
||||
"inputSize",
|
||||
"outputSize"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "SeqLSTM",
|
||||
"baseType": "Module",
|
||||
"params": [
|
||||
"inputsize",
|
||||
"hiddensize",
|
||||
"outputsize"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "SeqLSTMP",
|
||||
"baseType": "SeqLSTM",
|
||||
"params": [
|
||||
"inputsize",
|
||||
"hiddensize",
|
||||
"outputsize"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
},
|
||||
{
|
||||
"name": "SeqReverseSequence",
|
||||
"baseType": "Module",
|
||||
"params": [
|
||||
"dim"
|
||||
],
|
||||
"setters": {},
|
||||
"types": {},
|
||||
"defaults": {},
|
||||
"type": "RNN"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable no-console */
|
||||
// Update the metadata and schemas/index based on the new schemas in schemas/
|
||||
|
||||
// Update metadata
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
schemas,
|
||||
metadata = require('./metadata.json'),
|
||||
schemaList;
|
||||
|
||||
schemas = fs.readdirSync(__dirname + '/schemas/')
|
||||
.filter(name => path.extname(name) === '.json')
|
||||
.map(name => name.replace(/\.json$/, ''));
|
||||
|
||||
console.log('Discovered schemas: ' + schemas.join(', '));
|
||||
|
||||
schemaList = metadata.configStructure.find(struct => struct.name === 'layerSchema');
|
||||
schemaList.valueItems = schemas.concat('all');
|
||||
|
||||
console.log('Updating metadata...');
|
||||
fs.writeFileSync(__dirname + '/metadata.json', JSON.stringify(metadata, null, 2));
|
||||
|
||||
// Update index.js
|
||||
var index =
|
||||
`/*globals define*/
|
||||
define([
|
||||
${schemas.map(s => `'text!./${s}.json'`).join(',\n ')}
|
||||
], function(
|
||||
${schemas.map(s => s).join(',\n ')}
|
||||
) {
|
||||
return {
|
||||
${schemas.map(s => s + ': ' + s).join(',\n ')}
|
||||
};
|
||||
});`;
|
||||
|
||||
console.log('Updating index.js...');
|
||||
fs.writeFileSync(__dirname + '/schemas/index.js', index);
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -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,97 @@
|
||||
-- Instantiate the deepforge object
|
||||
deepforge = {}
|
||||
|
||||
function deepforge.initialize()
|
||||
require 'nn'
|
||||
require 'rnn'
|
||||
require './classes/init'
|
||||
require './custom-layers'
|
||||
end
|
||||
|
||||
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 .. ' ') -- guarantee ends w/ space
|
||||
end
|
||||
|
||||
-- Graph support
|
||||
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)
|
||||
assert(type(x) == "number" and type(y) == "number", "adding point (" .. tostring(x) .. ", " .. tostring(y) .. ") to " .. self.name .. " failed: expected (number, number)")
|
||||
deepforge._cmd('<%= GRAPH_PLOT %>', self.id, x, y)
|
||||
end
|
||||
|
||||
function Graph:line(name, opts)
|
||||
return deepforge._Line(self.id, name, opts)
|
||||
end
|
||||
|
||||
-- Image support
|
||||
local function saveImage(name, tensor)
|
||||
require 'image'
|
||||
require 'paths'
|
||||
|
||||
-- save it in the tmp directory
|
||||
local filename = name .. '.png'
|
||||
local path = paths.concat('metadata', filename)
|
||||
|
||||
if paths.dir('metadata') == nil then
|
||||
paths.mkdir('metadata')
|
||||
end
|
||||
|
||||
image.save(path, tensor)
|
||||
end
|
||||
|
||||
function deepforge.image(name, tensor)
|
||||
saveImage(name, tensor)
|
||||
deepforge._cmd("<%= IMAGE.BASIC %>", deepforge.id(), name)
|
||||
end
|
||||
|
||||
Image = torch.class('deepforge.Image')
|
||||
function Image:__init(name, tensor)
|
||||
self.id = deepforge.id()
|
||||
self.name = name
|
||||
|
||||
if tensor ~= nil then
|
||||
saveImage(name, tensor)
|
||||
deepforge._cmd('<%= IMAGE.CREATE %>', self.id, self.name)
|
||||
end
|
||||
end
|
||||
|
||||
function Image:update(tensor)
|
||||
saveImage(self.name, tensor)
|
||||
deepforge._cmd('<%= IMAGE.UPDATE %>', self.id, self.name)
|
||||
end
|
||||
|
||||
function Image:title(name)
|
||||
self.name = name
|
||||
deepforge._cmd('<%= IMAGE.NAME %>', self.id, self.name)
|
||||
end
|
||||
|
||||
return deepforge
|
||||
@@ -0,0 +1,6 @@
|
||||
require 'paths'
|
||||
|
||||
local path = 'inputs/<%= name %>/data'
|
||||
local abs_path = paths.concat('inputs', '<%= name %>', 'data')
|
||||
|
||||
<%= 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'
|
||||
+6
-2
@@ -1,22 +1,26 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'text!./start.ejs',
|
||||
'text!./entry.ejs',
|
||||
'text!./main.ejs',
|
||||
'text!./deepforge.ejs',
|
||||
'text!./serialize.ejs',
|
||||
'text!./deserialize.ejs'
|
||||
], function(
|
||||
START,
|
||||
ENTRY,
|
||||
MAIN,
|
||||
DEEPFORGE,
|
||||
SERIALIZE,
|
||||
DESERIALIZE
|
||||
) {
|
||||
|
||||
var BASH = 'th init.lua 2>&1';
|
||||
return {
|
||||
BASH,
|
||||
START,
|
||||
ENTRY,
|
||||
MAIN,
|
||||
SERIALIZE,
|
||||
DEEPFORGE,
|
||||
DESERIALIZE
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
-- load custom layers and classes
|
||||
deepforge.initialize()
|
||||
|
||||
-- input data<% inputs.forEach(function(pair) { var input = pair[0], isNil = pair[1];%>
|
||||
local <%= input %> = <% if (isNil) { %>nil<% } else { %>require './inputs/<%= input %>'<%}}); %>
|
||||
|
||||
-- load references<% pointers.forEach(function(pair) { var pointer = pair[0], isNil = pair[1];%>
|
||||
local <%= pointer %> = <% if (isNil) { %>nil<% } else { %>require './pointers/<%= pointer %>'<%}}); %>
|
||||
local attributes = require './attributes'
|
||||
|
||||
-- main operation code for <%= name %>
|
||||
<%= code %>
|
||||
@@ -0,0 +1,224 @@
|
||||
// A wrapper for the torch script which:
|
||||
// - merges stdout, stderr
|
||||
// - receives some commands and uploads intermediate data
|
||||
var spawn = require('child_process').spawn,
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
log = console.error,
|
||||
logger = {};
|
||||
|
||||
// Create the stderr only logger
|
||||
['error', 'warn', 'info', 'log', 'debug'].forEach(method => logger[method] = log);
|
||||
|
||||
// Get the BlobClient...
|
||||
var COMMAND_PREFIX = '<%= START_CMD %>',
|
||||
IMAGE = '<%= IMAGE.PREFIX %>',
|
||||
requirejs = require('webgme').requirejs,
|
||||
remainingImageCount = 0,
|
||||
exitCode = null;
|
||||
|
||||
requirejs([
|
||||
'q',
|
||||
'blob/BlobClient'
|
||||
], function(
|
||||
Q,
|
||||
BlobClient
|
||||
) {
|
||||
var url = process.env.ORIGIN_URL || 'http://127.0.0.1:8888',
|
||||
CACHE_DIR = process.env.DEEPFORGE_WORKER_CACHE || './worker-cache',
|
||||
protocol = url.split('://').shift(),
|
||||
address,
|
||||
port = (url.split(':') || ['80']).pop();
|
||||
|
||||
address = url.replace(protocol + '://', '')
|
||||
.replace(':' + port, '');
|
||||
|
||||
// Create CACHE_DIR if it doesn't exist
|
||||
var prepareCache = function() {
|
||||
var dirs = CACHE_DIR.replace(/\/$/, '').split('/'),
|
||||
cacheParent;
|
||||
|
||||
dirs.pop();
|
||||
cacheParent = dirs.join('/');
|
||||
return makeIfNeeded(cacheParent).then(() => makeIfNeeded(CACHE_DIR));
|
||||
};
|
||||
|
||||
var makeIfNeeded = function(dir) {
|
||||
var deferred = Q.defer(),
|
||||
job;
|
||||
|
||||
log(`makeIfNeeded: ${JSON.stringify(dir)}`);
|
||||
fs.lstat(dir, (err, stat) => {
|
||||
|
||||
if (err || !stat.isDirectory()) {
|
||||
fs.mkdir(dir, err => {
|
||||
if (err) {
|
||||
return deferred.reject(err);
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var blobClient = new BlobClient({
|
||||
server: address,
|
||||
httpsecure: protocol === 'https',
|
||||
serverPort: port,
|
||||
logger: logger
|
||||
});
|
||||
|
||||
var checkFinished = () => {
|
||||
if (exitCode !== null && remainingImageCount === 0) {
|
||||
log('finished!');
|
||||
process.exit(exitCode);
|
||||
}
|
||||
};
|
||||
|
||||
var uploadImage = function(line) {
|
||||
var args = line.split(/\s+/),
|
||||
name = args.slice(3).join(' ').replace(/\s+$/, ''),
|
||||
filename = 'metadata/' + name + '.png';
|
||||
|
||||
// Upload the image from metadata/
|
||||
remainingImageCount++;
|
||||
fs.readFile(filename, (err, content) => {
|
||||
if (err) {
|
||||
logger.error(`Could not read ${filename}: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add hash to the image command
|
||||
log('about to putFile', filename);
|
||||
blobClient.putFile(filename, content)
|
||||
.then(hash => {
|
||||
args.splice(2, 0, hash);
|
||||
console.log(args.join(' '));
|
||||
log('printing cmd:', args.join(' '));
|
||||
--remainingImageCount;
|
||||
log('finished uploading ' + filename + ' ' + remainingImageCount + ' remain');
|
||||
checkFinished();
|
||||
})
|
||||
.fail(err => logger.error(`${filename} upload failed: ${err}`));
|
||||
});
|
||||
};
|
||||
|
||||
var onStderr = function(data) {
|
||||
var text = data.toString();
|
||||
// Filter out directory label from stack traces
|
||||
process.stdout.write(text.replace(/\.\.\.\/.*\/(main|deepforge|init).lua/g, '$1'));
|
||||
};
|
||||
|
||||
var onStdout = function(data) {
|
||||
var lines = data.toString().split('\n'),
|
||||
result = [],
|
||||
cmdStart;
|
||||
|
||||
// Check for commands...
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
cmdStart = lines[i].indexOf(COMMAND_PREFIX);
|
||||
if (cmdStart !== -1 && lines[i].indexOf(IMAGE) !== -1) {
|
||||
uploadImage(lines[i]);
|
||||
} else {
|
||||
result.push(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
process.stdout.write(result.join('\n'));
|
||||
};
|
||||
|
||||
var createCacheDir = function(hash) {
|
||||
var dir = hash.substring(0, 2);
|
||||
return makeIfNeeded(CACHE_DIR + '/' + dir);
|
||||
};
|
||||
|
||||
var dataCachePath = function(hash) {
|
||||
var dir = hash.substring(0, 2),
|
||||
filename = hash.substring(2),
|
||||
cachePath = `${CACHE_DIR}/${dir}/${filename}`;
|
||||
|
||||
// Get the path for data in the cache
|
||||
return cachePath;
|
||||
};
|
||||
|
||||
var makeSymLink = function(target, src) {
|
||||
var deferred = Q.defer(),
|
||||
job;
|
||||
|
||||
src = path.resolve(src);
|
||||
target = path.resolve(target);
|
||||
fs.stat(src, err => {
|
||||
if (err.code === 'ENOENT') {
|
||||
logger.debug(`creating symlink "ln -s ${target} ${src}"`);
|
||||
job = spawn('ln', ['-s', target, src || '.']);
|
||||
job.on('exit', code => {
|
||||
if (code) {
|
||||
deferred.reject(`Could not create symlink ${target} -> ${src||'.'}`);
|
||||
return;
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var getData = function(ipath, hashes) {
|
||||
// Download the data and put it in the given path
|
||||
var deferred = Q.defer(),
|
||||
inputName = ipath.split('/')[1],
|
||||
cachePath = dataCachePath(hashes.cache);
|
||||
|
||||
|
||||
logger.debug(`retrieving ${ipath}`);
|
||||
fs.lstat(cachePath, (err, cacheStats) => {
|
||||
// Check if the data exists in the cache
|
||||
if (!err && cacheStats.isFile()) {
|
||||
logger.info(`${inputName} already cached. Skipping retrieval from blob`);
|
||||
return makeSymLink(cachePath, ipath).then(deferred.resolve);
|
||||
}
|
||||
|
||||
createCacheDir(hashes.cache)
|
||||
.then(() => blobClient.getObject(hashes.req))
|
||||
.then(buffer => fs.writeFile(cachePath, buffer, (err, result) => {
|
||||
if (err) {
|
||||
logger.error('Retrieving ' + ipath + ' failed!');
|
||||
return deferred.reject(`Could not write to ${ipath}: ${err}`);
|
||||
}
|
||||
// Create the symlink
|
||||
logger.info('Retrieved ' + ipath);
|
||||
return makeSymLink(cachePath, ipath).then(deferred.resolve);
|
||||
}))
|
||||
.fail(err => deferred.reject(`Could not retrieve "${inputName}" (${err})`));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
// Download the large files
|
||||
var inputData = JSON.parse(fs.readFileSync('./input-data.json')),
|
||||
inputPaths = Object.keys(inputData);
|
||||
|
||||
// Request the data from the blob
|
||||
prepareCache()
|
||||
.then(() => Q.all(inputPaths.map(ipath => getData(ipath, inputData[ipath]))))
|
||||
.then(() => {
|
||||
// Run 'th init.lua' and merge the stdout, stderr
|
||||
var job = spawn('th', ['init.lua']);
|
||||
job.stdout.on('data', onStdout);
|
||||
job.stderr.on('data', onStderr);
|
||||
job.on('close', code => {
|
||||
exitCode = code;
|
||||
log('script finished w/ exit code:', code);
|
||||
checkFinished();
|
||||
});
|
||||
})
|
||||
.fail(err => {
|
||||
console.log(`Data retrieval failed: ${err}`);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
@@ -3,23 +3,21 @@
|
||||
|
||||
define([
|
||||
'plugin/CreateExecution/CreateExecution/CreateExecution',
|
||||
'deepforge/plugin/PtrCodeGen',
|
||||
'plugin/ExecuteJob/ExecuteJob/ExecuteJob',
|
||||
'deepforge/JobLogsClient',
|
||||
'common/storage/constants',
|
||||
'common/core/constants',
|
||||
'q',
|
||||
'text!./metadata.json',
|
||||
'./templates/index',
|
||||
'./LocalExecutor',
|
||||
'executor/ExecutorClient',
|
||||
'underscore'
|
||||
], function (
|
||||
CreateExecution,
|
||||
PtrCodeGen,
|
||||
ExecuteJob,
|
||||
JobLogsClient,
|
||||
STORAGE_CONSTANTS,
|
||||
CONSTANTS,
|
||||
Q,
|
||||
pluginMetadata,
|
||||
Templates,
|
||||
LocalExecutor, // DeepForge operation primitives
|
||||
ExecutorClient,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
@@ -32,14 +30,17 @@ define([
|
||||
* @classdesc This class represents the plugin ExecutePipeline.
|
||||
* @constructor
|
||||
*/
|
||||
var OUTPUT_INTERVAL = 1500,
|
||||
STDOUT_FILE = 'job_stdout.txt';
|
||||
var ExecutePipeline = function () {
|
||||
// Call base class' constructor.
|
||||
CreateExecution.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
|
||||
this._currentSave = Q();
|
||||
this.changes = {};
|
||||
this.currentChanges = {}; // read-only changes being applied
|
||||
this.creations = {};
|
||||
this.deletions = [];
|
||||
this.createIdToMetadataId = {};
|
||||
this.initRun();
|
||||
};
|
||||
|
||||
@@ -49,12 +50,13 @@ define([
|
||||
* @type {object}
|
||||
*/
|
||||
ExecutePipeline.metadata = pluginMetadata;
|
||||
ExecutePipeline.UPDATE_INTERVAL = 1500;
|
||||
|
||||
// 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 = {};
|
||||
@@ -70,6 +72,23 @@ define([
|
||||
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.canceled = false;
|
||||
this.runningJobs = 0;
|
||||
|
||||
// metadata records
|
||||
this._metadata = {};
|
||||
this._markForDeletion = {}; // id -> node
|
||||
this._oldMetadataByName = {}; // name -> id
|
||||
this.lastAppliedCmd = {};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -82,37 +101,46 @@ define([
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
ExecutePipeline.prototype.main = function (callback) {
|
||||
// This will probably need to execute the operations, too, because the
|
||||
// inputs for the next operation cannot be created until the inputs have
|
||||
// been generated
|
||||
var startPromise;
|
||||
|
||||
this.initRun();
|
||||
this.pipelineName = this.core.getAttribute(this.activeNode, 'name');
|
||||
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.logger.debug(`Finished creating execution "${this.getAttribute(execNode, 'name')}"`);
|
||||
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);
|
||||
this.logger.debug('Restarting execution');
|
||||
startPromise = Q();
|
||||
} else {
|
||||
return callback('Current node is not a Pipeline or Execution!', this.result);
|
||||
}
|
||||
|
||||
// Set debug and the final callback
|
||||
this.debug = true; // this.getCurrentConfig().debug;
|
||||
// Get the gmeConfig...
|
||||
this.logManager = new JobLogsClient({
|
||||
logger: this.logger,
|
||||
port: this.gmeConfig.server.port,
|
||||
branchName: this.branchName,
|
||||
projectId: this.projectId
|
||||
});
|
||||
this._callback = callback;
|
||||
this.currentForkName = null;
|
||||
|
||||
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.getAttribute(this.activeNode, 'name');
|
||||
this.logger.debug(`Loaded subtree of ${this.pipelineName}. About to build cache`);
|
||||
this.buildCache(subtree);
|
||||
this.logger.debug('Parsing execution for job inter-dependencies');
|
||||
this.parsePipeline(children); // record deps, etc
|
||||
|
||||
this.logger.debug('Clearing old results');
|
||||
return this.clearResults();
|
||||
})
|
||||
.then(() => this.executePipeline())
|
||||
@@ -124,11 +152,47 @@ define([
|
||||
// When 'save' is called, it should still finish any current save op
|
||||
// before continuing
|
||||
this._currentSave = this._currentSave
|
||||
.then(() => CreateExecution.prototype.save.call(this, msg));
|
||||
.then(() => this.updateForkName(this.pipelineName))
|
||||
.then(() => this.applyModelChanges())
|
||||
.then(() => CreateExecution.prototype.save.call(this, msg))
|
||||
.then(result => {
|
||||
var msg;
|
||||
if (result.status === STORAGE_CONSTANTS.FORKED) {
|
||||
this.currentForkName = result.forkName;
|
||||
this.logManager.fork(result.forkName);
|
||||
msg = `"${this.pipelineName}" execution has forked to "${result.forkName}"`;
|
||||
this.sendNotification(msg);
|
||||
} else if (result.status === STORAGE_CONSTANTS.MERGED) {
|
||||
this.logger.debug('Merged changes. About to update plugin nodes');
|
||||
return this.updateNodes();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return this._currentSave;
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.updateNodes = function (hash) {
|
||||
var result = ExecuteJob.prototype.updateNodes.call(this, hash);
|
||||
return result.then(() => this.updateCache());
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.updateCache = function () {
|
||||
var nodeIds = Object.keys(this.nodes),
|
||||
nodes = nodeIds.map(id => this.core.loadByPath(this.rootNode, id));
|
||||
|
||||
this.logger.debug(`updating node cache (${nodeIds.length} nodes)`);
|
||||
return Q.all(nodes).then(nodes => {
|
||||
for (var i = nodeIds.length; i--;) {
|
||||
this.nodes[nodeIds[i]] = nodes[i];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.isExecutionCanceled = function () {
|
||||
return this.getAttribute(this.activeNode, 'status') === 'canceled';
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.isInputData = function (node) {
|
||||
var prnt = this.core.getParent(node);
|
||||
return this.core.isTypeOf(prnt, this.META.Inputs);
|
||||
@@ -146,10 +210,17 @@ define([
|
||||
|
||||
// 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.setAttribute(node, 'status', 'pending');
|
||||
});
|
||||
|
||||
// Set the status of the execution to 'running'
|
||||
this.setAttribute(this.activeNode, 'status', 'running');
|
||||
this.logger.info('Setting all jobs status to "pending"');
|
||||
this.logger.debug(`Making a commit from ${this.currentHash}`);
|
||||
this.setAttribute(this.activeNode, 'startTime', Date.now());
|
||||
this.core.delAttribute(this.activeNode, 'endTime');
|
||||
return this.save(`Initializing ${this.pipelineName} for execution`);
|
||||
};
|
||||
|
||||
@@ -174,9 +245,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
|
||||
@@ -239,16 +308,106 @@ define([
|
||||
this.executeReadyOperations();
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onPipelineComplete = function(err) {
|
||||
var name = this.core.getAttribute(this.activeNode, 'name');
|
||||
this.logger.debug(`Pipeline "${name}" complete!`);
|
||||
ExecutePipeline.prototype.onOperationFail = function(node, err) {
|
||||
var job = this.core.getParent(node),
|
||||
id = this.core.getPath(node),
|
||||
name = this.getAttribute(node, 'name');
|
||||
|
||||
this.save('Pipeline execution finished')
|
||||
.then(() => {
|
||||
this.result.setSuccess(!err);
|
||||
this._callback(err || null, this.result);
|
||||
this.logger.debug(`Operation ${name} (${id}) failed: ${err}`);
|
||||
this.setAttribute(job, 'status', 'fail');
|
||||
this.clearOldMetadata(job);
|
||||
this.onPipelineComplete(err);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onOperationCanceled = function(op) {
|
||||
var job = this.core.getParent(op);
|
||||
this.setAttribute(job, 'status', 'canceled');
|
||||
this.runningJobs--;
|
||||
this.logger.debug(`${this.getAttribute(job, 'name')} has been canceled`);
|
||||
this.onPipelineComplete();
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onPipelineComplete = function(err) {
|
||||
var name = this.getAttribute(this.activeNode, 'name'),
|
||||
msg = `"${this.pipelineName}" `;
|
||||
|
||||
if (err) {
|
||||
this.runningJobs--;
|
||||
}
|
||||
|
||||
this.pipelineError = this.pipelineError || err;
|
||||
|
||||
this.logger.debug(`${this.runningJobs} remaining jobs`);
|
||||
if ((this.pipelineError || this.canceled) && this.runningJobs > 0) {
|
||||
var action = this.pipelineError ? 'error' : 'cancel';
|
||||
this.logger.info(`Pipeline ${action}ed but is waiting for the running ` +
|
||||
'jobs to finish');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentForkName) {
|
||||
// notify client that the job has completed
|
||||
this.sendNotification(`"${this.pipelineName}" execution completed on branch "${this.currentForkName}"`);
|
||||
}
|
||||
|
||||
if (this.pipelineError) {
|
||||
msg += 'failed!';
|
||||
} else if (this.canceled) {
|
||||
msg += 'canceled!';
|
||||
} else {
|
||||
msg += 'finished!';
|
||||
}
|
||||
|
||||
this.isDeleted().then(isDeleted => {
|
||||
if (!isDeleted) {
|
||||
|
||||
this.logger.debug(`Pipeline "${name}" complete!`);
|
||||
this.setAttribute(this.activeNode, 'endTime', Date.now());
|
||||
this.setAttribute(this.activeNode, 'status',
|
||||
(this.pipelineError ? 'failed' :
|
||||
(this.canceled ? 'canceled' : 'success')
|
||||
)
|
||||
);
|
||||
|
||||
this._finished = true;
|
||||
this.resultMsg(msg);
|
||||
this.save('Pipeline execution finished')
|
||||
.then(() => {
|
||||
this.result.setSuccess(!this.pipelineError);
|
||||
this._callback(this.pipelineError || null, this.result);
|
||||
})
|
||||
.fail(e => this.logger.error(e));
|
||||
} else { // deleted!
|
||||
this.logger.debug('Execution has been deleted!');
|
||||
this.result.setSuccess(!this.pipelineError);
|
||||
this._callback(this.pipelineError || null, this.result);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.isDeleted = function () {
|
||||
var activeId = this.core.getPath(this.activeNode);
|
||||
|
||||
// Check if the current execution has been deleted
|
||||
return this.project.getBranchHash(this.branchName)
|
||||
.then(hash => this.updateNodes(hash))
|
||||
.then(() => this.core.loadByPath(this.rootNode, activeId))
|
||||
.then(node => {
|
||||
var deleted = node === null,
|
||||
msg = `Verified that execution is ${deleted ? '' : 'not '}deleted`;
|
||||
|
||||
this.logger.debug(msg);
|
||||
return deleted;
|
||||
})
|
||||
.fail(e => this.logger.error(e));
|
||||
.fail(err => this.logger.error(err));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onPipelineDeleted = function () {
|
||||
var msg = `${this.pipelineName} has been deleted`;
|
||||
this.resultMsg(msg);
|
||||
this.result.setSuccess(true);
|
||||
this._callback(null, this.result);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.executeReadyOperations = function () {
|
||||
@@ -257,270 +416,31 @@ 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 || this.canceled) {
|
||||
if (this.runningJobs === 0) {
|
||||
this.onPipelineComplete();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Execute all ready operations
|
||||
readyOps.forEach(jobId => {
|
||||
delete this.incomingCounts[jobId];
|
||||
});
|
||||
this.logger.info(`Found ${readyOps.length} ready job(s)`);
|
||||
readyOps.reduce((prev, jobId) => {
|
||||
return prev.then(() => this.executeOperation(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
|
||||
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 => {
|
||||
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]
|
||||
});
|
||||
|
||||
if (this.debug) {
|
||||
outputs.push({
|
||||
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(jobId, node, hash);
|
||||
})
|
||||
.fail(e => {
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'fail');
|
||||
this.logger.info(`Setting ${jobId} status to "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}"`);
|
||||
|
||||
this.outputLineCount[jobId] = 0;
|
||||
// Set the job status to 'running'
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'running');
|
||||
this.core.setAttribute(this.nodes[jobId], 'stdout', '');
|
||||
this.logger.info(`Setting ${jobId} status to "running" (${this.currentHash})`);
|
||||
this.logger.debug(`Making a commit from ${this.currentHash}`);
|
||||
this.save(`Started "${name}" operation in ${this.pipelineName}`)
|
||||
.then(() => 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 job = this.nodes[jobId],
|
||||
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');
|
||||
|
||||
// Handle the \b
|
||||
// TODO
|
||||
stdout += output;
|
||||
this.core.setAttribute(job, 'stdout', stdout);
|
||||
return this.save(`Received stdout for ${jobName}`);
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (info.status === 'CREATED' || info.status === 'RUNNING') {
|
||||
setTimeout(
|
||||
this.watchOperation.bind(this, executor, hash, opId, jobId),
|
||||
ExecutePipeline.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 => {
|
||||
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.core.setAttribute(this.nodes[jobId], 'status', 'fail');
|
||||
this.logger.info(`Setting ${jobId} status to "fail"`);
|
||||
this.onPipelineComplete(`Operation "${opId}" failed! ${JSON.stringify(info)}`); // Failed
|
||||
} else {
|
||||
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);
|
||||
this.logger.info(`Setting ${nodeId} data to ${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'),
|
||||
var name = this.getAttribute(opNode, 'name'),
|
||||
nextPortIds = this.getOperationOutputIds(opNode),
|
||||
jNode = this.core.getParent(opNode),
|
||||
resultPorts,
|
||||
@@ -528,8 +448,11 @@ define([
|
||||
hasReadyOps;
|
||||
|
||||
// Set the operation to 'success'!
|
||||
this.core.setAttribute(jNode, 'status', 'success');
|
||||
this.clearOldMetadata(jNode);
|
||||
this.runningJobs--;
|
||||
this.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(() => {
|
||||
@@ -546,11 +469,11 @@ define([
|
||||
.forEach(pair => { // [ resultPort, nextPort ]
|
||||
var result = pair[0],
|
||||
next = pair[1],
|
||||
hash = this.core.getAttribute(result, 'data');
|
||||
hash = this.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.setAttribute(next, 'data', hash);
|
||||
this.logger.info(`Setting ${jobId} data to ${hash}`);
|
||||
});
|
||||
|
||||
@@ -583,255 +506,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.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);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createCustomLayers = function (node, files) {
|
||||
var isCustomLayer = {},
|
||||
metaDict = this.core.getAllMetaNodes(this.rootNode),
|
||||
metanodes,
|
||||
customLayers,
|
||||
code;
|
||||
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
// Get all the custom layers
|
||||
metanodes.forEach(node => {
|
||||
if (this.core.getAttribute(node, 'name') === 'CustomLayer') {
|
||||
isCustomLayer[this.core.getPath(node)] = true;
|
||||
}
|
||||
});
|
||||
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;
|
||||
};
|
||||
|
||||
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 ]
|
||||
//
|
||||
// For each input,
|
||||
// - create the deserializer
|
||||
// - put it in inputs/<name>/init.lua
|
||||
// - copy the data asset to /inputs/<name>/init.lua
|
||||
inputs = inputs
|
||||
.filter(pair => !!this.core.getAttribute(pair[2], 'data')); // remove empty inputs
|
||||
|
||||
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
|
||||
// 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;
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createPointers = function (node, files, cb) {
|
||||
var pointers,
|
||||
nIds;
|
||||
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
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
|
||||
.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);
|
||||
});
|
||||
};
|
||||
|
||||
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,
|
||||
PtrCodeGen.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": "name",
|
||||
"displayName": "Execution name",
|
||||
"description": "Optional name for this execution instance",
|
||||
"value": "",
|
||||
"valueType": "string",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"displayName": "Debug Mode",
|
||||
"description": "Allow for operation editing after creation",
|
||||
"value": false,
|
||||
"valueType": "boolean",
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
require 'paths'
|
||||
|
||||
local path = 'inputs/<%= name %>/<%= filename %>'
|
||||
local abs_path = paths.concat('inputs', '<%= name %>', '<%= filename %>')
|
||||
|
||||
<%= code %>
|
||||
@@ -1,12 +0,0 @@
|
||||
-- load custom layers
|
||||
require './custom-layers'
|
||||
|
||||
-- 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,22 +1,18 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
/**
|
||||
* Generated by PluginGenerator 0.14.0 from webgme on Sun Mar 20 2016 16:49:12 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'SimpleNodes/SimpleNodes',
|
||||
'SimpleNodes/Constants',
|
||||
'deepforge/layer-args',
|
||||
'./dimensionality',
|
||||
'deepforge/utils',
|
||||
'underscore',
|
||||
'text!./metadata.json'
|
||||
], function (
|
||||
PluginBase,
|
||||
Constants,
|
||||
createLayerDict,
|
||||
dimensionality,
|
||||
utils,
|
||||
_,
|
||||
metadata
|
||||
) {
|
||||
@@ -42,11 +38,15 @@ define([
|
||||
GenerateArchitecture.prototype = Object.create(PluginBase.prototype);
|
||||
GenerateArchitecture.prototype.constructor = GenerateArchitecture;
|
||||
|
||||
GenerateArchitecture.prototype.getTemplateSettings = function () {
|
||||
return null;
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.main = function () {
|
||||
this.addCustomLayersToMeta();
|
||||
this.LayerDict = createLayerDict(this.core, this.META);
|
||||
this.uniqueId = 2;
|
||||
this._oldTemplateSettings = _.templateSettings;
|
||||
this.varnames = {net: true};
|
||||
return PluginBase.prototype.main.apply(this, arguments);
|
||||
};
|
||||
|
||||
@@ -60,25 +60,36 @@ define([
|
||||
.forEach(node => this.META[this.core.getAttribute(node, 'name')] = node);
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.hoist = function (code) {
|
||||
this.definitions.push(code);
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createOutputFiles = function (tree) {
|
||||
var layers = tree[Constants.CHILDREN],
|
||||
//initialLayers,
|
||||
result = {},
|
||||
code = 'require \'nn\'\n';
|
||||
code = '';
|
||||
|
||||
this.definitions = [
|
||||
'require \'nn\'',
|
||||
'require \'rnn\''
|
||||
];
|
||||
|
||||
//initialLayers = layers.filter(layer => layer[Constants.PREV].length === 0);
|
||||
// Add an index to each layer
|
||||
layers.forEach((l, index) => l[INDEX] = index);
|
||||
|
||||
// Define custom layers
|
||||
if (this.getCurrentConfig().standalone) {
|
||||
this.logger.debug('Generating layer definitions');
|
||||
code += this.genLayerDefinitions(layers);
|
||||
}
|
||||
|
||||
this.logger.debug('Generating architecture code...');
|
||||
code += this.genArchCode(layers);
|
||||
this.logger.debug('Prepending hoisted code...');
|
||||
code = this.definitions.join('\n') + '\n' + code;
|
||||
|
||||
result[tree.name + '.lua'] = code;
|
||||
_.templateSettings = this._oldTemplateSettings; // FIXME: Fix this in SimpleNodes
|
||||
this.logger.debug(`Finished generating ${tree.name}.lua`);
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -89,10 +100,38 @@ define([
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.genRawArchCode = function (layers, name) {
|
||||
var result = '';
|
||||
if (layers.length > 1) {
|
||||
return this.createSequential(layers[0], name).code;
|
||||
} else if (name) {
|
||||
result = `\nlocal ${name} = `;
|
||||
}
|
||||
result += this.createLayer(layers[0]);
|
||||
return result;
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.getVarName = function (base) {
|
||||
// Check "this.varnames"
|
||||
var name = base,
|
||||
i = 2;
|
||||
|
||||
while (this.varnames[name]) {
|
||||
name = base + '_' + (i++);
|
||||
}
|
||||
this.varnames[name] = true;
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createLayer = function (layer) {
|
||||
var args = this.createArgString(layer);
|
||||
return `nn.${layer.name}${args}`;
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createSequential = function (layer, name) {
|
||||
var next = layer[Constants.NEXT][0],
|
||||
args,
|
||||
template,
|
||||
snippet,
|
||||
snippets,
|
||||
code = `\nlocal ${name} = nn.Sequential()`,
|
||||
@@ -107,10 +146,8 @@ define([
|
||||
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;
|
||||
snippet = this.createLayer(layer);
|
||||
code += `\n${name}:add(${snippet})`;
|
||||
|
||||
}
|
||||
|
||||
@@ -119,7 +156,7 @@ define([
|
||||
|
||||
this.logger.debug(`detected fork of size ${layer[Constants.NEXT].length}`);
|
||||
snippets = layer[Constants.NEXT].map(nlayer =>
|
||||
this.createSequential(nlayer, 'net_'+(this.uniqueId++)));
|
||||
this.createSequential(nlayer, this.getVarName('net')));
|
||||
code += '\n' + snippets.map(snippet => snippet.code).join('\n');
|
||||
|
||||
// Make sure all snippets end at the same concat node
|
||||
@@ -157,7 +194,7 @@ define([
|
||||
|
||||
// merge the elements in the group
|
||||
if (snippets.length) { // prepare next iteration
|
||||
result = this.createSequential(next, 'net_'+(this.uniqueId++));
|
||||
result = this.createSequential(next, this.getVarName('net'));
|
||||
code += result.code;
|
||||
group = [result];
|
||||
this.logger.debug('updating group ('+ snippets.length+ ' left)');
|
||||
@@ -177,11 +214,75 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.getValue = function (arg, layer) {
|
||||
var content = layer[arg];
|
||||
|
||||
if (typeof content === 'object') { // layer as arg
|
||||
if (content[Constants.CHILDREN].length) {
|
||||
// Generate the code for the children of layer[arg]
|
||||
var name = this.getVarName(utils.abbr(arg)),
|
||||
layers;
|
||||
|
||||
this.logger.debug(`Adding layer arg for ${arg} (${layer.name})`);
|
||||
try {
|
||||
layers = this.genRawArchCode(layer[arg][Constants.CHILDREN], name);
|
||||
} catch (e) {
|
||||
this.logger.error(`Layer arg creation failed: ${e}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// hoist layer definitions to the top of the file
|
||||
this.hoist(layers);
|
||||
return name;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
GenerateArchitecture.prototype.createArgString = function (layer) {
|
||||
return '(' + this.LayerDict[layer.name]
|
||||
.map(arg => layer[arg.name])
|
||||
.filter(GenerateArchitecture.isSet)
|
||||
.join(', ') + ')';
|
||||
var setters = this.LayerDict[layer.name].setters,
|
||||
setterNames = Object.keys(this.LayerDict[layer.name].setters),
|
||||
base = layer[Constants.BASE],
|
||||
desc,
|
||||
fn,
|
||||
layerCode,
|
||||
args,
|
||||
i;
|
||||
|
||||
this.logger.debug(`Creating arg string for ${layer.name}`);
|
||||
args = this.LayerDict[layer.name].args
|
||||
.map(arg => this.getValue(arg.name, layer));
|
||||
|
||||
for (i = args.length; i--;) {
|
||||
if (GenerateArchitecture.isSet(args[i])) {
|
||||
break;
|
||||
}
|
||||
args.pop();
|
||||
}
|
||||
|
||||
layerCode = '(' + args.map(arg => GenerateArchitecture.isSet(arg) ? arg : 'nil')
|
||||
.join(', ') + ')';
|
||||
|
||||
// Add any setters
|
||||
// For each setter, check if it has been changed (and needs to be set)
|
||||
for (i = setterNames.length; i--;) {
|
||||
desc = setters[setterNames[i]];
|
||||
if (desc.setterType === 'const') {
|
||||
// if the value is not the default, add the given fn
|
||||
if (layer[setterNames[i]] !== base[setterNames[i]]) {
|
||||
fn = desc.setterFn[layer[setterNames[i]]];
|
||||
layerCode += `:${fn}()`;
|
||||
}
|
||||
} else if (layer[setterNames[i]] !== null && layer[setterNames[i]] !== undefined) {
|
||||
fn = desc.setterFn;
|
||||
layerCode += `:${fn}(${layer[setterNames[i]]})`;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug(`Created nn.${layer.name}${layerCode}`);
|
||||
return layerCode;
|
||||
};
|
||||
|
||||
GenerateArchitecture.isSet = function (value) {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/* globals define */
|
||||
define([
|
||||
'SimpleNodes/Constants',
|
||||
'deepforge/lua'
|
||||
], function(
|
||||
Constants,
|
||||
luajs
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var dimensionality = function(node) {
|
||||
var transform = node.dimensionalityTransform;
|
||||
return dimensionality[transform](node);
|
||||
};
|
||||
|
||||
// If 'same', return the input dimensions
|
||||
dimensionality.same = function(node) {
|
||||
var prev = node[Constants.PREV][0];
|
||||
return dimensionality(prev);
|
||||
};
|
||||
|
||||
dimensionality.custom = function(node) {
|
||||
var luaFn = node.calculateDimensionality,
|
||||
cxt = luajs.newContext(),
|
||||
layer, // lua layer
|
||||
bin,
|
||||
dims;
|
||||
|
||||
cxt.loadStdLib();
|
||||
// - cross compile to js
|
||||
bin = cxt.loadString(luaFn);
|
||||
bin(); // load the calc fn to global context
|
||||
|
||||
// Create the layer
|
||||
layer = new luajs.types.LuaTable();
|
||||
var attrs = Object.keys(node).filter(attr => attr.indexOf('_') !== 0);
|
||||
for (var i = attrs.length; i--;) {
|
||||
layer.set(attrs[i], node[attrs[i]]);
|
||||
}
|
||||
cxt._G.set('layer', layer);
|
||||
|
||||
// call the function with layer and input dimensions
|
||||
bin = cxt.loadString('return calcDims(layer)');
|
||||
dims = bin()[0]; // TODO: Add support for multiple dimensions
|
||||
|
||||
// TODO: return a fn if it depends on the previous value
|
||||
|
||||
return dims;
|
||||
};
|
||||
|
||||
return dimensionality;
|
||||
});
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"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,
|
||||
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -78,29 +78,32 @@ define([
|
||||
}
|
||||
|
||||
// Get the base node
|
||||
dataNode = this.core.createNode({
|
||||
base: baseType,
|
||||
parent: this.activeNode
|
||||
});
|
||||
|
||||
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;
|
||||
this.getArtifactsDir()
|
||||
.then(targetDir => {
|
||||
dataNode = this.core.createNode({
|
||||
base: baseType,
|
||||
parent: targetDir
|
||||
});
|
||||
}
|
||||
|
||||
getName.then(name => this.core.setAttribute(dataNode, 'name', name))
|
||||
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);
|
||||
@@ -112,5 +115,14 @@ define([
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"id": "ImportArtifact",
|
||||
"name": "ImportArtifact",
|
||||
"name": "Import Artifact",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"class": "glyphicon glyphicon-cloud-upload",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
/**
|
||||
* Generated by PluginGenerator 0.14.0 from webgme on Thu Mar 10 2016 04:16:02 GMT-0600 (CST).
|
||||
*/
|
||||
|
||||
define([
|
||||
'deepforge/layer-args',
|
||||
'deepforge/lua',
|
||||
'./nn',
|
||||
'plugin/PluginBase',
|
||||
'text!./metadata.json'
|
||||
], function (
|
||||
LayerDict,
|
||||
luajs,
|
||||
createNNSearcher,
|
||||
PluginBase,
|
||||
@@ -55,14 +49,21 @@ define([
|
||||
return callback('Torch code not provided.', this.result);
|
||||
}
|
||||
|
||||
this.addCustomLayersToMeta();
|
||||
|
||||
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
|
||||
@@ -73,15 +74,17 @@ define([
|
||||
this.loadNNMock();
|
||||
|
||||
// Cross compile to js and run
|
||||
src = 'require \'nn\'\n' + src; // guarantee it loads nn
|
||||
this.bin = this.context.loadString(src);
|
||||
this.bin();
|
||||
|
||||
this.afterExecution();
|
||||
|
||||
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 =>
|
||||
@@ -89,25 +92,36 @@ define([
|
||||
);
|
||||
};
|
||||
|
||||
ImportTorch.prototype.addCustomLayersToMeta = function () {
|
||||
// Add custom layers to the metamodel
|
||||
var metanodes = this.core.getAllMetaNodes(this.rootNode),
|
||||
name;
|
||||
|
||||
Object.keys(metanodes).map(id => metanodes[id])
|
||||
.filter(node => this.core.isTypeOf(node, this.META.Layer))
|
||||
.forEach(layer => {
|
||||
name = this.core.getAttribute(layer, 'name');
|
||||
if (!this.META[name]) {
|
||||
this.logger.debug(`Adding ${name} to the meta`);
|
||||
this.META[name] = layer;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Create the 'nn' shim and add it to the global context
|
||||
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) {
|
||||
if (name === 'nn') {
|
||||
return lib;
|
||||
} else {
|
||||
return () => {};
|
||||
}
|
||||
}]);
|
||||
|
||||
// Some scripts don't include `require 'nn'`. I may have to add the
|
||||
// "nn" package to the global scope...
|
||||
};
|
||||
|
||||
ImportTorch.prototype.afterExecution = function () {
|
||||
// TODO
|
||||
};
|
||||
|
||||
return ImportTorch;
|
||||
|
||||
+170
-17
@@ -7,35 +7,91 @@ define([
|
||||
], function(
|
||||
createLayerDict,
|
||||
assert,
|
||||
luajs
|
||||
lua
|
||||
) {
|
||||
'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,
|
||||
connsFrom = {};
|
||||
|
||||
// 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 stringify = function(table) {
|
||||
var strings = table.array.map(val => {
|
||||
if (val instanceof lua.types.LuaTable) {
|
||||
return stringify(val);
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
return '{' + strings.join(', ') + '}';
|
||||
};
|
||||
|
||||
var allConnectedTo = function(current) {
|
||||
var connectedIds = {},
|
||||
node,
|
||||
id;
|
||||
|
||||
while (current.length) {
|
||||
node = current.shift();
|
||||
id = core.getGuid(node);
|
||||
if (connectedIds[id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
connectedIds[id] = node;
|
||||
if (connsFrom[id]) {
|
||||
current = current.concat(connsFrom[id]);
|
||||
}
|
||||
}
|
||||
// Return an array of all things connected to the
|
||||
// given node
|
||||
return Object.keys(connectedIds).map(key => connectedIds[key]);
|
||||
};
|
||||
|
||||
var connect = function(src, dst) {
|
||||
var conn = core.createNode({
|
||||
var conn,
|
||||
id;
|
||||
|
||||
conn = core.createNode({
|
||||
parent: parent,
|
||||
base: META.Connection
|
||||
});
|
||||
core.setPointer(conn, 'src', src);
|
||||
core.setPointer(conn, 'dst', dst);
|
||||
// Record this
|
||||
id = core.getGuid(src);
|
||||
if (!connsFrom[id]) {
|
||||
connsFrom[id] = [];
|
||||
}
|
||||
connsFrom[id].push(conn, dst);
|
||||
};
|
||||
|
||||
// nn drawing library
|
||||
var Layer = function(base, attrs, args) {
|
||||
this._base = base;
|
||||
this._attrs = attrs;
|
||||
|
||||
for (var i = 0; i < attrs.length; i++) {
|
||||
this[attrs[i].name] = args[i];
|
||||
}
|
||||
|
||||
// inputs/outputs used for being added to containers
|
||||
this._values = args;
|
||||
this._cachedNode = null;
|
||||
this._inputs = [this._node()];
|
||||
this._outputs = [this._node()];
|
||||
@@ -44,6 +100,10 @@ define([
|
||||
Layer.prototype._node = function() {
|
||||
var name,
|
||||
node,
|
||||
nodes,
|
||||
cntr,
|
||||
layer,
|
||||
cntrName,
|
||||
value;
|
||||
|
||||
if (this._cachedNode) {
|
||||
@@ -59,16 +119,42 @@ define([
|
||||
|
||||
for (var i = this._attrs.length; i--;) {
|
||||
name = this._attrs[i].name;
|
||||
value = this[name];
|
||||
if ((typeof value) === 'object') {
|
||||
// special lua.js object
|
||||
value = value.valueOf();
|
||||
}
|
||||
value = this._values[i];
|
||||
|
||||
// TODO: Update this to check if inferred and the value matches
|
||||
// our inferred value. If so, skip it
|
||||
if (value !== undefined/*&& !this._attrs[i].infer*/) {
|
||||
core.setAttribute(node, name, value);
|
||||
if (value instanceof lua.types.LuaTable) {
|
||||
layer = value.get('_node');
|
||||
if (layer) { // layer arg!
|
||||
// add all the inputs, outputs (and connected elements) to
|
||||
// be in an "Architecture" node in the current node
|
||||
cntr = core.createNode({
|
||||
base: META.Architecture,
|
||||
parent: node
|
||||
});
|
||||
cntrName = `${name} (${this._base})`;
|
||||
logger.debug(`Naming layer arg ${cntrName}`);
|
||||
core.setAttribute(cntr, 'name', cntrName);
|
||||
// Move all connecting elements of the value to
|
||||
// the cntr
|
||||
nodes = allConnectedTo(layer._inputs.concat(layer._outputs));
|
||||
for (var j = nodes.length; j--;) {
|
||||
core.moveNode(nodes[j], cntr);
|
||||
}
|
||||
core.setPointer(node, name, cntr);
|
||||
logger.debug(`Moving ${nodes.length} to ${name}(${this._base})`);
|
||||
} else { // Something like {1, 2, 3}
|
||||
value = stringify(value);
|
||||
logger.debug(`Setting ${name} to ${value} (${this._base})`);
|
||||
core.setAttribute(node, name, value);
|
||||
}
|
||||
} else { // attribute value
|
||||
if ((typeof value) === 'object') {
|
||||
// special lua.js object
|
||||
value = value.valueOf();
|
||||
}
|
||||
if (value !== undefined) {
|
||||
logger.debug(`Setting ${name} to ${value} (${this._base})`);
|
||||
core.setAttribute(node, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +162,13 @@ define([
|
||||
return node;
|
||||
};
|
||||
|
||||
Layer.prototype._setAttribute = function(name, self, value) {
|
||||
var node = this._node();
|
||||
logger.info(`Setting ${name} to ${value}`);
|
||||
core.setAttribute(node, name, value);
|
||||
return self;
|
||||
};
|
||||
|
||||
// Each container will have `inputs` and `outputs`
|
||||
var Container = function() {
|
||||
// inputs and outputs are webgme nodes
|
||||
@@ -142,15 +235,59 @@ define([
|
||||
Sequential: Sequential
|
||||
};
|
||||
|
||||
var getValue = function(txt) {
|
||||
if (txt === 'true') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (txt === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/^\d+$/.test(txt)) {
|
||||
return +txt;
|
||||
}
|
||||
|
||||
return txt;
|
||||
};
|
||||
|
||||
var addSetterMethods = function(table, attr, dict) {
|
||||
var desc = dict[attr],
|
||||
layer = table.get('_node'),
|
||||
vals,
|
||||
value,
|
||||
fn;
|
||||
|
||||
if (desc.setterType === 'arg') {
|
||||
fn = desc.setterFn;
|
||||
table.set(fn, layer._setAttribute.bind(layer, attr));
|
||||
} else {
|
||||
vals = Object.keys(desc.setterFn);
|
||||
for (var i = vals.length; i--;) {
|
||||
fn = desc.setterFn[vals[i]];
|
||||
value = getValue(vals[i]);
|
||||
table.set(fn, layer._setAttribute.bind(layer, attr, table, value));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var CreateLayer = function(type) {
|
||||
var res = luajs.newContext()._G,
|
||||
var res = lua.newContext()._G,
|
||||
attrs = [].slice.call(arguments, 1),
|
||||
ltGet = lua.types.LuaTable.prototype.get,
|
||||
setters = [],
|
||||
args = [],
|
||||
node;
|
||||
|
||||
if (LayerDict[type]) {
|
||||
args = LayerDict[type].args;
|
||||
setters = Object.keys(LayerDict[type].setters);
|
||||
}
|
||||
|
||||
if (LAYERS[type]) {
|
||||
node = new LAYERS[type](LayerDict[type] || [], attrs);
|
||||
node = new LAYERS[type](args, attrs);
|
||||
} else { // Call generic Layer with type name
|
||||
node = new Layer(type, LayerDict[type] || [], attrs);
|
||||
node = new Layer(type, args, attrs);
|
||||
}
|
||||
|
||||
res.set('_node', node);
|
||||
@@ -165,6 +302,22 @@ define([
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add setters
|
||||
// look up the setters
|
||||
for (var i = setters.length; i--;) {
|
||||
addSetterMethods(res, setters[i], LayerDict[type].setters);
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
@@ -175,7 +328,7 @@ define([
|
||||
}
|
||||
|
||||
// TODO: Create the nn object
|
||||
var nn = luajs.newContext()._G,
|
||||
var nn = lua.newContext()._G,
|
||||
names = Object.keys(LayerDict);
|
||||
|
||||
for (var i = names.length; i--;) {
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'child_process',
|
||||
'path',
|
||||
'q',
|
||||
'fs',
|
||||
'module',
|
||||
'plugin/PluginBase'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
childProcess,
|
||||
path,
|
||||
Q,
|
||||
fs,
|
||||
module,
|
||||
PluginBase
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
var SEEDS_DIR = path.join(path.dirname(module.uri), '..', '..', 'seeds');
|
||||
|
||||
/**
|
||||
* Initializes a new instance of UpdateLibrarySeed.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin UpdateLibrarySeed.
|
||||
* @constructor
|
||||
*/
|
||||
var UpdateLibrarySeed = 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}
|
||||
*/
|
||||
UpdateLibrarySeed.metadata = pluginMetadata;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
UpdateLibrarySeed.prototype = Object.create(PluginBase.prototype);
|
||||
UpdateLibrarySeed.prototype.constructor = UpdateLibrarySeed;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
UpdateLibrarySeed.prototype.main = function (callback) {
|
||||
// get the root hash
|
||||
var name = this.projectName,
|
||||
version;
|
||||
|
||||
// get the name and validate
|
||||
return this.getLibraryVersion()
|
||||
.then(vers => {
|
||||
version = vers;
|
||||
return this.checkForLibName(name);
|
||||
})
|
||||
.then(valid => {
|
||||
if (!valid) {
|
||||
var err = `Invalid library name "${name}"`;
|
||||
this.logger.error(err);
|
||||
return callback(err, this.result);
|
||||
}
|
||||
return this.updateSeed(name);
|
||||
})
|
||||
.then(() => this.recordVersion(name, version))
|
||||
.then(() => {
|
||||
this.logger.info(`Finished updating library seed for ${name}`);
|
||||
this.result.setSuccess(true);
|
||||
callback(null, this.result);
|
||||
})
|
||||
.fail(err => callback(err, this.result));
|
||||
};
|
||||
|
||||
UpdateLibrarySeed.prototype.getLibraryVersion = function () {
|
||||
var version,
|
||||
config = this.getCurrentConfig(),
|
||||
vnames = ['major', 'minor', 'patch'],
|
||||
bumpIndex,
|
||||
newVersion;
|
||||
|
||||
version = (this.core.getAttribute(this.rootNode, 'version') || '0.0.0')
|
||||
.split('.').map(num => parseInt(num));
|
||||
bumpIndex = vnames.indexOf(config.releaseType);
|
||||
version[bumpIndex]++;
|
||||
|
||||
newVersion = version.join('.');
|
||||
this.core.setAttribute(this.rootNode, 'version', newVersion);
|
||||
return this.save(`Bumped version to ${newVersion}`).then(() => newVersion);
|
||||
};
|
||||
|
||||
UpdateLibrarySeed.prototype.checkForLibName = function (name) {
|
||||
// check for the library name from the fs
|
||||
return Q.nfcall(fs.readdir, SEEDS_DIR).then(seeds => seeds.indexOf(name) !== -1);
|
||||
};
|
||||
|
||||
UpdateLibrarySeed.prototype.updateSeed = function (seedName) {
|
||||
var deferred = Q.defer(),
|
||||
err,
|
||||
job = childProcess.spawn('webgme', ['new', 'seed', seedName], {
|
||||
cwd: path.dirname(module.uri)
|
||||
});
|
||||
|
||||
this.logger.info(`Updating ${seedName} seed`);
|
||||
job.on('error', _err => {
|
||||
err = _err;
|
||||
});
|
||||
|
||||
job.on('exit', code => {
|
||||
if (!code) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject(err || code);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
UpdateLibrarySeed.prototype.recordVersion = function (seed, version) {
|
||||
var versionPath = path.join(SEEDS_DIR, seed, 'version.txt');
|
||||
this.logger.info(`Updating ${seed} version (${version})`);
|
||||
return Q.nfcall(fs.writeFile, versionPath, version);
|
||||
};
|
||||
|
||||
return UpdateLibrarySeed;
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"id": "UpdateLibrarySeed",
|
||||
"name": "UpdateLibrarySeed",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": true,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": [
|
||||
{
|
||||
"name": "releaseType",
|
||||
"displayName": "Release Type",
|
||||
"description": "Specify major, minor or patch release",
|
||||
"value": "minor",
|
||||
"valueItems": [
|
||||
"major",
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"valueType": "string",
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
var path = require('path'),
|
||||
Q = require('q'),
|
||||
fs = require('fs'),
|
||||
exists = require('exists-file'),
|
||||
utils = require('../../common/utils'),
|
||||
NO_LOG_FOUND = '';
|
||||
|
||||
var JobLogManager = function(logger, config) {
|
||||
this.rootDir = path.join(config.blob.fsDir, 'log-storage');
|
||||
this.logger = logger.fork('JobLogManager');
|
||||
this._onCopyFinished = {};
|
||||
};
|
||||
|
||||
JobLogManager.prototype._getFilePath = function(jInfo) {
|
||||
this.logger.debug(`getting file path for ${jInfo.job} in ${jInfo.project} on ${jInfo.branch}`);
|
||||
var jobId = jInfo.job.replace(/\//g, '_'),
|
||||
filename = `${jobId}.txt`;
|
||||
|
||||
return path.join(this.rootDir, jInfo.project, jInfo.branch, filename);
|
||||
};
|
||||
|
||||
JobLogManager.prototype.exists = function(jobInfo) {
|
||||
var filename = this._getFilePath(jobInfo);
|
||||
return Q.nfcall(exists, filename);
|
||||
};
|
||||
|
||||
JobLogManager.prototype.mkdirIfNeeded = function(dir) {
|
||||
return Q.nfcall(exists, dir).then(exist => {
|
||||
if (!exist) {
|
||||
this.logger.debug('making dir:', dir);
|
||||
return Q.nfcall(fs.mkdir, dir)
|
||||
.catch(() => this.logger.debug(`dir already created: ${dir}`));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
JobLogManager.prototype._copyFile = function(src, dst) {
|
||||
return Q.nfcall(exists, src).then(exists => {
|
||||
if (!exists) {
|
||||
this.logger.warn(`Cannot copy file from ${src}. File doesn't exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
return this.mkdirIfNeeded(path.dirname(dst)).then(() => {
|
||||
var deferred = Q.defer(),
|
||||
stream = fs.createReadStream(src).pipe(fs.createWriteStream(dst));
|
||||
|
||||
stream.on('error', deferred.reject);
|
||||
stream.on('finish', deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Copy one branch info to the next
|
||||
// Could optimize this to symlink until data appended...
|
||||
JobLogManager.prototype.migrate = function(migrationInfo, jobIds) {
|
||||
// Recursively copy the srcBranch dir to the dstBranch dir
|
||||
// Should probably use streams...
|
||||
// Need to block appends to the given files so they are not written
|
||||
// to until they have finished copying...
|
||||
// TODO
|
||||
var jobs,
|
||||
src,
|
||||
dst,
|
||||
i;
|
||||
|
||||
for (i = jobIds.length; i--;) {
|
||||
this._onCopyFinished[jobIds[i]] = [];
|
||||
}
|
||||
|
||||
// Copy the job files and evaluate each of the finish functions
|
||||
this.logger.info('migrating from ' + migrationInfo.srcBranch + ' to '+ migrationInfo.dstBranch);
|
||||
return Q.all(jobIds.map(jobId => {
|
||||
src = this._getFilePath({
|
||||
project: migrationInfo.project,
|
||||
branch: migrationInfo.srcBranch,
|
||||
job: jobId
|
||||
});
|
||||
dst = this._getFilePath({
|
||||
project: migrationInfo.project,
|
||||
branch: migrationInfo.dstBranch,
|
||||
job: jobId
|
||||
});
|
||||
return this._copyFile(src, dst).then(() => {
|
||||
jobs = this._onCopyFinished[jobId];
|
||||
for (var j = jobs.length; j--;) {
|
||||
jobs[j]();
|
||||
}
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
JobLogManager.prototype._appendTo = function(filename, logs) {
|
||||
return Q.nfcall(exists, filename).then(exists => {
|
||||
var promise = Q().then(() => '');
|
||||
if (exists) {
|
||||
promise = Q.nfcall(fs.readFile, filename, 'utf8');
|
||||
}
|
||||
|
||||
return promise.then(content => {
|
||||
// This could be optimized to not re-read/write the whole file each time...
|
||||
var lines = utils.resolveCarriageReturns(content + logs);
|
||||
return Q.nfcall(fs.writeFile, filename, lines.join('\n'));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
JobLogManager.prototype.appendTo = function(jobInfo, logs) {
|
||||
var filename = this._getFilePath(jobInfo),
|
||||
branchDirname = path.dirname(filename),
|
||||
projDirname = path.dirname(branchDirname);
|
||||
|
||||
this.logger.info(`Appending content to ${filename}`);
|
||||
// Make directory if needed
|
||||
return this.mkdirIfNeeded(this.rootDir)
|
||||
.then(() => this.mkdirIfNeeded(projDirname))
|
||||
.then(() => this.mkdirIfNeeded(branchDirname))
|
||||
.then(() => this._appendTo(filename, logs));
|
||||
};
|
||||
|
||||
JobLogManager.prototype.getLog = function(jobInfo) {
|
||||
var filename = this._getFilePath(jobInfo);
|
||||
|
||||
this.logger.info(`Getting log content from ${filename}`);
|
||||
return this.exists(jobInfo)
|
||||
.then(exists => {
|
||||
if (exists) {
|
||||
return Q.nfcall(fs.readFile, filename);
|
||||
}
|
||||
return NO_LOG_FOUND;
|
||||
});
|
||||
};
|
||||
|
||||
JobLogManager.prototype.delete = function(jobInfo) {
|
||||
var filename = this._getFilePath(jobInfo);
|
||||
|
||||
return this.exists(jobInfo)
|
||||
.then(exists => {
|
||||
if (exists) {
|
||||
this.logger.debug(`Removing file ${filename}`);
|
||||
return Q.nfcall(fs.unlink, filename);
|
||||
}
|
||||
this.logger.info(`${filename} doesn't exist. No need to delete...`);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = JobLogManager;
|
||||
@@ -0,0 +1,93 @@
|
||||
/*jshint node:true*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var express = require('express'),
|
||||
JobLogManager = require('./JobLogManager'),
|
||||
router = express.Router();
|
||||
|
||||
/**
|
||||
* Called when the server is created but before it starts to listening to incoming requests.
|
||||
* N.B. gmeAuth, safeStorage and workerManager are not ready to use until the start function is called.
|
||||
* (However inside an incoming request they are all ensured to have been initialized.)
|
||||
*
|
||||
* @param {object} middlewareOpts - Passed by the webgme server.
|
||||
* @param {GmeConfig} middlewareOpts.gmeConfig - GME config parameters.
|
||||
* @param {GmeLogger} middlewareOpts.logger - logger
|
||||
* @param {function} middlewareOpts.ensureAuthenticated - Ensures the user is authenticated.
|
||||
* @param {function} middlewareOpts.getUserId - If authenticated retrieves the userId from the request.
|
||||
* @param {object} middlewareOpts.gmeAuth - Authorization module.
|
||||
* @param {object} middlewareOpts.safeStorage - Accesses the storage and emits events (PROJECT_CREATED, COMMIT..).
|
||||
* @param {object} middlewareOpts.workerManager - Spawns and keeps track of "worker" sub-processes.
|
||||
*/
|
||||
function initialize(middlewareOpts) {
|
||||
var logger = middlewareOpts.logger.fork('JobLogsAPI'),
|
||||
ensureAuthenticated = middlewareOpts.ensureAuthenticated,
|
||||
gmeConfig = middlewareOpts.gmeConfig,
|
||||
logManager = new JobLogManager(logger, gmeConfig);
|
||||
|
||||
logger.debug('initializing ...');
|
||||
|
||||
// Ensure authenticated can be used only after this rule.
|
||||
router.use('*', function (req, res, next) {
|
||||
// This header ensures that any failures with authentication won't redirect.
|
||||
res.setHeader('X-WebGME-Media-Type', 'webgme.v1');
|
||||
next();
|
||||
});
|
||||
|
||||
// Use ensureAuthenticated if the routes require authentication. (Can be set explicitly for each route.)
|
||||
router.use('*', ensureAuthenticated);
|
||||
|
||||
router.get('/:project/:branch/:job', function (req, res/*, next*/) {
|
||||
// Retrieve the job logs for the given job
|
||||
logManager.getLog(req.params).then(log => {
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.send(log);
|
||||
});
|
||||
});
|
||||
|
||||
router.patch('/:project/:branch/:job', function (req, res/*, next*/) {
|
||||
var logs = req.body.patch;
|
||||
logger.info(`Received append request for ${req.params.job} in ${req.params.project}`);
|
||||
logManager.appendTo(req.params, logs)
|
||||
.then(() => res.send('Append successful'))
|
||||
.catch(err => logger.error(`Append failed: ${err}`));
|
||||
});
|
||||
|
||||
router.delete('/:project/:branch/:job', function (req, res/*, next*/) {
|
||||
logManager.delete(req.params).then(() => res.send('delete successful'));
|
||||
});
|
||||
|
||||
router.post('/migrate/:project/:srcBranch/:dstBranch', function (req, res/*, next*/) {
|
||||
var jobs = req.body.jobs;
|
||||
logManager.migrate(req.params, jobs)
|
||||
.then(() => res.send('migration successful'))
|
||||
.fail(err => logger.error(err));
|
||||
});
|
||||
|
||||
logger.debug('ready');
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before the server starts listening.
|
||||
* @param {function} callback
|
||||
*/
|
||||
function start(callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the server stopped listening.
|
||||
* @param {function} callback
|
||||
*/
|
||||
function stop(callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
initialize: initialize,
|
||||
router: router,
|
||||
start: start,
|
||||
stop: stop
|
||||
};
|
||||
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.
@@ -0,0 +1 @@
|
||||
0.2.0
|
||||
Arquivo binário não exibido.
@@ -0,0 +1 @@
|
||||
0.3.0
|
||||
Arquivo binário não exibido.
Arquivo binário não exibido.
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