Comparar commits

...

118 Commits

Autor SHA1 Mensagem Data
Brian Broll d12ab2a352 v1.4.1 2017-08-11 00:57:30 -05:00
Brian Broll 149be7cd18 Fixed node unload in table layer children. Fixes #1033 (#1034) 2017-08-11 15:54:52 +10:00
Brian Broll e84a946302 updated quickstart in docs to use docker-compose 2017-08-09 01:10:19 -05:00
Brian Broll ce5f890cec Added dependencies to compose file. Fixes #1031 (#1032)
* WIP #1031 added container dependency info

* WIP #1031 updated quickstart
2017-08-09 11:41:46 +10:00
yogeshVU f9551b18c9 Added docker-compose file (#1030) 2017-07-25 18:21:23 -05:00
Chandan Rai 66b794cdbe corrected typo (#1029) 2017-07-23 22:56:09 -04:00
Brian Broll 89edea8c15 updated install instructions to include nvm 2017-07-22 18:41:56 -05:00
Brian Broll a533669dd9 v1.4.0 2017-06-02 18:04:38 -05:00
Brian Broll c9935b52a3 minor updates to gitignore and npmignore 2017-06-02 18:02:52 -05:00
Brian Broll 1e2a634d5f Added customization opts to code editor. Fixes #1025 (#1026)
* WIP #1025 Added context menu to text editor

* WIP #1025 added updating of font size

* WIP #1025 Added theme customization

* WIP #1025 Added keybinding customization

* WIP #1025 Saving editor settings with the guest user

* WIP #1025 Added Tomorrow night theme

* WIP #1025 removed old comments

* WIP #1025 set the keybindings on initial load

* WIP #1025 fixed indentations
2017-05-12 17:20:56 -05:00
Brian Broll b755b78209 Added user feedback info to the readme 2017-05-09 18:57:07 -05:00
Brian Broll 1ce0d70a25 replaced /home/irishninja with $HOME var 2017-05-04 11:03:26 -05:00
Brian Broll 819b541140 v1.3.0 2017-04-30 21:14:32 -05:00
Brian Broll 4fe4dd1bd4 edit name on single click in opint editor. Fixes #1022 (#1023) 2017-04-28 08:07:05 -05:00
Brian Broll 4c876dd767 Added hover buttons to operation in op int editor. Fixes #1020 (#1021)
* WIP Added some hover handlers

* WIP #1020 Finished the hover handling for the op int editor
2017-04-27 19:21:42 -05:00
Brian Broll 236c1aa93d Sorted nn layers in add layer dialog. Fixes #1009 (#1019) 2017-04-26 22:00:40 -05:00
Brian Broll 73f7ba38ba Refactored code gen out of ExecuteJob. Fixes #1014 (#1018)
* WIP created GenerateJob plugin for generating exec code

* WIP moved templates to GenerateJob

* WIP #1014 GenerateJob tests passing

* WIP #1014 Added attribute file test

* WIP #1014 Added more GenerateJob tests

* WIP #1014 removed extra logs

* WIP #1014 Follow base for plugin, namespace

* WIP #1014 Added CodeGen component settings

* WIP #1014 Removed component settings

* WIP #1014 Fixed the loading of inputs

* WIP #1014 Fixed namespace detection

* WIP #1014 Fixed code gen of job refs

* WIP #1014 Fixed 2nd degree code gen

* WIP #1014 Removed old code generation file

* WIP #1014 Fixed code linting issues

* WIP #1014 Fixed blob fetch error handling

* WIP #1014 fixed error handling for bad blob fetch

* WIP #1014 Updated the commit hash for the ptr code gen

* WIP #1014 save after forwarding data from the successful job

* WIP #1014 updated error msg on blob retrieval fail

* WIP #1014 fixed code linting issues
2017-04-26 21:46:17 -05:00
Brian Broll ff541be7a0 Added docker files and updated docs. Fixes #990 (#1017)
* WIP #990 initial commit of dockerfiles

* WIP #990 Everything but CMD working

* Added deepforge config, added torch install

* Updated the comment describing the image

* WIP Added cuda worker docker image

* WIP #990 Added wget and blob configuration

* WIP #990 Fixed worker connection dockerfile

* WIP #990 Fixed docker image issues

* WIP #990 Reuse the node_modules directory

* WIP #990 Reuse node modules in docker image

* WIP #990 Added entrypoint and cmd files

* WIP #990 Updated the standalone image

* WIP #990 Removed standalone docker file

This should be replaced with something like docker compose instead

* WIP #990 Added deployment instructions

* WIP #990 Added additional comments about volumes, gpu
2017-04-22 21:26:18 -05:00
Brian Broll 900ecae537 Removed early return when handling deps. Fixes #1015 (#1016) 2017-04-21 21:03:22 -05:00
Brian Broll b54b6110ef Quoted generated attributes file. Fixes #1006 (#1013) 2017-04-16 15:57:49 -05:00
Brian Broll 61fb3cad96 Trim trailing whitespace in execution names. Fixes #1011 (#1012)
* WIP #1011 Added tests for execution name creation

* WIP #1011 trim whitespace in execution name
2017-04-15 21:02:54 -05:00
Brian Broll 9fe83de22e Removed the server timeout for file upload. Fixes #997 (#1000) 2017-04-15 10:21:39 -05:00
Brian Broll a1438e2993 Fixed criterion selection bug. Fixes #1007 (#1008) 2017-04-12 21:02:59 -05:00
Brian Broll 1b7a6e56fd Removed unordered list at the top
No longer needed for navigation (it was used for nav in the wiki and was just a left over artifact of this)
2017-04-10 16:10:39 -05:00
Brian Broll 4d7a158973 Added sphinx docs to /docs Fixes #988 (#1005)
* WIP #988 Added "Getting Started" section

* WIP #988 Added some reference docs for cli and config

* WIP #988 Added quick start

* WIP #988 moved installation to deployment section

* WIP #988 started adding initial tutorial

* WIP #988 Finished hello, cifar10

* WIP #988 Added custom operation info

* WIP #988 Added operation feedback reference

* WIP #988 added custom layer docs

* WIP #988 bolded the setter fn best practice

* WIP #988 'DeepForge Fundamentals' -> 'Fundamentals'

* WIP #988 fixed the inline code snippets

* WIP #988 added custom data types

* WIP #988 added images to custom operation docs

* WIP #988 updated operation feedback section

* WIP #988 Added example of finished train operation

* WIP #988 Added custom layer images

* WIP #988 Added image for editing prim types

* WIP #988 deepforge->DeepForge and updated styling

* WIP #988 Added images for getting started

* WIP #988 Added more images to hello world example

* WIP #988 added test results
2017-04-10 07:24:50 -05:00
Brian Broll 012c9fe287 Cleaned up cifar10 example. Fixes #1003 (#1004) 2017-04-09 22:00:55 -05:00
Brian Broll 0b4f0145e7 moved exit code outside of canceling the job. Fixes #1001 (#1002) 2017-04-07 22:10:23 -05:00
Brian Broll 4b87d5867d v1.2.0 2017-04-01 22:38:48 -05:00
Brian Broll e53b684b45 Only retrieve member registry from nodes in set. Fixes #998 (#999)
* Only retrieve member registry from nodes in set. Fixes #998

* WIP #998 Added test case
2017-03-31 07:35:59 -05:00
Brian Broll c8e9dfd0e0 Wait for mongo to listen on port before starting server. Fixes #991 (#996)
* WIP #991

* WIP #991 wait for mongo to listen on the given port

* WIP #991 refactored to make more testable

* WIP #991 fixed port bug

* WIP #991 added test for mongodb start race cond
2017-03-23 15:34:51 -05:00
Brian Broll 8c88bca52b Added http:// to worker url if doesnt exist. Fixes #994 (#995) 2017-03-22 19:39:18 -05:00
Brian Broll de50123046 Updated the arch editor deletion. Fixes #992 (#993) 2017-03-20 18:22:53 -05:00
Brian Broll 619a0b8db0 Set custom operation colors. Fixes #980 (#989)
* WIP #980 Added set operation color button

* WIP #980 Added color selector for operations

* WIP #980 hide color picker on close

* WIP #980 Added displayColor for operations

* WIP #980 made it so you can actually set the color

* WIP #980 use the operation color in op int editor

* WIP #980 Removed displayColor from generated attributes

* WIP #980 Removed displayColor from displayed job attributes

* WIP #980 Added display color to add operation fab

* WIP #980 Updated color in add node dialog

* WIP #980 Selected the color palette

* WIP #980 Fixed linter issues

* WIP #980 updated cifar10 and project seeds
2017-03-18 12:02:51 -05:00
Brian Broll 71d79a6d07 Updated the torch install error message when no internet. Fixes #986 (#987)
* Updated the torch install error message when no internet. Fixes #986

* WIP #986 removed extra console log
2017-03-12 12:13:20 -05:00
Brian Broll 4ca379f8b9 Removed the notice from the readme (#985) 2017-02-28 19:32:07 -06:00
Brian Broll 53ce61caa6 v1.1.0 2017-02-28 19:18:17 -06:00
Brian Broll 26398a38d0 Don't show monitor icon if connection. Fixes #983 (#984) 2017-02-26 07:57:45 -06:00
Brian Broll 11d0a5e113 Set the Output op color to light grey. Fixes #189 (#982) 2017-02-05 17:37:11 -06:00
Brian Broll 1d58dd19ab Added axis labeling to graph feedback. Fixes #979 (#981)
* WIP #979 Added 'xlabel', 'ylabel' to deepforge.Graph

* WIP #979 Added 'xlabel','ylabel' to the Graph metadata

* WIP #979 Updated graph to show xlabel, ylabel
2017-02-04 22:05:16 -06:00
Brian Broll 98848ef048 v1.0.0 2017-01-31 21:54:27 -06:00
Brian Broll 1f75cab8eb Replaced npm start w/ server start script. Fixes #977 (#978) 2017-01-31 21:50:34 -06:00
Brian Broll bbef573418 Added pipeline info to code sections for executor. Fixes #975 (#976)
* Added pipelineName, pipelineInputNames fields. Fixes #975

* WIP #975 Added logs, removed unnecessary code

* WIP #975 Updated export pipeline to cli
2017-01-31 21:38:02 -06:00
Brian Broll a172af208c Added 'repository' to package.json. Fixes #973 (#974)
* Added 'repository' to package.json. Fixes #973

* WIP #973 Updated tests
2017-01-29 14:22:10 -06:00
Brian Broll bd28a61901 Changed argument order to the ext config struct. Fixes #971 (#972) 2017-01-29 12:06:24 -06:00
Brian Broll 349f3c9954 Added extension config to the main function invoc. Fixes #969 (#970) 2017-01-29 09:20:16 -06:00
Brian Broll e44d74e90f Changed export pipeline icon to 'launch' icon. Fixes #967 (#968) 2017-01-28 12:56:42 -06:00
Brian Broll f4df050f25 Aliased 'npm start' to 'deepforge start'. Fixes #965 (#966) 2017-01-28 12:49:14 -06:00
Brian Broll dc2df294a4 Added support for export pipeline extension objects. Fixes #963 (#964)
* WIP #963 Added support for extension objects (not just fns)

* WIP #963 Updated the cli exporter to extension object format
2017-01-28 12:49:05 -06:00
Brian Broll c3f9ce7c59 Updated export pipeline config. Fixes #961 (#962)
* WIP #961 Updated the config dialog to run the pipeline after

* WIP #961 Added static inputs detection

* WIP #961 Fixed linting issues
2017-01-28 10:40:37 -06:00
Brian Broll 0b43ddfb40 Added 'getAllDefinitions' utility fn for Export:Pipeline extensions. Fixes #959 (#960)
* WIP #959 Added getAllDefinitions utility function

* WIP #959 Updated cli code to use new utility function

* WIP #959 Removed unused import
2017-01-28 10:32:15 -06:00
Brian Broll 5cedd2cb30 Updated the arch data transform in the arch index. Fixes #957 (#958)
* WIP #957 Added custom arch index control

* WIP #957 Use correct template in arch index widget
2017-01-25 20:02:42 -06:00
Brian Broll a6625cfadc Added extension architecture. Fixes #945 (#956)
* WIP #945 Added some structure for the extensions support

* WIP #945 Added ext installation using npm

* WIP #945 Added some structure for the extensions support

* WIP #945 Added ext installation using npm

* WIP #945 Restructured export formats

* WIP #945 Added ExportFormat:Pipeline installation

* WIP #945 Updated extension installer

* WIP #945 Fixed format.js format path

* WIP #945 Added reinstalling message

* WIP #945 Updated the default Basic CLI

* WIP Updated deserializers and cli deserializer

* WIP #945 Added simple static ext config

* WIP #945 Added custom config dialog for exporting pipelines

* WIP #945 Added dynamic updating based on config

* WIP #945 Added section headers

* WIP #945 Renamed to Export:Pipeline

* WIP #945 moved cli export to first option

* WIP #945 Renamed plugin 'GenerateExecFile' -> 'Export'

* WIP #945 Renamed GenExecFile -> Export

* WIP #945 Added 'deepforge extension remove X'

* WIP #945 Added 'list' command for extensions

* WIP #945 fixed minor linting issues

* WIP #945 aliased remove/rm, list/ls

* WIP #945 Moved gen installation fn to utils/extender

* WIP #945 Added ext reinstall script on postinstall

* WIP #945 don't prompt if no options

* WIP #945 Updated the plugin name in registry

* WIP #945 Updated format template

* WIP #945 Fixed cli tests

* WIP #945 Removed unused main method

* WIP #945 Fixed linter issues
2017-01-25 19:27:39 -06:00
Brian Broll 1b45ae52fc Fixed port update on node update. Fixes #923 (#954) 2017-01-15 19:34:14 -06:00
Brian Broll f74d65b00e Detect export canceled and exit. Fixes #952 (#953) 2017-01-14 18:23:31 -06:00
Brian Broll 1f735179ea Set theme jekyll-theme-cayman 2017-01-10 08:54:55 -06:00
Brian Broll cdd46abdee Removed explicit exec tray height setting. Fixes #950 (#951) 2016-12-30 15:45:53 -06:00
Brian Broll be50c8dca3 Added defaults for format, static inputs on cancel. Fixes #948 (#949) 2016-12-30 15:41:37 -06:00
Brian Broll bab0cc66de Added support for exporting pipeline w/ static inputs. Fixes #946 (#947)
* WIP Updated GenerateExecFile to accept different formats

* WIP Fixed minor style issues

* WIP Added 'static' options for pipeline inputs

* WIP Fixed the circular ref issue

* WIP Changed export pipeline to use the gme config dialog

* WIP Updated the config structure and Q usage

* WIP Download export files on completion

* WIP Don't prompt for export pipeline info unless there are opts

* WIP Added support for static inputs

* WIP Simplified cli plugin and removed old comments

* WIP #946 Fixed pipeline export tests

* WIP Fixed result autodownload when not text
2016-12-30 12:49:05 -06:00
Brian Broll 58c1f401a4 Filter displaying options for input op if constrained by connections. Fixes #703 (#944) 2016-12-23 16:05:54 -06:00
Brian Broll c780e7a801 Updated artifact descriptors to provide type info. Fixes #942 (#943) 2016-12-23 16:05:05 -06:00
Brian Broll 4da87b5806 v0.22.0 2016-12-20 12:21:54 -06:00
Brian Broll f73d9d6958 Updated to webgme v2.8.0. Fixes #936 (#941)
* WIP #936 Added webgme-executor-worker

* WIP #936 Updated start-worker.js

* WIP #936 Fixed bug in class generation
2016-12-20 12:14:26 -06:00
Brian Broll fd96d41698 Fixed ValidateArch tests and cleaned up npm scripts. Fixes #939 (#940)
* WIP #939 improved the validate arch plugin

* WIP #939 cleaned up npm scripts
2016-12-20 11:47:13 -06:00
Brian Broll 7ccf8dafc4 Removed tmp dir used for arch validation. Fixes #929 (#930)
* WIP #929 Removed tmp dir after arch validation

* WIP #929 Added test for mkdir/rmdir of tmp dir

* WIP #929 Fixed tests

* WIP #929 Refactored cli tests

* WIP #929 Increased timeout to 3000

* WIP #929 Added logs to help debug travis

* WIP #929 Added more logs

* WIP typo in console log

* WIP #929 Trying webgme-simple-nodes patch

* WIP added assertion to only execute callback once

* WIP removed old logs and updated the dependency

* WIP #929 Removed debug logs and set torch installation to true

* WIP #929 removed extra debug logs
2016-12-19 22:35:03 -06:00
Brian Broll 2ea4f71a1a Updated input/ptr delete action to deselect. Fixes #937 (#938)
* Updated input/ptr delete action to deselect. Fixes #937

* WIP #937 Fixed test

No idea why it broke here... This change should have had nothing to
do with this test...
2016-12-19 19:49:36 -06:00
Brian Broll e702f5cef0 Fixed child process group management and termination. Fixes #934 (#935)
* WIP #934 Added cleanup fn

* WIP #934 Fixed group termination

* WIP #934 Fixed exit code when canceled (SIGINT)

* WIP #934 Added SIGINT exit code
2016-12-19 19:00:08 -06:00
Brian Broll 24d8a02fff Fixed getUniqueName to filter null children. Fixes #932 (#933) 2016-12-11 22:42:53 -06:00
Brian Broll 1917f71856 Added support for validating custom layers. Fixes #928 (#931)
* Added support for validating custom layers. Fixes #928

* WIP #928 Added test for validating custom layers

* WIP #928 Updated torch mock for custom layer
2016-12-10 13:27:53 -06:00
Brian Broll 81e1622621 Added starter kit link to the additional resources 2016-12-10 11:58:00 -06:00
Brian Broll 2e9e5e7889 Updated gitter for the new channel 2016-12-08 16:05:16 -06:00
Brian Broll 017223945f Removed lineOffset from operation attrs in exec. Fixes #924 (#925)
* Removed lineOffset from operation attrs in exec. Fixes #924

* WIP #924 replaced hard coded string with constant
2016-12-07 20:18:57 -06:00
Brian Broll 735cbd4e73 added additional resource links 2016-12-06 21:50:09 -06:00
Brian Broll eef307476d Updated badges urls for the organization transfer (#921) 2016-12-06 15:37:54 -06:00
Brian Broll ae37e17cae Added layer validation and visual cues on error. Fixes #915 (#920)
* WIP #915 Added layer validation plugin

* WIP #915 Validate plugin on territory update (1.5s delay)

* WIP #915 Added validation plugin

* WIP #915 Fixed multiple layer check at once and cleaned msg

* WIP #915 Changed from 'warn' to 'error'

* WIP #915 Fixed flickering issue

* WIP #915 decreased validation delay

* WIP #915 graceful failing if th not installed

* WIP #915 updated for unsupported server side

* WIP #915 Added validation plugin test

* WIP can't seem to push to github...

* WIP removed stupid tmp file
2016-12-06 14:43:59 -06:00
Brian Broll 392ebc286c v0.21.0 2016-12-05 18:43:44 -06:00
Brian Broll 994a8b6f39 Removed custom disable checking. Fixes #917 (#918)
* Removed custom disable checking. Fixes #917

* WIP #917 Removed unused variable

* WIP #917 removed unused var
2016-12-05 18:32:23 -06:00
Brian Broll 3da5cc990f Fixed the width updating of the center panel. Fixes #916 (#919) 2016-12-05 18:29:20 -06:00
Brian Broll bdfad427eb Removed floating add icon from operation interface editor. Fixes #913 (#914) 2016-12-04 17:26:41 -06:00
Brian Broll da3c7ea7f7 Disable CloneAndEdit same as GoToBase. Fixes #911 (#912) 2016-12-04 17:18:51 -06:00
Brian Broll 5a744b665a Updated types to be more consistent w/ torch/lua. Fixes #905 (#910) 2016-12-04 10:30:34 -06:00
Brian Broll 38c891158d Fixed export with multiple inputs/ouputs. Fixes #908 (#909) 2016-12-04 10:22:21 -06:00
Brian Broll 7ddf11318f Added export to single file for local execution. Fixes #13 (#907)
* WIP #13 Added ExportExecution plugin

* WIP #13 Added safer scoping

* WIP #13 Fixed indentation and refactored for operation function creation

* WIP #13 Fixed test setup/takedown

* WIP #13 Refactoring generated code to define fns first and take args

* WIP #13 Fixed single file generation

* WIP #13 Added class, layer initialization

* WIP #13 Added deepforge object support (nop-ed)

* WIP #13 Made a better save name

* WIP #13 added casting input types

* WIP #13 Fixed deepforge.Graph support

* WIP #13 Added ref support and fixed attribute creation

* WIP #13 Added output downloading

* WIP #13 Added export button w/ auto-download

* WIP #13 Removed GenerateExecFile from plugin metadata

* WIP #906 Updated tests and removed unused plugin

* WIP #906 Added test for generated single file

* WIP #906 Fixed code style issues

* WIP #906 Added math test case
2016-12-03 22:02:20 -06:00
Brian Broll 69b9e9e7b6 Added support for inserting layer on a connection. Fixes #542 (#904) 2016-11-29 18:00:59 -06:00
Brian Broll 27c01af612 Removed keyboard switch in footer. Fixes #902 (#903)
* Removed keyboard switch in footer. Fixes #902

* WIP #902 Removed unused vars
2016-11-29 14:50:57 -06:00
Brian Broll bdfe742730 Set log viewer font to bitstream. Fixes #313 (#901) 2016-11-29 14:40:58 -06:00
Brian Broll 28beb1f77e Added "create from copy" when alt pressed on select. Fixes #797 (#900)
* WIP #797 added clone-and-edit button

* WIP #797 Added some support for creating from operation

* WIP #797 Removed unused dependency

* WIP #797 Added custom name on creation and color

* WIP #797 Updated webgme version

* WIP #797 Add 'register'-ing new node

* WIP #797 Removed console log
2016-11-29 14:20:37 -06:00
Brian Broll 779aa04f53 Updated delMetaPointer -> delPointerMeta. Fixes #898 (#899) 2016-11-29 12:50:23 -06:00
Brian Broll 1c1095b553 v0.20.0 2016-11-23 11:44:21 -06:00
Brian Broll 211623ea88 Added expand all nodes option when right clicking the background. Fixes #892 (#897)
* WIP #892 Added basic support for expanding all nodes

* WIP #892 Added the WIDGET_CLASS for use w/ $.contextMenu

* WIP 892 Updated the menu item node names
2016-11-20 08:48:23 -06:00
Brian Broll fac7964c9d Added height rule to op int editor. Fixes #895 (#896) 2016-11-16 19:44:05 -06:00
Brian Broll 63e6ddd82b Updated library version check. Fixes #893 (#894) 2016-11-16 18:09:08 -06:00
Brian Broll 964ebc9714 Added generic container support. Fixes #476 (#891)
* WIP #476 Update 'Container' category and added 'addLayers' set

* WIP #476 Added ContainerLayerDecorator

* WIP #476 Embedded arch editor in decorator

* WIP #476 Shrink the nested layers. Hide on condense

* WIP #476 Fixed positioning of nested layers

* WIP #476 Added background click handling

* WIP #476 alternate colors w/ nested containers

* WIP #476 Fixed nesting viz and layer prompt

* WIP #476 Refactored nested layers

* WIP #476 Added box on hover for nested layers

* WIP #476 Fixed the nested layer hover box position/size

* WIP #476 added button event handling

* WIP #476 Some nested layer creation support

* WIP #476 Fixed nested layer deletion

* WIP #476 prompt layer on first creation

* WIP #476 Fixed horizontal positioning of nested layers

* WIP #476 Fixed nested layer removal

* WIP #476 Fixed reordering nested layers

* WIP #476 Added basic button styling

* WIP #476 Added hint text

* WIP #476 nested layer delete icon

* WIP #476 Fixed hover and added first button

* WIP #476 Fixed the position of the nested layers (w/ attrs)

* WIP #476 more position adjustments

* WIP #476 Fixed add nested layer not updating

* WIP #476 Load nested children eagerly

* WIP #476 Minor aesthetic tweaks

* WIP #476 Fixed attribute field editing (width messing up)

* WIP #476 Added support for container code generation

* WIP #476 Added generic container support to importer

* WIP #476 Moved Concat to 'Simple'

Concat is only implicitly a container in deepforge (the contained
nodes are inferred by the graph structure rather than physically
nested w/in the node)

* WIP #476 Fixed minor code climate issues

* WIP Protected against null id on project unload

* WIP #476 Fixed 'push' of undefined bug in import tests

* WIP #476 Updated tests

* WIP #476 Only reset attr field size if attribute changed

* WIP #476 re-open error fixed

* WIP #476 update territory on creation

* WIP #476 Updated the nested editor height calculation

* WIP #476 Added resnet import example
2016-11-15 18:54:26 -06:00
Brian Broll 8ba2b397bb Replaced 'fs' w/ graceful-fs. Fixes #888 (#889) 2016-11-14 20:17:53 -06:00
Brian Broll 1960be5fec Replaced 'fs' w/ graceful-fs. Fixes #888 2016-11-13 07:41:34 -06:00
Brian Broll e431763a97 Change process dir to project root on start. Fixes #886 (#887) 2016-11-08 17:18:14 -06:00
Brian Broll 24d10fd0c7 Added more logs and error handlers. Fixes #884 (#885)
* Added more logs and error handlers. Fixes #884

* Suppressed extra logs during testing
2016-11-08 17:16:13 -06:00
Brian Broll b4d1e39d06 Handled failed subprocess error on seed creation. Fixes #882 (#883) 2016-11-01 16:36:15 -04:00
Brian Broll fdb1076c93 v0.19.0 2016-10-30 22:09:12 -05:00
Brian Broll 09ec77d98e Fixed double loading of nodes on refresh. Fixes #880 (#881)
* WIP #880 Fixed initial load of 'pipelines'

This was breaking the detection of the retrieval of the node history
from localStorage

* WIP #880 Fixed the root child check to not require node load

* WIP #880 Removed unused var
2016-10-30 23:06:07 -04:00
Brian Broll 0dcb7dfe6b Added button hints on hover. Fixes #878 (#879)
* Added hint text for custom buttons. Fixes #878

* WIP #878 Added 'View output' hint
2016-10-30 15:54:27 -04:00
Brian Broll 3cadcd10db Added hover buttons to layers in arch editor. Fixes #876 (#877) 2016-10-30 10:34:24 -04:00
Brian Broll 633f9908fc Updated nn seed. Made new connection query more robust. Fixes #874 (#875)
* WIP Updated the nn, project, cifar seeds

nn seed needed the 'thumbnail' attribute

* WIP #874 Added the conn type to dialog. Overrode getValidSuccessors
2016-10-29 18:45:08 -04:00
Brian Broll 018f28acf2 Updated client api calls. Fixes #872 (#873)
* WIP #872 setAttributes -> setAttribute; makePointer -> setPointer

* WIP #872 getAttributeSchema -> getAttributeMeta

* WIP #872 delAttributes -> delAttribute

* WIP #872 delMoreNodes -> deleteNode(s)

* WIP #872 createChild -> createNode

* WIP #872 createChild -> createNode; setAttributeSchema->setAttributeMeta

* WIP #872 removeAttributeSchema -> delAttributeMeta

* WIP #872 deleteMetaPointer -> delMetaPointer

* WIP #872 isTypeOf to node obj method

* WIP #872 isTypeOf
2016-10-29 18:05:16 -04:00
Brian Broll d2129cbf81 Updated pipeline editor's empty message. Fixes #868 (#869) 2016-10-29 15:23:17 -04:00
Brian Broll 8edf718201 Added orange left border to active sidebar item. Fixes #870 (#871) 2016-10-29 15:23:06 -04:00
Brian Broll 71a71d09d1 Show operation ports on hover. Fixes #866 (#867)
* Static port position regardless of amt showing. Fixes #864

* WIP #866 don't unhover (hide ports) when selected

* WIP #866 don't unhover when over the operation name

* WIP don't lose hover when over ports

* WIP #866 decreased mouse out delay. Fixed false unhovers

* WIP #866 greatly simplified code

* WIP #866 hide extra ports on conn create or removal
2016-10-29 14:38:01 -04:00
Brian Broll 63a1ec0095 Static port position regardless of amt showing. Fixes #864 (#865) 2016-10-29 11:42:46 -04:00
Brian Broll 997b0dd1c8 Fixed library version bumping. Fixes #862 (#863)
* Fixed library version bumping. Fixes #862

* WIP #862 Updated basic run test
2016-10-29 11:07:04 -04:00
Brian Broll 44de472561 Updated webgme version to v2.6.0 (#861) 2016-10-29 09:58:08 -04:00
Brian Broll 3ba8d71d59 Record creation time on artifact import. Fixes #857 (#860) 2016-10-29 09:12:36 -04:00
Brian Broll d39e32f532 Disabled opening artifacts. Fixes #858 (#859) 2016-10-29 09:12:29 -04:00
Brian Broll 5af00247c6 Added lib check on branch changed. Fixes #855 (#856) 2016-10-28 18:33:21 -04:00
Brian Broll b820d71685 Added screenshot and improved desc. Fixes #853 (#854)
* WIP #853 Added overview screenshot

* Update README.md

* Update README.md
2016-10-28 17:51:48 -04:00
Brian Broll 155d1903a6 Added support for varargs on arch import. Fixes #848 (#852) 2016-10-28 17:11:21 -04:00
Brian Broll 34ee8bc267 Ordered categories in "add layer" dialog. Fixes #850 (#851) 2016-10-28 17:03:07 -04:00
Brian Broll a31043d661 Updated main ui. Fixes #828 (#849)
* WIP #828 Added icons for main nav bar

* WIP #828 Change active node using the icons on the left

* WIP #828 Basic panel loading

* WIP #828 Added visualizers for each option

* WIP #828 Added active indicator of icons

* WIP #828 Added Architecture card

* WIP #828-Removed unnecessary MainViewControl

* WIP #828 Added thumbnail support to arch editor

* WIP #828 Added basic table for ArtifactIndex

* WIP #828 Added artifact delete support

* WIP #828 Removed unnecessary arg from completeTransaction

* WIP #828 Added size info and download link

* WIP #828 italicized the type

* WIP #828 Added SidebarLayout

* WIP #828 Updated nav viz for SideBarLayout

* WIP #828 Removed panel loading from MainView

* WIP #828 Updated breadcrumb header to hide root children

* WIP #828 Updated Index views for each container in root

* WIP #828 Added ArchIndex

* WIP #828 Fixed the sidebar height

* WIP #828 Fixed nav bar size; layout left value

* WIP #828 Renamed Sidebar (layout/viz), added ForwardViz

* WIP #828 Fixed highlight on refresh

* WIP #828 Fixed overlap w/ dropdown menu

* WIP #828 Added support for old projects

* WIP #828 Fixed old project errors

* WIP #828 Fixed flashing when clicking 'HOME' on the breadcrumb path

* WIP #828 Fixed the text alignment for the exec container

* WIP #828 Centered text

* WIP #828 Added creation time to artifact index

* WIP #828 Fixed code climate issues
2016-10-28 16:23:52 -04:00
Brian Broll fc69aad300 Added execution reconnection support. Fixes #821 (#847)
* WIP #821 Added ExecPulse router

* WIP #821 Added heartbeat and fixed job origin router

* WIP #821 Created pulse client and updated JobEditor

* WIP #821 Added execpulse tests

* WIP #821 Updated execjob tests

* WIP #821 Fixed status code

* WIP #821 Fixed resumable detection

* WIP #821 Added CONSTANTS for heartbeat liveliness

* WIP #821 Added reconnect support for executing jobs

* WIP #821 resume execution iff dead and not read only

* WIP #821 Added logging

* WIP #821 Added some pipeline resume support

* WIP #821 Don't resume jobs if readonly

* WIP #821 Refactoring ExecuteJob

* WIP #821 Fixed constant value

* WIP #821 Fixed heartbeat id for pipelines

* WIP #821 Added debug endpoint for all heartbeats

* WIP #821 No longer clearing pulse on complete

* WIP #821 Fixed pipeline resume detection

* WIP #821 Added tests for resuming

* WIP #821 Added / endpoint to job origin

* WIP #821 resume job based on branch, ui status and plugin liveliness

* WIP #821 Added tests for resuming pipelines

* WIP #821 recording metadata before running

* WIP #821 Updated ExecPulse router tests

* WIP #821 stop heartbeat on plugin completion

* WIP #821 Fixed double run of running ops on resume

* WIP #821 Checked for branch origin before resuming pipeline

* WIP #821 Added cmdCount to joblogs metadata

* WIP #821 Updated appendTo call

* WIP #821 Added tests for resuming preparation

* WIP #821 Fixed execjob tests' namespace

* WIP #821 record createdIds for metadata

support deletion of old metadata during resume

* WIP #821 Fixed resuming w/ graphs

* WIP #821 Checked that the jobs can be resumed

* WIP #821 Removed the extra metadata fields

* WIP #821 Resuming executions should run on server

* WIP #821 Fixed linting issues

* WIP #821 Increased pulse stale constant
2016-10-22 16:44:18 -05:00
Brian Broll 15a6e2195e 845 bottle support (#846)
Added support for nn.Bottle. Fixes #845
2016-10-18 10:11:17 -05:00
239 arquivos alterados com 29233 adições e 3776 exclusões
+1
Ver Arquivo
@@ -31,6 +31,7 @@ exclude_paths:
- test/
- src/common/lua.js
- src/common/js-yaml.min.js
- src/visualizers/widgets/Sidebar/lib/
- src/visualizers/widgets/TextEditor/lib/
- src/visualizers/widgets/PipelineIndex/styles/PipelineIndex.css
- src/visualizers/widgets/LineGraph/lib/
+1
Ver Arquivo
@@ -0,0 +1 @@
/node_modules
+3
Ver Arquivo
@@ -35,3 +35,6 @@ test-tmp/
blob-local-storage/
src/seeds/nn/hash.txt
src/seeds/pipeline/hash.txt
notes/
src/worker
+43
Ver Arquivo
@@ -0,0 +1,43 @@
src/worker/tmp
.env
*.swp
*.swo
**.sass-cache
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
tmp/
test-tmp/
blob-local-storage/
src/seeds/nn/hash.txt
src/seeds/pipeline/hash.txt
# docs
images/
notes/
src/worker
+24
Ver Arquivo
@@ -0,0 +1,24 @@
# Dockerfile for running the server itself
FROM node:6.10.1
MAINTAINER Brian Broll <brian.broll@gmail.com>
RUN echo '{"allow_root": true}' > /root/.bowerrc && mkdir -p /root/.config/configstore/ && \
echo '{}' > /root/.config/configstore/bower-github.json
RUN mkdir /deepforge
ADD . /deepforge
WORKDIR /deepforge
RUN cd $(npm root -g)/npm \
&& npm install fs-extra \
&& sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js
RUN ln -s /deepforge/bin/deepforge /usr/local/bin
EXPOSE 8888
# Set up the data storage
RUN deepforge config blob.dir /data/blob && \
deepforge config mongo.dir /data/db
CMD ["deepforge", "start", "--server"]
+65
Ver Arquivo
@@ -0,0 +1,65 @@
# This has torch and cuda support
FROM kaixhin/cuda-torch
MAINTAINER Brian Broll <brian.broll@gmail.com>
# install nodejs v6
RUN groupadd --gid 1000 node \
&& useradd --uid 1000 --gid node --shell /bin/bash --create-home node
# gpg keys listed at https://github.com/nodejs/node#release-team
RUN set -ex \
&& for key in \
9554F04D7259F04124DE6B476D5A82AC7E37093B \
94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
FD3A5288F042B6850C66B31F09FE44734EB7990E \
71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
B9AE9905FFD7803F25714661B63B535A4C206CA9 \
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
56730D5401028683275BD23C23EFEFE93C4CFFFE \
; do \
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
done
ENV NPM_CONFIG_LOGLEVEL info
ENV NODE_VERSION 6.10.1
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
# install deepforge
RUN echo '{"allow_root": true}' > /root/.bowerrc && mkdir -p /root/.config/configstore/ && \
echo '{}' > /root/.config/configstore/bower-github.json
RUN mkdir /deepforge
ADD . /deepforge
WORKDIR /deepforge
RUN cd $(npm root -g)/npm \
&& npm install fs-extra \
&& sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js
RUN ln -s /deepforge/bin/deepforge /usr/local/bin
# configure the worker
RUN deepforge config blob.dir /data/blob && \
deepforge config mongo.dir /data/db && \
deepforge config worker.cache.useBlob false && \
deepforge config worker.cache.dir /deepforge/worker-cache && \
deepforge config torch.dir /root/torch/ && \
git config --global user.email "deepforge-worker@deepforge.org" && \
git config --global user.name "deepforge-worker"
# Update torch
RUN apt-get update && apt-get install sudo wget && \
. /root/torch/install/bin/torch-activate && \
cd /root/torch/ && bash /root/torch/update.sh && \
deepforge update -t
ENTRYPOINT ["deepforge", "start", "--worker"]
CMD ["http://172.17.0.1:8888"]
+28 -18
Ver Arquivo
@@ -1,40 +1,50 @@
[![Release State](https://img.shields.io/badge/state-beta-yellow.svg)](https://img.shields.io/badge/state-beta-yellow.svg)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](./LICENSE)
[![Build Status](https://travis-ci.org/dfst/deepforge.svg?branch=master)](https://travis-ci.org/dfst/deepforge)
[![Join the chat at https://gitter.im/dfst/deepforge](https://badges.gitter.im/dfst/deepforge.svg)](https://gitter.im/dfst/deepforge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Stories in Ready](https://badge.waffle.io/dfst/deepforge.png?label=ready&title=Ready)](https://waffle.io/dfst/deepforge)
[![Build Status](https://travis-ci.org/deepforge-dev/deepforge.svg?branch=master)](https://travis-ci.org/deepforge-dev/deepforge)
[![Join the chat at https://gitter.im/deepforge-dev/deepforge](https://badges.gitter.im/deepforge-dev/deepforge.svg)](https://gitter.im/deepforge-dev/deepforge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Stories in Ready](https://badge.waffle.io/deepforge-dev/deepforge.png?label=ready&title=Ready)](https://waffle.io/deepforge-dev/deepforge)
**Notice**: DeepForge is still a work in progress and in beta! That being said, any contributions and/or feedback is greatly appreciated. If you have any questions, check out the [wiki](https://github.com/dfst/deepforge/wiki/) or drop me a line on the gitter!
Using DeepForge? [Let us know what you think!](https://goo.gl/forms/2pDdCPXoUvkQhVzQ2)
# DeepForge
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:
DeepForge is an open-source visual development environment for deep learning providing end-to-end support for creating deep learning models. This is achieved through providing the ability to design **architectures**, create training **pipelines**, and then execute these pipelines over a cluster. Using a notebook-esque api, users can get real-time feedback about the status of any of their **executions** including compare them side-by-side in real-time.
![overview](images/overview.png "")
Additional features include:
- Graphical architecture editor
- Training/testing pipeline creation
- Distributed pipeline execution
- Real-time pipeline feedback
- Collaborative editing
- Automatic version control.
- Facilitates defining custom layers
## Quick Start
Simply run the following command to install deepforge with its dependencies:
The easiest way to start deepforge is using [docker-compose](https://docs.docker.com/compose/). Using docker-compose, deepforge can be started with
```
curl -o- https://raw.githubusercontent.com/dfst/deepforge/master/install.sh | bash
wget https://raw.githubusercontent.com/deepforge-dev/deepforge/master/docker-compose.yml
docker-compose up
```
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 and other installation options, check out the [docs](http://deepforge.readthedocs.io/en/latest/deployment/overview.html).
```
npm install -g deepforge
```
## Additional Resources
- [Intro to DeepForge Slides](https://docs.google.com/presentation/d/10_y5O3gHXSATfjHVLJg7dOdrz-tAXNWjlxhJ5SlA0ic/edit?usp=sharing)
- [wiki](https://github.com/deepforge-dev/deepforge/wiki) containing overview, installation, configuration and developer information
- [Starter Kit](https://github.com/deepforge-dev/examples/tree/master/starterkit) containing example pipelines demonstrating various deepforge features
- [Examples](https://github.com/deepforge-dev/examples)
Finally, start deepforge with `deepforge start`and 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).
**Note**: running deepforge w/ `deepforge start` will also require [MongoDB](https://www.mongodb.com/download-center?jmp=nav#community) to be installed locally.
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!
- [Datamodel Developer Slides](https://docs.google.com/presentation/d/1hd3IyUlzW_TIPnzCnE-1pdz00Pw8WaIxYiOW_Hyog-M/edit#slide=id.p)
## Interested in contributing?
Contributions are welcome! Either fork the project and submit some PR's or shoot me an email about getting more involved!
Contributions are welcome! There are a couple different ways to contribute to DeepForge:
- Provide user feedback!
- on the [documentation](http://deepforge.readthedocs.io)
- on deepforge and its future development: https://goo.gl/forms/2pDdCPXoUvkQhVzQ2
- Contribute to the project directly by submitting some PR's!
If you have any questions, check out the [wiki](https://github.com/dfst/deepforge/wiki/) or drop me a line on the gitter!
Sponsored by [Digital Reasoning](http://www.digitalreasoning.com/)
+1
Ver Arquivo
@@ -0,0 +1 @@
theme: jekyll-theme-cayman
+6
Ver Arquivo
@@ -4,11 +4,17 @@
var gmeConfig = require('./config'),
webgme = require('webgme'),
path = require('path'),
fs = require('fs'),
rm_rf = require('rimraf'),
gracefulFs = require('graceful-fs'),
myServer;
process.chdir(__dirname);
webgme.addToRequireJsPaths(gmeConfig);
// Patch the 'fs' module to fix 'too many files open' error
gracefulFs.gracefulify(fs);
// Clear seed hash info
['nn', 'pipeline'].map(lib => path.join(__dirname, 'src', 'seeds', lib, 'hash.txt'))
.forEach(file => rm_rf.sync(file));
+87 -53
Ver Arquivo
@@ -1,6 +1,7 @@
#!/usr/bin/env node
var Command = require('commander').Command,
tcpPortUsed = require('tcp-port-used'),
program = new Command(),
childProcess = require('child_process'),
rawSpawn = childProcess.spawn,
@@ -8,7 +9,8 @@ var Command = require('commander').Command,
execSync = childProcess.execSync,
path = require('path'),
fs = require('fs'),
version = require('../package.json').version,
pkgJson = require('../package.json'),
version = pkgJson.version,
exists = require('exists-file'),
DEFAULT_CONFIG = require('./config.json'),
merge = require('lodash.merge'),
@@ -117,32 +119,35 @@ var isLocalUri = function(protocol, uri) {
uri.indexOf(protocol + '://127.0.0.1') === 0;
};
var checkMongo = function(args, notSilent) {
var checkMongo = function(args, notSilent, mongoUri) {
// check the webgme config
var gmeConfig = require('../config'),
mongoUri = gmeConfig.mongo.uri;
var gmeConfig = require('../config');
mongoUri = mongoUri || gmeConfig.mongo.uri;
if (isLocalUri('mongodb', mongoUri)) {
var match = mongoUri.match(/:([0-9]+)/),
port = '80';
if (match) {
port = match[1];
}
// 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);
}
return tcpPortUsed.waitUntilUsed(+port, 100, 1000);
} else if (notSilent) {
console.log(`Cannot start remote mongo locally: ${mongoUri}`);
} else {
console.log(`Using remote mongo: ${mongoUri}`);
}
return Q();
};
var startMongo = function(args, port, silent) {
@@ -208,28 +213,34 @@ var installTorch = function() {
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');
.catch(result => {
var error = result.error || result.stderr ||
`Torch install failed with exit code ${result.code}`;
}
if (result.stderr.includes('unable to access')) {
error = `Could not access the torch repository. Are you ` +
`connected to the internet?\n`;
} else if (result.code === 128) {
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);
});
}
console.error(error);
process.exit(result.code);
})
.then((code, stderr) => {
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();
@@ -242,21 +253,32 @@ var spawn = function(cmd, args, opts) {
spawnOpts = typeof opts === 'object' ? opts : null,
forwardStdin = opts === true,
isOpen = true,
stderr = '',
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.stderr.on('data', data => {
stderr += data;
process.stderr.write(data);
});
job.on('close', code => {
isOpen = false;
if (err) {
deferred.reject(err, code);
if (err || code !== 0) {
deferred.reject({
code: code,
stderr: stderr,
error: err
});
} else {
deferred.resolve(code);
}
});
job.on('error', e => err = e);
job.on('error', e => {
err = e;
});
if (forwardStdin) {
process.stdin.on('data', data => {
@@ -276,42 +298,49 @@ program.command('start')
.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');
var main = path.join(__dirname, 'start-local.js'),
current = Q();
if (args.port) {
process.env.PORT = args.port;
}
if (args.mongo) {
current = current.then(() => checkMongo(args, true));
}
if (args.server) {
checkMongo(args);
main = path.join(__dirname, '..', 'app.js');
spawn('node', [main]);
current = current
.then(() => checkMongo(args))
.then(() => {
main = path.join(__dirname, '..', 'app.js');
return 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]);
}
});
current
.then(() => 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);
current = current.then(() => checkMongo(args));
if (hasTorch()) {
installTorchExtras().then(() => spawn('node', [main]));
current.then(() => installTorchExtras())
.then(() => spawn('node', [main]));
} else {
installTorch();
}
@@ -334,7 +363,7 @@ program
if (!args.torch || args.server) {
if (args.git) {
pkg = 'dfst/deepforge';
pkg = pkgJson.repository.url;
} else {
// Check the version
try {
@@ -447,12 +476,17 @@ program
}
});
// extensions
program
.command('extensions <command>', 'Manage deepforge extensions');
module.exports = function(cmd) {
var cmds = cmd.split(/\s+/).filter(w => !!w);
cmds.unshift('./bin/deepforge');
cmds.unshift('node');
program.parse(cmds);
};
module.exports.checkMongo = checkMongo;
if (require.main === module) {
program.parse(process.argv);
Arquivo executável
+67
Ver Arquivo
@@ -0,0 +1,67 @@
#!/usr/bin/env node
var Command = require('commander').Command,
program = new Command(),
extender = require('../utils/extender');
// Supported commands
// - add
// - remove
// - list
// - update
program
.command('add <project>')
.description('Add an extension to deepforge')
.option('-n, --name <name>', 'Project name (if different from <project>)')
.action(project => {
console.log('loading extension from: ' + project);
extender.install(project)
.then(extConfig =>
console.log(`The ${extConfig.name} extension has been added to deepforge.`))
.fail(err => {
console.error('Could not install extension:\n');
console.error(err);
process.exit(1);
});
});
program
.command('remove <name>').alias('rm')
.description('Remove an extension from deepforge')
.action(name => {
try {
extender.uninstall(name);
console.log(`${name} has been successfully removed!`);
} catch (e) {
console.error('Could not remove extension:');
console.error(e);
process.exit(1);
}
});
program
.command('list').alias('ls')
.description('List installed deepforge extensions')
.action(() => {
var allExtConfigs = extender.getExtensionsConfig(),
types = Object.keys(allExtConfigs),
hasContents = false,
names;
for (var i = types.length; i--;) {
names = Object.keys(allExtConfigs[types[i]]);
if (names.length) {
hasContents = true;
console.log(types[i]);
for (var j = names.length; j--;) {
console.log(` ${names[j]}`);
}
}
}
if (!hasContents) {
console.log('No installed extensions');
}
});
program.parse(process.argv);
+2 -2
Ver Arquivo
@@ -13,8 +13,8 @@ if (gmeConfig.blob.type === 'FS') {
}
process.env.NODE_ENV = 'local';
execJob = spawn('npm', [
'start'
execJob = spawn('node', [
path.join(__dirname, '..', 'app.js')
], env);
execJob.stdout.pipe(process.stdout);
execJob.stderr.pipe(process.stderr);
+6 -15
Ver Arquivo
@@ -6,8 +6,7 @@ var path = require('path'),
spawn = childProcess.spawn,
rm_rf = require('rimraf'),
projectConfig = require(__dirname + '/../config'),
executorSrc = path.join(__dirname, '..', 'node_modules', 'webgme', 'src',
'server', 'middleware', 'executor', 'worker'),
executorSrc = path.join(__dirname, '..', 'node_modules', '.bin', 'webgme-executor-worker'),
id = Date.now(),
workerRootPath = process.env.DEEPFORGE_WORKER_DIR || path.join(__dirname, '..', 'src', 'worker'),
workerPath = path.join(workerRootPath, `worker_${id}`),
@@ -37,7 +36,6 @@ try {
} catch (e) {
// Create dir
childProcess.spawnSync('ln', ['-s', `${__dirname}/../node_modules`, modules]);
return true;
}
// Check torch support
@@ -60,7 +58,7 @@ var startExecutor = function() {
// Start the executor
var execJob = spawn('node', [
'node_worker.js',
executorSrc,
workerConfigPath,
workerTmp
]);
@@ -74,20 +72,13 @@ var createConfigJson = function() {
if (process.argv.length > 2) {
address = process.argv[2];
if (!/^https?:\/\//.test(address)) {
address = 'http://' + address;
}
}
config[address] = {};
fs.writeFile(workerConfigPath, JSON.stringify(config), startExecutor);
};
process.chdir(executorSrc);
fs.mkdir(workerTmp, function() {
// npm install in this directory
var npmInstall = spawn('npm', ['install']);
npmInstall.stdout.pipe(process.stdout);
npmInstall.stderr.pipe(process.stderr);
npmInstall.on('close', function() {
createConfigJson();
});
});
fs.mkdir(workerTmp, createConfigJson);
+24 -6
Ver Arquivo
@@ -2,13 +2,29 @@
"AutoViz": {
"preloadIds": [
"ArchEditor",
"ArchIndex",
"PipelineIndex",
"PipelineEditor",
"OperationEditor",
"ExecutionView"
]
],
"visualizerOverrides": {
"": "ForwardViz",
"MyArtifacts": "ArtifactIndex",
"MyArchitectures": "ArchIndex",
"MyExecutions": "ExecutionIndex",
"MyPipelines": "PipelineIndex"
}
},
"PipelineEditor": {
"itemName": "operation"
},
"ExecutionView": {
"itemName": "job"
},
"ArchEditor": {
"hotkeys": "none",
"itemName": "layer",
"LayerColors": {}
},
"BreadcrumbHeader": {
@@ -32,10 +48,6 @@
"ImportTorch": {
"icon": "import_export",
"priority": -1
},
"GenerateExecFile": {
"icon": "play_for_work",
"priority": -1
}
}
},
@@ -43,7 +55,7 @@
"rootMenuClass": "deepforge-logo",
"rootDisplayName": "DeepForge"
},
"CHFLayout": {
"SidebarLayout": {
"panels": [
{
"id": "WorkerHeader",
@@ -63,6 +75,12 @@
"container": "center",
"DEBUG_ONLY": false
},
{
"id": "Sidebar",
"panel": "Sidebar/SidebarPanel",
"container": "sidebar",
"DEBUG_ONLY": false
},
{
"id": "ForgeActionButton",
"panel": "ForgeActionButton/ForgeActionButton",
+1
Ver Arquivo
@@ -8,6 +8,7 @@ require('dotenv').load({silent: true});
// Add/overwrite any additional settings here
config.server.port = +process.env.PORT || config.server.port;
config.server.timeout = 0;
config.mongo.uri = process.env.MONGO_URI || config.mongo.uri;
config.blob.fsDir = process.env.DEEPFORGE_BLOB_DIR || config.blob.fsDir;
+3 -1
Ver Arquivo
@@ -9,6 +9,7 @@ var config = require('webgme/config/config.default'),
// The paths can be loaded from the webgme-setup.json
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 + '/../src/layouts');
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');
@@ -33,6 +34,7 @@ config.visualization.panelPaths.push(__dirname + '/../src/visualizers/panels');
config.rest.components['execution/logs'] = __dirname + '/../src/routers/JobLogsAPI/JobLogsAPI.js';
config.rest.components['job/origins'] = __dirname + '/../src/routers/JobOriginAPI/JobOriginAPI.js';
config.rest.components['execution/pulse'] = __dirname + '/../src/routers/ExecPulse/ExecPulse.js';
// Visualizer descriptors
config.visualization.visualizerDescriptors.push(__dirname + '/../src/visualizers/Visualizers.json');
@@ -57,7 +59,7 @@ config.requirejsPaths = {
'widgets/FloatingActionButton': './node_modules/webgme-fab/src/visualizers/widgets/FloatingActionButton'
};
config.visualization.layout.default = 'CHFLayout';
config.visualization.layout.default = 'SidebarLayout';
config.mongo.uri = 'mongodb://127.0.0.1:27017/deepforge';
validateConfig(config);
module.exports = config;
+22
Ver Arquivo
@@ -0,0 +1,22 @@
---
services:
mongo:
image: mongo
volumes:
- "$HOME/.deepforge/data:/data/db"
server:
environment:
- "MONGO_URI=mongodb://mongo:27017/deepforge"
image: deepforge/server
ports:
- "8888:8888"
volumes:
- "$HOME/.deepforge/blob:/data/blob"
depends_on:
- mongo
worker:
command: "http://server:8888"
image: deepforge/worker
depends_on:
- server
version: "2"
+1
Ver Arquivo
@@ -0,0 +1 @@
_build
+20
Ver Arquivo
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = deepforge
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+159
Ver Arquivo
@@ -0,0 +1,159 @@
import sphinx_rtd_theme
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# deepforge documentation build configuration file, created by
# sphinx-quickstart on Mon Mar 13 18:56:27 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'DeepForge'
copyright = '2017, Brian Broll'
author = 'Brian Broll'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = ''
# The full version, including alpha/beta/rc tags.
release = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'deepforgedoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'deepforge.tex', 'deepforge Documentation',
'Brian Broll', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'deepforge', 'deepforge Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'deepforge', 'deepforge Documentation',
author, 'deepforge', 'One line description of project.',
'Miscellaneous'),
]
+45
Ver Arquivo
@@ -0,0 +1,45 @@
Dockerized Installation
-----------------------
Each of the components are also available as docker containers. This page outlines the running of each of the main components as docker containers and connecting them as necessary.
Database
~~~~~~~~
First, you can start the mongo container using:
.. code-block:: bash
docker run -d -v /abs/path/to/data:/data/db mongo
where :code:`/abs/path/to/data` is the path to the mongo data location on the host. If running the database in a container, you will need to get the ip address of the given container:
.. code-block:: bash
docker inspect <container id> | grep IPAddr
The :code:`<container id>` is the value returned from the original :code:`docker run` command.
When running mongo in a docker container, it is important to mount an external volume (using the :code:`-v` flag) to be used for the actual data (otherwise the data will be lost when the container is stopped).
Server
~~~~~~
The DeepForge server can be started with
.. code-block:: bash
docker run -d -v $HOME/.deepforge/blob:/data/blob \
-p 8888:8888 -e MONGO_URI=mongodb://172.17.0.2:27017/deepforge \
deepforge/server
where :code:`172.17.0.2` is the ip address of the mongo container and :code:`$HOME/.deepforge/blob` is the path to use for binary DeepForge data on the host. Of course, if the mongo instance is locating at a different location, :code:`MONGO_URI` can be set to this address as well. Also, the first port (:code:`8888`) can be replaced with the desired port to expose on the host.
Worker
~~~~~~
As workers may require GPU access, they will need to use the nvidia-docker plugin. Workers can be created using
.. code-block:: bash
nvidia-docker run -d deepforge/worker http://172.17.0.1:8888
where :code:`http://172.17.0.1:8888` is the location of the DeepForge server to which to connect.
**Note**: The :code:`deepforge/worker` image is packaged with cuda 7.5. Depending upon your hardware and nvidia version, you may need to build your own docker image or run the worker natively.
+130
Ver Arquivo
@@ -0,0 +1,130 @@
Native Installation
===================
Dependencies
------------
First, install `NodeJS <https://nodejs.org/en/>`_ (v6) and `MongoDB <https://www.mongodb.org/>`_. You may also need to install git if you haven't already.
Next, you can install DeepForge using npm:
.. code-block:: bash
npm install -g deepforge
Now, you can check that it installed correctly:
.. code-block:: bash
deepforge --version
DeepForge can now be started with:
.. code-block:: bash
deepforge start
However, the first time DeepForge is started, it will make sure that the deep learning framework is installed (if it isn't found on the host system). This may require you to start DeepForge a couple times; the first time it starts it will install Torch7 and require a terminal restart to update a couple environment variables (like `PATH`). The second time it starts it will install additional torch packages but will not require a terminal restart. Finally, DeepForge will start with all the required dependencies.
Database
~~~~~~~~
Download and install MongoDB from the `website <https://www.mongodb.org/>`_. If you are planning on running MongoDB locally on the same machine as DeepForge, simply start `mongod` and continue to setting up DeepForge.
If you are planning on running MongoDB remotely, set the environment variable "MONGO_URI" to the URI of the Mongo instance that DeepForge will be using:
.. code-block:: bash
MONGO_URI="mongodb://pathToMyMongo.com:27017/myCollection" deepforge start
Server
~~~~~~
The DeepForge server is included with the deepforge cli and can be started simply with
.. code-block:: bash
deepforge start --server
By default, DeepForge will start on `http://localhost:8888`. However, the port can be specified with the `--port` option. For example:
.. code-block:: bash
deepforge start --server --port 3000
Worker
~~~~~~
The DeepForge worker can be started with
.. code-block:: bash
deepforge start --worker
The worker will install dependencies the first time it is run (including torch, if it is not already installed).
To connect to a remote deepforge instance, add the url of the DeepForge server:
.. code-block:: bash
deepforge start --worker http://myaddress.com:1234
Updating
~~~~~~~~
DeepForge can be updated with the command line interface rather simply:
.. code-block:: bash
deepforge update
By default, this will update both DeepForge and the local torch installation. To only update DeepForge, add the `--server` flag:
.. code-block:: bash
deepforge update --server
For more update options, check out `deepforge update --help`!
Manual Installation (Development)
---------------------------------
Installing DeepForge for development is essentially cloning the repository and then using `npm` (node package manager) to run the various start, test, etc, commands (including starting the individual components). The deepforge cli can still be used but must be referenced from `./bin/deepforge`. That is, `deepforge start` becomes `./bin/deepforge start` (from the project root).
DeepForge Server
~~~~~~~~~~~~~~~~
First, clone the repository:
.. code-block:: bash
git clone https://github.com/dfst/deepforge.git
Then install the project dependencies:
.. code-block:: bash
npm install
To run all components locally start with
.. code-block:: bash
./bin/deepforge start
and navigate to `http://localhost:8888` to start using DeepForge!
Alternatively, if jobs are going to be executed on an external worker, run `./bin/deepforge start -s` locally and navigate to `http://localhost:8888`.
DeepForge Worker
~~~~~~~~~~~~~~~~
If you are using `./bin/deepforge start -s` you will need to set up a DeepForge worker (`./bin/deepforge start` starts a local worker for you!). DeepForge workers are slave machines connected to DeepForge which execute the provided jobs. This allows the jobs to access the GPU, etc, and provides a number of benefits over trying to perform deep learning tasks in the browser.
Once DeepForge is installed on the worker, start it with
.. code-block:: bash
./bin/deepforge start -w
Note: If you are running the worker on a different machine, put the address of the DeepForge server as an argument to the command. For example:
.. code-block:: bash
./bin/deepforge start -w http://myaddress.com:1234
Updating
~~~~~~~~
Updating can be done the same as any other git project; that is, by running `git pull` from the project root. Sometimes, the dependencies need to be updated so it is recommended to run `npm install` following `git pull`.
+26
Ver Arquivo
@@ -0,0 +1,26 @@
Overview
========
DeepForge Component Overview
----------------------------
DeepForge is composed of four main elements:
- *Server*: Main component hosting all the project information and is connected to by the clients
- *Database*: MongoDB database containing DeepForge, job queue for the workers, etc
- *Worker*: Slave machine performing the actual machine learning computation
- *Client*: The connected browsers working on DeepForge projects.
Of course, only the *Server*, *Database* (MongoDB) and *Worker* need to be installed. If you are not going to execute any machine learning pipelines, installing the *Worker* can be skipped.
Component Dependencies
----------------------
The following dependencies are required for each component:
- *Server* (NodeJS v6.2.1)
- *Database* (MongoDB v3.0.7)
- *Worker*: NodeJS v6.2.1 (used for job management logic) and `Torch <http://torch.ch/docs/getting-started.html#>`_ (this will be installed automatically by the cli when needed)
- *Client*: We recommend using Google Chrome and are not supporting other browsers (for now). In other words, other browsers can be used at your own risk.
Configuration
-------------
After installing DeepForge, it can be helpful to check out `configuring DeepForge <getting_started/configuration.rst>`_
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 37 KiB

+13
Ver Arquivo
@@ -0,0 +1,13 @@
Custom Data Types
=================
As operation inputs and outputs are strongly typed, DeepForge supports the creation of custom data types to promote flexibility when designing complex pipelines and operations. DeepForge data types can be either primitive types or custom classes. Custom DeepForge primitive types are relatively straight-forward; they can inherit from other types and must implement a serialization and deserialization methods (which may be as simple as :code:`torch.save` and :code:`torch.load`). Custom classes are also relatively simple to define but actually contain their own methods along with serialization and deserialization functions.
New data types can be defined from the operation editor from the dialog for selecting input or output data for the operation. After defining a new class, this class is available from within any of the operations in the DeepForge project.
.. figure:: model_data_editor.png
:align: center
:scale: 55 %
Editing the serialization and deserialization for the "model" type
+87
Ver Arquivo
@@ -0,0 +1,87 @@
Custom Layers
=============
DeepForge supports the creation of custom neural network layers using Torch7 and the easy usage of these layers in the visual architecture editor. Before creating custom layers, it is recommended to read about `creating custom layers in Torch7 <http://torch.ch/docs/developer-docs.html>`_.
A new custom layer can be created from the "add layer dialog" in the architecture editor. When creating a layer, DeepForge provides a code editor for creating custom neural network layers prepopulated with a basic template for defining the custom layer.
After defining the layer in the layer editor, DeepForge will provide this layer in the architecture editor and expose any configurable attributes for the layer. These attributes are parsed from the layer definition.
Best Practices
--------------
Here are a couple best practices to keep in mind when defining custom neural network layers:
- Use type assertions for layer, boolean attributes
- Return :code:`self` when defining setter functions
**Type assertions** should be used when defining layer attributes (ie, constructor arguments or arguments to a setter function). For example, consider the following layer definition for :code:`RecurrentAttention` which accepts an :code:`action` layer argument to its constructor.
.. code-block:: lua
local RecurrentAttention, parent = torch.class("nn.RecurrentAttention", "nn.AbstractSequencer")
function RecurrentAttention:__init(rnn, action, nStep, hiddenSize)
parent.__init(self)
assert(torch.isTypeOf(action, 'nn.Module'))
assert(torch.type(nStep) == 'number')
assert(torch.type(hiddenSize) == 'table')
assert(torch.type(hiddenSize[1]) == 'number', "Does not support table hidden layers" )
self.rnn = rnn
-- we can decorate the module with a Recursor to make it AbstractRecurrent
self.rnn = (not torch.isTypeOf(rnn, 'nn.AbstractRecurrent')) and nn.Recursor(rnn) or rnn
-- samples an x,y actions for each example
self.action = (not torch.isTypeOf(action, 'nn.AbstractRecurrent')) and nn.Recursor(action) or action
self.hiddenSize = hiddenSize
self.nStep = nStep
self.modules = {self.rnn, self.action}
self.output = {} -- rnn output
self.actions = {} -- action output
self.forwardActions = false
self.gradHidden = {}
end
In this example, :code:`assert(torch.isTypeOf(action, 'nn.Module'))` enforces that the :code:`action` variable is another neural network layer. After defining the layer, DeepForge will parse the layer definition and create a visual representation for use in the architecture editor. As this assertion enforces that :code:`action` is a neural network layer, DeepForge will update itself accordingly; in this case, editing the attribute will allow the user to hierarchically create nested neural network architectures to be passed as the :code:`action` argument to the constructor.
.. figure:: recurrent_attention.png
:align: center
:scale: 85 %
RecurrentAttention has attributes for each of the constructor arguments
An example of the generated visual model for the :code:`RecurrentAttention` is provided above. This layer has attributes for each of the constructor arguments defined in its definition. Clicking on the :code:`<none>` value for the :code:`action` attribute will then allow the user to provide layer inputs as shown below.
.. figure:: action_layer.png
:align: center
:scale: 55 %
Creating layer inputs for the "action" variable
The second best practice is to make sure to **return self in any setter functions**. An example of this can be found in the setters in the :code:`SpatialMaxPooling` layer shown below:
.. code-block:: lua
function SpatialMaxPooling:ceil()
self.ceil_mode = true
return self
end
function SpatialMaxPooling:floor()
self.ceil_mode = false
return self
end
Returning :code:`self` in setter functions is a good convention when defining neural network layers in Torch7 as it promotes simple and legible code such as
.. code-block:: lua
net:add(nn.SpatialMaxPooling(5, 5, 2, 2):ceil())
where :code:`net` is a container like a :code:`Sequential` layer. DeepForge enforces this convention and, if it finds a setter function (which also returns :code:`self`) in the layer definition will expose the internal variable (in this case :code:`ceil_mode`) to the user in the visual editor.
+69
Ver Arquivo
@@ -0,0 +1,69 @@
Custom Operations
=================
In this document we will outline the basics of custom operations including the operation editor and operation feedback utilities.
The Basics
----------
Operations are used in pipelines and have named, typed inputs and outputs. When creating a pipeline, if you don't currently find an operation for the given task, you can easily create your own by selecting the `New Operation...` operation from the add operation dialog. This will create a new operation definition and open it in the operation editor. The operation editor has two main parts, the interface editor and the implementation editor.
.. figure:: operation_editor.png
:align: center
:scale: 45 %
Editing the "train" operation provided in the "First Steps" section
The interface editor is provided on the left and presents the interface as a diagram showing the input data and output data as objects flowing into or out of the given operation. Selecting the operation node in the operation interface editor will expand the node and allow the user to add or edit attributes for the given operation. These attributes are exposed when using this operation in a pipeline and can be set at design time - that is, these are set when creating the given pipeline. The interface diagram may also contain light blue nodes flowing into the operation. These nodes represent "references" that the operation accepts as input before running. When using the operation, references will appear alongside the attributes but will allow the user to select from a list of all possible targets when clicked.
.. figure:: operation_interface.png
:align: center
:scale: 85 %
The train operation accepts training data, an architecture and criterion and returns a trained model
On the right of the operation editor is the implementation editor. The implementation editor is a code editor specially tailored for programming the implementations of operations in DeepForge. This includes some autocomplete support for common globals in this context like the :code:`deepforge` and :code:`torch` globals. It also is synchronized with the interface editor and will provide input to the interface editor about unused variables, etc. These errors will present themselves as error or warning highlights on the data in the interface editor. A section of the implementation is shown below:
.. code:: lua
trainer = nn.StochasticGradient(net, criterion)
trainer.learningRate = attributes.learningRate
trainer.maxIteration = attributes.maxIterations
print('training for ' .. tostring(attributes.maxIterations) .. ' iterations (max)')
print('learning rate is ' .. tostring(attributes.learningRate))
print(trainer)
-- Adding the error graph
graph = deepforge.Graph('Training Error') -- creating graph feedback
errLine = graph:line('error')
trainer.hookIteration = function(t, iter, currentErr)
errLine:add(iter, currentErr) -- reporting the current error (will update in real time in DeepForge)
end
trainer:train(trainset)
return {
net = net
}
The "train" operation uses the :code:`StochasticGradient` functionality from the :code:`nn` package to perform stochastic gradient descent. This operation sets all the parameters using values provided to the operation as either attributes or references. In the implementation, attributes are provided by the :code:`attributes` variable and provides access to the user defined attributes from within the implementation. References are treated similarly to operation inputs and are defined in variables of the same name. This can be seen with the :code:`net` and :code:`criterion` variables in the first line. Finally, operations return a table of their named outputs; in this example, it returns a single output named :code:`net`, that is, the trained neural network.
After defining the interface and implementation, we can now use the "train" operation in our pipelines! An example is shown below.
.. figure:: train_operation.png
:align: center
:scale: 85 %
Using the custom "train" operation in a pipeline
Operation feedback
------------------
Operations in DeepForge can generate metadata about its execution. This metadata is generated during the execution and provided back to the user in real-time. An example of this includes providing real-time plotting feedback of the loss function of a model while training. When implementing an operation in DeepForge, this metadata can be created using the :code:`deepforge` global.
.. figure:: graph_example.png
:align: center
:scale: 75 %
An example graph of the loss function while training a neural network
Detailed information about the available operation metadata types can be found in the `reference <reference/feedback_mechanisms.rst>`_.
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 39 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 51 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 108 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 16 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 8.8 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 12 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 35 KiB

+32
Ver Arquivo
@@ -0,0 +1,32 @@
Getting Started
===============
.. _Torch: http://torch.ch
What is DeepForge?
------------------
Deep learning is a very promising, yet complex, area of machine learning. This complexity can both create a barrier to entry for those wanting to get involved in deep learning as well as slow the development of those already comfortable in deep learning.
DeepForge is a development environment for deep learning focused on alleviating these problems. Leveraging the flexibility of Torch_, DeepForge is able to reduce the complexity of using deep learning while still providing advanced features such as defining custom layers.
Design Goals
------------
As mentioned above, DeepForge focuses on two main goals:
1. **Improving the efficiency** of experienced data scientists/researchers in deep learning
2. **Lowering the barrier to entry** for newcomers to deep learning
It is important to highlight that although one of the goals is focused on lowering the barrier to entry, DeepForge is intended to be more than simply an educational tool; that is, it is important not to compromise on flexibility and effectiveness as a research/industry tool in order to provide an easier experience for beginners (that's what forks are for!).
Overview and Features
---------------------
DeepForge provides a collaborative, distributed development environment for deep learning. The development environment is a hybrid visual and textual programming environment. Higher levels of abstraction, such as creating architectures, use visual environments to capture the overall structure of the task while lower levels of abstraction, such as defining custom layers, utilize text environments to maintain the flexibility provided by torch.
Concepts and Terminology
~~~~~~~~~~~~~~~~~~~~~~~~
- *Architecture* - neural network architecture composed of torch defined layers
- *Operation* - essentially a function written in torch (such as `SGD`)
- *Pipeline* - directed acyclic graph composed of operations
- eg, a training pipeline may retrieve and normalize data, train an architecture and return the trained model
- *Execution* - when a pipeline is run, an "execution" is created and reports the status of each operation as it is run (distributed over a number of worker machines)
- *Artifact* - an artifact represents some data (either user uploaded or created during an execution)
+63
Ver Arquivo
@@ -0,0 +1,63 @@
First Steps
===========
DeepForge provides an example project for creating a classifier using the `CIFAR10 <https://www.kaggle.com/c/cifar-10>`_ dataset.
When first opening DeepForge in your browser (at `http://localhost:8888` if following the instructions from the `quick start <getting_started/installation.rst>`_), you will be prompted with a list of projects to open and provided the option to create a new project. For this example, let's click "Create new..." and name our project "hello_cifar".
.. figure:: create_project.png
:align: center
:scale: 65 %
Creating our "hello_cifar" example project
Clicking "Create" will bring us to a prompt for the "seed" for our project. Select "cifar10" from the dropdown and click "Create". This will now create our new project based on the cifar10 example provided with DeepForge.
.. figure:: set_seed.png
:align: center
:scale: 75 %
Selecting the "cifar10" example seed
In this example, we have three main pipelines: :code:`download-normalize`, :code:`train` and :code:`test`. :code:`download-normalize` downloads and prepares our data. The :code:`train` pipeline trains a neural network model on the cifar10 dataset and the :code:`test` pipeline tests our trained model on our test set from the cifar10 dataset.
.. figure:: pipelines.png
:align: center
:scale: 65 %
Three main pipelines in the cifar10 example project
First, we will have to retrieve and prepare the data by running the :code:`download-normalize` pipeline. This can be done by opening the given pipeline then selecting the `Execute Pipeline` option from the action button in the lower right. As soon as that pipeline finishes, we can now use this data to train a neural network.
Next, we can open the :code:`train` pipeline. Before we execute the pipeline we have to set the input training data that we will be using. This is done by selecting the :code:`Input` operation then clicking the value for the :code:`artifact` field. This will provide all the possible options for the input data; for this example, we will want to select the "trainingdata" artifact. After setting the input, we can click on the :code:`train` operation to inspect the hyperparameters we are using and the architecture we are training. Selecting the :code:`Output` operation will allow you to change the name of the resulting artifact of this operation (in this case, a trained model). Finally, we can execute this pipeline like before to train the model.
.. figure:: select_train_data.png
:align: center
:scale: 65 %
Selecting the training data for the input to the training pipeline
As this operation trains, we can view the status by viewing the running execution. The easiest way to view the running execution is by clicking the given execution from the execution tray in the bottom left when viewing the originating pipeline.
.. figure:: training_execution.png
:align: center
:scale: 65 %
Viewing the execution of the training pipeline
Once the model has been trained, we can test the given model using the :code:`test` pipeline. In this pipeline, we have a few more inputs to set: "testing data", "model to test" and the "human-readable class labels". If you aren't clear which operation provides which input, you can simply hover over it's connected port on the :code:`test` operation. This will provide a tooltip with the full name of the input.
.. figure:: test_pipeline.png
:align: center
:scale: 65 %
Viewing the execution of the testing pipeline
After setting the inputs for the :code:`test` pipeline (using the trained model and data from the first two pipelines), we can simply execute this pipeline to test our model. After executing the :code:`test` pipeline, we can view the execution and open the :code:`test` job to view the stdout for the given job. In the :code:`test` operation, this will allow us to view the printed accuracies of the model over each class.
.. figure:: test_results.png
:align: center
:scale: 65 %
Viewing the results of the testing operation
And that's it! We have just trained and tested our first neural network model using DeepForge. Although there are still a lot more advanced features that can be used, this should at least familiarize us with some of the core concepts in DeepForge.
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 47 KiB

+19
Ver Arquivo
@@ -0,0 +1,19 @@
Quick Start
===========
The easiest way to get started quickly with DeepForge is using docker-compose. First, install `docker <https://docs.docker.com/engine/installation/>`_ and `docker-compose <https://docs.docker.com/compose/install/>`_.
Next, download the docker-compose file for DeepForge:
.. code-block:: bash
wget https://raw.githubusercontent.com/deepforge-dev/deepforge/master/docker-compose.yml
Then start DeepForge using docker-compose:
.. code-block:: bash
docker-compose up
and now DeepForge can be used by opening a browser to `http://localhost:8888 <http://localhost:8888>`_!
For detailed instructions about deployment installations, check out our `deployment installation instructions <getting_started/configuration.rst>`_
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 6.7 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 17 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 44 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 55 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 46 KiB

+40
Ver Arquivo
@@ -0,0 +1,40 @@
.. DeepForge documentation master file, created by
sphinx-quickstart on Mon Mar 13 18:56:27 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to DeepForge's documentation!
=====================================
.. toctree::
:maxdepth: 1
:caption: Getting Started
getting_started/getting_started.rst
getting_started/quick_start.rst
getting_started/hello_cifar.rst
.. toctree::
:maxdepth: 1
:caption: Fundamentals
fundamentals/custom_operations.rst
fundamentals/custom_layers.rst
fundamentals/custom_data_types.rst
.. toctree::
:maxdepth: 1
:caption: Deployment
deployment/overview.rst
deployment/native.rst
deployment/dockerized.rst
.. toctree::
:maxdepth: 1
:caption: Reference
reference/cli.rst
reference/configuration.rst
reference/operation_feedback.rst
reference/extensions.rst
+91
Ver Arquivo
@@ -0,0 +1,91 @@
Command Line Interface
======================
This document outlines the functionality of the deepforge command line interface (provided after installing deepforge with :code:`npm install -g deepforge`).
- Installation Configuration
- Starting DeepForge or Components
- Installing and Upgrading Torch
- Update or Uninstall DeepForge
- Managing Extensions
Installation Configuration
--------------------------
Installation configuration including the installation location of Torch7 and data storage locations. These can be edited using the :code:`deepforge config` command as shown in the following examples:
Printing all the configuration settings:
.. code-block:: bash
deepforge config
Printing the value of a configuration setting:
.. code-block:: bash
deepforge config torch.dir
Setting a configuration option, such as :code:`torch.dir` can be done with:
.. code-block:: bash
deepforge config torch.dir /some/new/directory
For more information about the configuration settings, check out the `configuration <configuration.rst>`_ page.
Starting DeepForge Components
-----------------------------
DeepForge components, such as the server or the workers, can be started with the :code:`deepforge start` command. By default, this command will start all the necessary components to run including the server, a mongo database (if applicable) and a worker.
The server can be started by itself using
.. code-block:: bash
deepforge start --server
The worker can be started by itself using
.. code-block:: bash
deepforge start --worker http://154.95.87.1:7543
where `http://154.95.87.1:7543` is the url of the deepforge server.
Installing and Upgrading Torch7
-------------------------------
Torch7 is lazily installed when starting a worker (if torch isn't already installed) with the rnn package. This installation can be manually updated as described in the update and installation section.
Update/Uninstall DeepForge
--------------------------
DeepForge can be updated or uninstalled using
.. code-block:: bash
deepforge update
The torch installation can be updated using
.. code-block:: bash
deepforge update --torch
DeepForge can be uninstalled using :code:`deepforge uninstall`
Managing Extensions
-------------------
DeepForge extensions can be installed and removed using the :code:`deepforge extensions` subcommand. Extensions can be added, removed and listed as shown below
.. code-block:: bash
deepforge extensions add https://github.com/example/some-extension
deepforge extensions remove some-extension
deepforge extensions list
+77
Ver Arquivo
@@ -0,0 +1,77 @@
Configuration
=============
Configuration of deepforge is done through the `deepforge config` command from the command line interface. To see all config options, simply run `deepforge config` with no additional arguments. This will print a JSON representation of the configuration settings similar to:
.. code-block:: bash
Current config:
{
"torch": {
"dir": "/home/irishninja/.deepforge/torch"
},
"blob": {
"dir": "/home/irishninja/.deepforge/blob"
},
"worker": {
"cache": {
"useBlob": true,
"dir": "~/.deepforge/worker/cache"
},
"dir": "~/.deepforge/worker"
},
"mongo": {
"dir": "~/.deepforge/data"
}
}
Setting an attribute, say `worker.cache.dir`, is done as follows
.. code-block:: bash
deepforge config worker.cache.dir /tmp
Environment Variables
---------------------
Most settings have a corresponding environment variable which can be used to override the value set in the cli's configuration. This allows the values to be temporarily set for a single run. For example, starting a worker with a different cache than set in `worker.cache.dir` can be done with:
.. code-block:: bash
DEEPFORGE_WORKER_CACHE=/tmp deepforge start -w
The complete list of the environment variable overrides for the configuration options can be found `here <https://github.com/deepforge-dev/deepforge/blob/master/bin/envConfig.json>`_.
Settings
--------
torch.dir
~~~~~~~~~
The path to the local installation of torch to be used by the deepforge worker. This is used when installing, upgrading and removing the local torch installation
blob.dir
~~~~~~~~
The path to the blob (large file storage containing models, datasets, etc) to be used by the deepforge server.
This can be overridden with the `DEEPFORGE_BLOB_DIR` environment variable.
worker.dir
~~~~~~~~~~
The path to the directory used for worker executions. The workers will run the executions from this directory.
This can be overridden with the `DEEPFORGE_WORKER_DIR` environment variable.
mongo.dir
~~~~~~~~~
The path to use for the `--dbpath` option of mongo if starting mongo using the command line interface. That is, if the MONGO_URI is set to a local uri and the cli is starting the deepforge server, the cli will check to verify that an instance of mongo is running locally. If not, it will start it on the given port and use this setting for the `--dbpath` setting of mongod.
worker.cache.dir
~~~~~~~~~~~~~~~~
The path to the worker cache directory.
This can be overridden with the `DEEPFORGE_WORKER_CACHE` environment variable.
worker.cache.useBlob
~~~~~~~~~~~~~~~~~~~~
When running the worker on the same machine as the server, this allows the worker to use the blob as a cache and simply create symbolic links to the data (eg, training data, models) to prevent having to even perform a copy of the data on the given machine.
This can be overridden with the `DEEPFORGE_WORKER_USE_BLOB` environment variable.
+53
Ver Arquivo
@@ -0,0 +1,53 @@
Operation Feedback
==================
DeepForge provides the `deepforge` global object in operation implementations for providing feedback during the execution. The various types of metadata are provided and discussed below.
Graphs
------
Real-time graphs can be created using the graph constructor:
.. code-block:: lua
local graph = deepforge.Graph('My Graph') -- created a new graph called "My Graph"
After creating a graph, lines can be added similarly.
.. code-block:: lua
local line1 = graph:line('first line') -- created a new line called "first line"
local line2 = graph:line('second line') -- created a second line called "second line"
Finally, points can be added to the lines by calling the `:add` method on the line and passing the x and y values for the given point.
.. code-block:: lua
line1:add(1, 3) -- adding point (1, 3) to line1
line2:add(1, 4) -- adding point (1, 4) to line2
line1:add(2, 5) -- adding point (2, 5) to line1
line2:add(2, 6) -- adding point (2, 6) to line2
Graphs can then label their axis as follows:
.. code-block:: lua
graph:xlabel('x axis') -- label the x axis "x axis"
graph:ylabel('y axis') -- label the y axis "y axis"
Images
------
Images can be created using:
.. code-block:: lua
local image = deepforge.Image('My Example Image', imageTensor)
The first argument is the title of the image and the second argument is the tensor for the image (optional). Both the title and the tensor can be updated during execution as follows.
.. code-block:: lua
image:title('My New Title') -- updating the image title
image:update(newTensor) -- updating the displayed image
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 681 KiB

+4626
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+16 -6
Ver Arquivo
@@ -1,34 +1,44 @@
{
"name": "deepforge",
"repository": {
"type": "git",
"url": "https://github.com/deepforge-dev/deepforge.git"
},
"bin": {
"deepforge": "./bin/deepforge"
},
"scripts": {
"start": "node app.js",
"start": "./bin/deepforge start",
"postinstall": "node utils/reinstall-extensions.js",
"start-dev": "NODE_ENV=dev node app.js",
"local": "node ./bin/start-local.js",
"worker": "node ./bin/start-worker.js",
"test": "mkdir ./test-tmp; node ./node_modules/mocha/bin/mocha --recursive test",
"watch-test": "./node_modules/nodemon/bin/nodemon.js --exec 'node ./node_modules/mocha/bin/mocha --recursive test'",
"test": "mkdir ./test-tmp; mocha --recursive test",
"watch-test": "nodemon --exec 'mocha --recursive test'",
"build-nn": "node ./utils/nn-parser.js"
},
"version": "0.18.0",
"version": "1.4.1",
"dependencies": {
"commander": "^2.9.0",
"dotenv": "^2.0.0",
"exists-file": "^2.1.0",
"express": "^4.14.0",
"graceful-fs": "^4.1.10",
"lodash.difference": "^4.1.2",
"lodash.merge": "^4.5.1",
"lodash.template": "^4.4.0",
"mongodb": "^2.2.10",
"nodemon": "^1.9.2",
"npm": "^4.0.5",
"q": "1.4.1",
"rimraf": "^2.4.0",
"webgme": "^2.0.0",
"webgme-autoviz": "dfst/webgme-autoviz",
"tcp-port-used": "^0.1.2",
"webgme": "^2.7.1",
"webgme-autoviz": "^2.2.0",
"webgme-breadcrumbheader": "^2.1.1",
"webgme-chflayout": "^2.0.0",
"webgme-easydag": "dfst/webgme-easydag",
"webgme-executor-worker": "^1.0.1",
"webgme-fab": "dfst/webgme-fab",
"webgme-simple-nodes": "^2.1.0"
},
+51 -24
Ver Arquivo
@@ -1,31 +1,58 @@
/* globals define */
define({
LINE_OFFSET: 'lineOffset',
(function(root, factory){
if(typeof define === 'function' && define.amd) {
define([], function(){
return factory();
});
} else if(typeof module === 'object' && module.exports) {
module.exports = factory();
} else {
root.CONSTANTS = factory();
}
}(this, function() {
return {
CONTAINED_LAYER_SET: 'addLayers',
CONTAINED_LAYER_INDEX: 'index',
// DeepForge metadata creation in dist execution
START_CMD: 'deepforge-cmd',
LINE_OFFSET: 'lineOffset',
DISPLAY_COLOR: 'displayColor',
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
},
// DeepForge metadata creation in dist execution
START_CMD: 'deepforge-cmd',
GRAPH_CREATE: 'GRAPH',
GRAPH_PLOT: 'PLOT',
GRAPH_CREATE_LINE: 'LINE',
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
},
// Code Generation Constants
CTOR_ARGS_ATTR: 'ctor_arg_order',
GRAPH_CREATE: 'GRAPH',
GRAPH_PLOT: 'PLOT',
GRAPH_CREATE_LINE: 'LINE',
GRAPH_LABEL_AXIS: {
X: 'X',
Y: 'Y'
},
// Operation types
OP: {
INPUT: 'Input',
OUTPUT: 'Output'
},
// Code Generation Constants
CTOR_ARGS_ATTR: 'ctor_arg_order',
// Job stdout update
STDOUT_UPDATE: 'stdout_update'
});
// Operation types
OP: {
INPUT: 'Input',
OUTPUT: 'Output'
},
// Heartbeat constants (ExecPulse router)
PULSE: {
DEAD: 0,
ALIVE: 1,
DOESNT_EXIST: 2
},
// Job stdout update
STDOUT_UPDATE: 'stdout_update'
};
}));
+4 -2
Ver Arquivo
@@ -265,6 +265,7 @@
var findTorchClass = function(ast){
var torchClassArgs, // args for `torch.class(...)`
name = '',
alias,
baseType,
params,
setters = {},
@@ -283,6 +284,7 @@
name = torchClassArgs[0];
if(name !== ''){
name = name.replace('nn.', '');
alias = func.names[0] || name;
if (torchClassArgs.length > 1) {
baseType = torchClassArgs[1].replace('nn.', '');
}
@@ -302,7 +304,7 @@
attrName;
// Record the setter functions
if (isSetterMethod(curr, parent, name)) {
if (isSetterMethod(curr, parent, alias)) {
firstLine = curr.block.stats[0];
// just use the attribute attrName for now...
attrName = getSettingAttrName(firstLine);
@@ -316,7 +318,7 @@
} else {
setters[attrName] = schema;
}
} else if (isInitFn(curr, name)) { // Record the defaults
} else if (isInitFn(curr, alias)) { // Record the defaults
paramDefs = getAttrsAndVals(curr);
attrDefs = getClassAttrDefs(curr);
types = inferParamTypes(curr, paramDefs);
+1 -1
Ver Arquivo
@@ -51,7 +51,7 @@ define([
var deferred = Q.defer(),
req = superagent[method](this.getUrl(jobId));
this.logger.info(`sending ${method} request to ${this.getUrl(jobId)}`);
this.logger.debug(`sending ${method} request to ${this.getUrl(jobId)}`);
if (this.token) {
req.set('Authorization', 'Bearer ' + this.token);
}
+44
Ver Arquivo
@@ -0,0 +1,44 @@
/* globals define */
define([
'./APIClient'
], function(
APIClient
) {
'use strict';
var ExecPulseClient = function(params) {
this.relativeUrl = '/execution/pulse/';
this.logger = params.logger.fork('ExecPulseClient');
APIClient.call(this, params);
};
ExecPulseClient.prototype = Object.create(APIClient.prototype);
ExecPulseClient.prototype.getUrl = function(hash) {
return this.url + hash;
};
// - update the heartbeat
// - check the heartbeat
// - delete the heartbeat
ExecPulseClient.prototype.update = function(hash) {
return this._request('post', hash)
.catch(err => {
throw err.text || err;
});
};
ExecPulseClient.prototype.check = function(hash) {
return this._request('get', hash)
.then(res => JSON.parse(res.text))
.catch(err => {
throw err.text || err;
});
};
ExecPulseClient.prototype.clear = function(hash) {
return this._request('delete', hash);
};
return ExecPulseClient;
});
+31 -3
Ver Arquivo
@@ -11,6 +11,9 @@ define([
'use strict';
// Wrap the ability to read, update, and delete logs using the JobLogsAPI
var METADATA_FIELDS = [
'lineCount'
];
var JobLogsClient = function(params) {
params = params || {};
@@ -63,18 +66,37 @@ define([
};
JobLogsClient.prototype.getUrl = function(jobId) {
var url = this.url;
if (typeof jobId !== 'string') {
url = this.url + jobId.route;
jobId = jobId.jobId;
}
return [
this.url,
url,
encodeURIComponent(this.project),
encodeURIComponent(this.branch),
encodeURIComponent(jobId)
].join('/');
};
JobLogsClient.prototype.appendTo = function(jobId, text) {
var hasRequiredFields = function(md) {
return METADATA_FIELDS.reduce((passing, nextField) => {
return passing && md.hasOwnProperty(nextField);
}, true);
};
JobLogsClient.prototype.appendTo = function(jobId, text, metadata) {
this._modifiedJobs.push(jobId);
this.logger.info(`Appending logs to ${jobId}`);
return this._request('patch', jobId, {patch: text});
if (metadata && !hasRequiredFields(metadata)) {
throw Error(`Required metadata fields: ${METADATA_FIELDS.join(', ')}`);
}
metadata = metadata || {};
metadata.patch = text;
return this._request('patch', jobId, metadata);
};
JobLogsClient.prototype.getLog = function(jobId) {
@@ -88,5 +110,11 @@ define([
return this._request('delete', jobId);
};
JobLogsClient.prototype.getMetadata = function(jobId) {
this.logger.info(`Getting line count for ${jobId}`);
return this._request('get', {jobId: jobId, route: '/metadata'})
.then(res => JSON.parse(res.text));
};
return JobLogsClient;
});
+15 -10
Ver Arquivo
@@ -57,7 +57,7 @@ define([
};
var createNamedNode = function(baseId, parentId, isMeta) {
var newId = client.createChild({parentId, baseId}),
var newId = client.createNode({parentId, baseId}),
baseNode = client.getNode(baseId),
basename = 'New' + baseNode.getAttribute('name'),
newName = getUniqueName(parentId, basename);
@@ -72,7 +72,7 @@ define([
client.setRegistry(newId, 'isAbstract', false);
}
client.setAttributes(newId, 'name', newName);
client.setAttribute(newId, 'name', newName);
return newId;
};
@@ -83,7 +83,9 @@ define([
exists = {},
i = 2;
children.forEach(child => exists[child.getAttribute('name')] = true);
children
.filter(child => child !== null)
.forEach(child => exists[child.getAttribute('name')] = true);
while (exists[name]) {
name = basename + '_' + i;
@@ -191,7 +193,7 @@ define([
.getId();
// Look up the parent container
DeepForge.places[placeName]().then(parentId => {
return DeepForge.places[placeName]().then(parentId => {
client.startTransaction(msg);
newId = createNamedNode(baseId, parentId, !!metasheetName);
@@ -265,14 +267,12 @@ define([
}
dataBaseId = dataBase.getId();
dataTypes = metanodes.filter(n => client.isTypeOf(n.getId(), dataBaseId))
dataTypes = metanodes.filter(n => n.isTypeOf(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],
// Add the target type to the pluginMetadata...
var metadata = WebGMEGlobal.allPluginsMetadata[UPLOAD_PLUGIN],
config = metadata.configStructure
.find(opt => opt.name === DATA_TYPE_CONFIG.name);
@@ -297,7 +297,8 @@ define([
};
DeepForge.last = {};
DeepForge.create = {};
DeepForge.create = {};
DeepForge.register = {};
instances.forEach(type => {
DeepForge.create[type] = function() {
return createNew.call(null, type);
@@ -308,6 +309,10 @@ define([
DeepForge.create[type] = function() {
return createNew.call(null, type, type);
};
DeepForge.register[type] = function(id) {
// Add the given element to the metasheet!
return addToMetaSheet(id, type);
};
});
DeepForge.create.Layer = createCustomLayer;
+1 -1
Ver Arquivo
@@ -2565,7 +2565,7 @@ function LuaContext(){
case 'number':
return c;
case 'string':
return parseInt(s) || dummy0;
return parseInt(c) || dummy0;
default:
if (c == dummy0){
return c;
+7 -4
Ver Arquivo
@@ -148,11 +148,14 @@ define([
// get the input node
if (dataNodes.length !== 0) {
var newNodes = this.core.copyNodes(dataNodes, parentNode),
newName = this.core.getOwnAttribute(node, 'saveName');
newName = this.core.getOwnAttribute(node, 'saveName'),
createdAt = Date.now();
if (newName) {
newNodes.forEach(node =>
this.setAttribute(node, 'name', newName)
);
newNodes.forEach(node => {
this.setAttribute(node, 'name', newName);
this.setAttribute(node, 'createdAt', createdAt);
});
}
var hashes = dataNodes.map(n => this.getAttribute(n, 'data'));
this.logger.info(`saving hashes: ${hashes.map(h => `"${h}"`)}`);
+35
Ver Arquivo
@@ -0,0 +1,35 @@
/*globals define */
// This is a mixin containing helpers for working with operation nodes
define([],function() {
var OperationOps = function() {
};
OperationOps.prototype.getOutputs = function (node) {
return this.getOperationData(node, this.META.Outputs);
};
OperationOps.prototype.getInputs = function (node) {
return this.getOperationData(node, this.META.Inputs);
};
OperationOps.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.getAttribute(node, 'name'),
this.getAttribute(bases[i], 'name'),
node
]);
});
};
return OperationOps;
});
+51 -13
Ver Arquivo
@@ -6,27 +6,64 @@ define([
PluginUtils,
Q
) {
var CodeGen = {
Operation: {
pluginId: 'GenerateJob',
namespace: 'pipeline'
}
};
var PtrCodeGen = function() {
};
PtrCodeGen.prototype.getCodeGenPluginIdFor = function(node) {
var base = this.core.getBase(node),
name = this.core.getAttribute(node, 'name'),
namespace = this.core.getNamespace(node),
pluginId;
//this.logger.debug(`loaded pointer target of ${ptrId}: ${ptrNode}`);
pluginId = (this.core.getOwnRegistry(node, 'validPlugins') || '').split(' ').shift();
//this.logger.info(`generating code for ${this.core.getAttribute(ptrNode, 'name')} using ${pluginId}`);
if (this.core.isMetaNode(node) && CodeGen[name]) {
pluginId = CodeGen[name].pluginId || CodeGen[name];
namespace = CodeGen[name].namespace;
}
if (pluginId) {
return {
namespace: namespace,
pluginId: pluginId
};
} else if (base) {
return this.getCodeGenPluginIdFor(base);
} else {
return null;
}
};
PtrCodeGen.prototype.getPtrCodeHash = function(ptrId) {
return this.core.loadByPath(this.rootNode, ptrId)
.then(ptrNode => {
// Look up the plugin to use
var metanode = this.core.getMetaType(ptrNode),
pluginId;
var genInfo = this.getCodeGenPluginIdFor(ptrNode);
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}`);
if (genInfo.pluginId) {
var context = {
namespace: genInfo.namespace,
activeNode: this.core.getPath(ptrNode)
};
var context = {
namespace: this.core.getNamespace(metanode),
activeNode: this.core.getPath(ptrNode)
};
// Load and run the plugin
return this.executePlugin(pluginId, context);
// Load and run the plugin
return this.executePlugin(genInfo.pluginId, context);
} else {
var metanode = this.core.getMetaType(ptrNode),
type = this.core.getAttribute(metanode, 'name');
this.logger.warn(`Could not find plugin for ${type}. Will try to proceed anyway`);
return null;
}
})
.then(hashes => hashes[0]); // Grab the first asset for now
};
@@ -56,12 +93,13 @@ define([
return PluginUtils.loadNodesAtCommitHash(
this.project,
this.core,
this.commitHash,
this.currentHash,
this.logger,
opts
).then(config => {
plugin.initialize(logger, this.blobClient, this.gmeConfig);
config.core = this.core;
config.project = this.project;
plugin.configure(config);
return plugin;
});
+73 -1
Ver Arquivo
@@ -1,8 +1,10 @@
/*globals define, WebGMEGlobal*/
define([
'deepforge/globals',
'widgets/EasyDAG/Buttons',
'widgets/EasyDAG/Icons'
], function(
DeepForge,
EasyDAGButtons,
Icons
) {
@@ -57,9 +59,79 @@ define([
return n && n.getBaseId();
};
var CloneAndEdit = function(params) {
GoToBase.call(this, params);
};
CloneAndEdit.prototype = Object.create(GoToBase.prototype);
CloneAndEdit.prototype.BTN_CLASS = 'clone-and-edit';
CloneAndEdit.prototype._render = function() {
var lineRadius = GoToBase.SIZE - GoToBase.BORDER,
btnColor = '#a5d6a7';
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
});
};
CloneAndEdit.prototype._onClick = function(item) {
var node = client.getNode(item.id),
baseId = node && node.getBaseId(),
base = baseId && client.getNode(baseId),
typeId = base && base.getBaseId(),
type = typeId && client.getNode(typeId),
ctrName,
typeName,
name,
newId;
// Clone the given node's base and change to it
if (type) {
typeName = type.getAttribute('name');
ctrName = `My${typeName}s`;
if (DeepForge.places[ctrName]) {
DeepForge.places[ctrName]().then(ctrId => {
type = base.getAttribute('name');
client.startTransaction(`Creating new ${typeName} from ${item.name}`);
newId = client.copyNode(baseId, ctrId);
name = node.getAttribute('name');
client.setAttribute(newId, 'name', 'Copy of ' + name);
DeepForge.register[typeName](newId);
client.completeTransaction();
WebGMEGlobal.State.registerActiveObject(newId);
});
}
} else {
this._logger.warn('Could not find the base node!');
}
};
var Insert = function(params) {
EasyDAGButtons.ButtonBase.call(this, params);
};
Insert.prototype = Object.create(EasyDAGButtons.Add.prototype);
Insert.prototype._onClick = function(item) {
this.onInsertButtonClicked(item);
};
return {
DeleteOne: EasyDAGButtons.DeleteOne,
GoToBase: GoToBase
GoToBase: GoToBase,
CloneAndEdit: CloneAndEdit,
Insert: Insert
};
});
+89 -4
Ver Arquivo
@@ -3,21 +3,31 @@
define([
'q',
'executor/ExecutorClient',
'deepforge/api/ExecPulseClient',
'deepforge/api/JobOriginClient',
'deepforge/Constants',
'panel/FloatingActionButton/styles/Materialize'
], function(
Q,
ExecutorClient,
ExecPulseClient,
JobOriginClient,
CONSTANTS,
Materialize
) {
var Execute = function(client, logger) {
this.client = this.client || client;
this.logger = this.logger || logger;
this.pulseClient = new ExecPulseClient({
logger: this.logger
});
this._executor = new ExecutorClient({
logger: this.logger.fork('ExecutorClient'),
serverPort: WebGMEGlobal.gmeConfig.server.port,
httpsecure: window.location.protocol === 'https:'
});
this.originManager = new JobOriginClient({logger: this.logger});
};
Execute.prototype.executeJob = function(node) {
@@ -108,9 +118,9 @@ define([
this.client.startTransaction(`Stopping "${name}" job`);
}
this.client.delAttributes(jobId, 'jobId');
this.client.delAttributes(jobId, 'secret');
this.client.setAttributes(jobId, 'status', 'canceled');
this.client.delAttribute(jobId, 'jobId');
this.client.delAttribute(jobId, 'secret');
this.client.setAttribute(jobId, 'status', 'canceled');
if (!silent) {
this.client.completeTransaction();
@@ -178,7 +188,7 @@ define([
jobIds = this._silentStopExecution(execNode);
this.client.setAttributes(execNode.getId(), 'status', 'canceled');
this.client.setAttribute(execNode.getId(), 'status', 'canceled');
jobIds.forEach(jobId => this._setJobStopped(jobId, true));
if (!inTransaction) {
@@ -196,5 +206,80 @@ define([
return runningJobIds;
};
// Resuming Executions
Execute.prototype.checkJobExecution= function (job) {
var pipelineId = job.getParentId(),
pipeline = this.client.getNode(pipelineId);
// First check the parent execution. If it doesn't exist, then check the job
return this.checkPipelineExecution(pipeline)
.then(tryToStartJob => {
if (tryToStartJob) {
return this._checkJobExecution(job);
}
});
};
Execute.prototype._checkJobExecution = function (job) {
var jobId = job.getAttribute('jobId'),
status = job.getAttribute('status');
if (status === 'running' && jobId) {
return this.pulseClient.check(jobId)
.then(status => {
if (status !== CONSTANTS.PULSE.DOESNT_EXIST) {
return this._onOriginBranch(jobId).then(onBranch => {
if (onBranch) {
this.runExecutionPlugin('ExecuteJob', {
node: job
});
}
});
} else {
this.logger.warn(`Could not restart job: ${job.getId()}`);
}
});
}
return Q();
};
Execute.prototype._onOriginBranch = function (hash) {
return this.originManager.getOrigin(hash)
.then(origin => {
var currentBranch = this.client.getActiveBranchName();
if (origin && origin.branch) {
return origin.branch === currentBranch;
}
return false;
});
};
Execute.prototype.checkPipelineExecution = function (pipeline) {
var runId = pipeline.getAttribute('runId'),
status = pipeline.getAttribute('status'),
tryToStartJob = true;
if (status === 'running' && runId) {
return this.pulseClient.check(runId)
.then(status => {
if (status === CONSTANTS.PULSE.DEAD) {
// Check the origin branch
return this._onOriginBranch(runId).then(onBranch => {
if (onBranch) {
this.runExecutionPlugin('ExecutePipeline', {
node: pipeline
});
}
});
}
// only try to start if the pulse info doesn't exist
tryToStartJob = status === CONSTANTS.PULSE.DOESNT_EXIST;
return tryToStartJob;
});
} else {
return Q().then(() => tryToStartJob);
}
};
return Execute;
});
+4
Ver Arquivo
@@ -4,9 +4,11 @@
// adding a "plus" button for creating new objects in line
define([
'deepforge/Constants',
'q',
'css!./NodePrompter.css'
], function(
Constants,
Q
) {
@@ -271,12 +273,14 @@ define([
};
var Container = function(svg, node) { // used for positioning
var colorAttr = node.attributes[Constants.DISPLAY_COLOR];
this.$el = svg.append('g');
this.x = 0;
this.y = 0;
this.node = node;
this.decorator = new node.Decorator({
node: node,
color: colorAttr && colorAttr.value,
parentEl: this.$el
});
};
+5 -1
Ver Arquivo
@@ -65,7 +65,7 @@ define([
PipelineControl.prototype.createNode = function(baseId) {
var parentId = this._currentNodeId,
newNodeId = this._client.createChild({parentId, baseId});
newNodeId = this._client.createNode({parentId, baseId});
return newNodeId;
};
@@ -103,6 +103,10 @@ define([
delete desc.attributes.code;
}
// Handle the display color
desc.displayColor = desc.attributes[CONSTANTS.DISPLAY_COLOR].value;
delete desc.attributes[CONSTANTS.DISPLAY_COLOR];
} else if (desc.isConnection) {
// Set src, dst to siblings and add srcPort, dstPort
desc.srcPort = desc.src;
+1 -1
Ver Arquivo
@@ -49,7 +49,7 @@ define([
if (!/^\s*$/.test(newValue)) {
this._client.startTransaction(msg);
this._client.setAttributes(nodeId, 'name', newValue);
this._client.setAttribute(nodeId, 'name', newValue);
this._client.completeTransaction();
}
}
+41
Ver Arquivo
@@ -0,0 +1,41 @@
/* globals define */
define([
'panels/EasyDAG/EasyDAGControl'
], function(
EasyDAGControl
) {
var ThumbnailControl = function() {
EasyDAGControl.apply(this, arguments);
};
ThumbnailControl.prototype = Object.create(EasyDAGControl.prototype);
ThumbnailControl.prototype._initWidgetEventHandlers = function () {
EasyDAGControl.prototype._initWidgetEventHandlers.call(this);
this._widget.updateThumbnail = this.updateThumbnail.bind(this);
};
ThumbnailControl.prototype.updateThumbnail = function (svg) {
var node = this._client.getNode(this._currentNodeId),
name,
attrs,
currentThumbnail,
attrName = 'thumbnail',
msg;
if (node) { // may have been deleted
name = node.getAttribute('name');
attrs = node.getValidAttributeNames();
currentThumbnail = node.getAttribute(attrName);
msg = `Updating pipeline thumbnail for "${name}"`;
if (attrs.indexOf(attrName) > -1 && currentThumbnail !== svg) {
this._client.startTransaction(msg);
this._client.setAttribute(this._currentNodeId, attrName, svg);
this._client.completeTransaction();
}
}
};
return ThumbnailControl;
});
+84
Ver Arquivo
@@ -0,0 +1,84 @@
/* globals define, $, _ */
define([
'widgets/EasyDAG/EasyDAGWidget'
], function(
EasyDAGWidget
) {
var ThumbnailWidget = function() {
EasyDAGWidget.apply(this, arguments);
};
ThumbnailWidget.prototype = Object.create(EasyDAGWidget.prototype);
ThumbnailWidget.prototype.addNode = function() {
var result = EasyDAGWidget.prototype.addNode.apply(this, arguments);
this.refreshThumbnail();
return result;
};
ThumbnailWidget.prototype.removeNode = function() {
var result = EasyDAGWidget.prototype.removeNode.apply(this, arguments);
this.refreshThumbnail();
return result;
};
ThumbnailWidget.prototype._removeConnection = function() {
var result = EasyDAGWidget.prototype._removeConnection.apply(this, arguments);
this.refreshThumbnail();
return result;
};
ThumbnailWidget.prototype.addConnection = function() {
var result = EasyDAGWidget.prototype.addConnection.apply(this, arguments);
this.refreshThumbnail();
return result;
};
////////////////////////// Thumbnail updates //////////////////////////
ThumbnailWidget.prototype.getSvgDistanceDim = function(dim) {
var maxValue = this._getMaxAlongAxis(dim),
nodes,
minValue;
nodes = this.graph.nodes().map(id => this.graph.node(id));
minValue = nodes.length ? Math.min.apply(null, nodes.map(node => node[dim] || 0)) : 0;
return maxValue-minValue;
};
ThumbnailWidget.prototype.getSvgWidth = function() {
return this.getSvgDistanceDim('x');
};
ThumbnailWidget.prototype.getSvgHeight = function() {
return this.height - 25;
};
ThumbnailWidget.prototype.getViewBox = function() {
var maxX = this.getSvgWidth('x'),
maxY = this.getSvgHeight('y');
return `0 0 ${maxX} ${maxY}`;
};
ThumbnailWidget.prototype.refreshThumbnail = _.debounce(function() {
// Get the svg...
var svg = document.createElement('svg'),
group = this.$svg.node(),
child;
svg.setAttribute('viewBox', this.getViewBox());
for (var i = 0; i < group.children.length; i++) {
child = $(group.children[i]);
svg.appendChild(child.clone()[0]);
}
this.updateThumbnail(svg.outerHTML);
}, 1000);
return ThumbnailWidget;
});
@@ -40,10 +40,40 @@ define([
ArtifactOpDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
ArtifactOpDecorator.prototype.getTargetFilterFnFor = function() {
var currentNode,
pId,
peerIds,
currentDataId,
targetTypeIds = [];
// Get all connections from this node's data
if (this._node.outputs[0]) {
currentNode = this.client.getNode(this._node.id);
pId = currentNode.getParentId();
peerIds = this.client.getNode(pId).getChildrenIds();
currentDataId = this._node.outputs[0].id;
targetTypeIds = peerIds.map(id => this.client.getNode(id))
.filter(node => {
var ptr = node.getPointer('src');
return ptr.to === currentDataId;
})
// Get the target data types
.map(node => this.client.getNode(node.getPointer('dst').to).getMetaTypeId());
}
return id => {
var node = this.client.getNode(id),
isMetaTgt = node.getId() === node.getMetaTypeId();
return isMetaTgt === this.castOpts.metaTgt;
isMetaTgt = node.getId() === node.getMetaTypeId(),
isValidType = true;
// make sure it is a type of the target types
isValidType = targetTypeIds.reduce((passing, typeId) => {
return passing && node.isTypeOf(typeId);
}, true);
return isValidType && isMetaTgt === this.castOpts.metaTgt;
};
};
@@ -57,13 +87,13 @@ define([
this.client.startTransaction(`Removing output of ${this.name}`);
this.client.delPointer(this._node.id, name);
if (outputId) {
this.client.delAttributes(outputId, 'data');
this.client.delAttribute(outputId, 'data');
}
this.client.completeTransaction();
} else if (name === this.castOpts.ptr) { // set the casted value
this.client.startTransaction(`Setting output of ${this.name} to ${to}`);
this.castOutputType(to);
this.client.makePointer(this._node.id, name, to);
this.client.setPointer(this._node.id, name, to);
this.client.completeTransaction();
} else {
DecoratorBase.prototype.savePointer.call(this, name, to);
@@ -0,0 +1,39 @@
/*globals define, _*/
/*jshint browser: true, camelcase: false*/
define([
'js/Decorators/DecoratorBase',
'./EasyDAG/ContainerLayerDecorator.EasyDAGWidget'
], function (
DecoratorBase,
ContainerLayerDecoratorEasyDAGWidget
) {
'use strict';
var ContainerLayerDecorator,
__parent__ = DecoratorBase,
__parent_proto__ = DecoratorBase.prototype,
DECORATOR_ID = 'ContainerLayerDecorator';
ContainerLayerDecorator = function (params) {
var opts = _.extend({loggerName: this.DECORATORID}, params);
__parent__.apply(this, [opts]);
this.logger.debug('ContainerLayerDecorator ctor');
};
_.extend(ContainerLayerDecorator.prototype, __parent_proto__);
ContainerLayerDecorator.prototype.DECORATORID = DECORATOR_ID;
/*********************** OVERRIDE DecoratorBase MEMBERS **************************/
ContainerLayerDecorator.prototype.initializeSupportedWidgetMap = function () {
this.supportedWidgetMap = {
EasyDAG: ContainerLayerDecoratorEasyDAGWidget
};
};
return ContainerLayerDecorator;
});
@@ -0,0 +1,25 @@
.condense .nested-layers {
display: none;
}
.nested-layers .hover-box {
stroke: black;
stroke-dasharray: 4, 4;
stroke-opacity: 0;
}
.nested-layers .unhovered .button {
opacity: 0;
}
.nested-layers .hovered .button {
opacity: 1;
}
.nested-layers .unhovered .hover-box {
stroke-opacity: 0;
}
.nested-layers .hovered .hover-box {
stroke-opacity: 0;
}
@@ -0,0 +1,428 @@
/*globals define, _, */
/*jshint browser: true, camelcase: false*/
define([
'decorators/LayerDecorator/EasyDAG/LayerDecorator.EasyDAGWidget',
'js/Constants',
'deepforge/Constants',
'./NestedLayer',
'widgets/EasyDAG/Buttons',
'css!./ContainerLayerDecorator.EasyDAGWidget.css'
], function (
LayerDecorator,
GME_CONSTANTS,
CONSTANTS,
NestedLayer,
Buttons
) {
'use strict';
var ContainerLayerDecorator,
ZOOM = 0.8,
DECORATOR_ID = 'ContainerLayerDecorator';
// Container layer nodes need to be able to nest the containedLayers
// in order inside of themselves when expanded
ContainerLayerDecorator = function (options) {
this.nestedLayers = {};
LayerDecorator.call(this, options);
this.$nested = this.$el.append('g')
.attr('class', 'nested-layers');
// If clicked, deselect the given nested layer
this.$el.on('click', () => {
if (this.expanded) {
Object.keys(this.nestedLayers).forEach(id => {
this.nestedLayers[id].widget.onBackgroundClick();
});
}
});
this.onNestedRefresh = _.debounce(this.updateExpand.bind(this), 50);
// Add event handlers
NestedLayer.prototype.addLayerBefore = function(layerId) {
var decorator = this._parent,
index = decorator._node.containedLayers.indexOf(this.id);
return decorator.addLayerAt(layerId, index - 1);
};
NestedLayer.prototype.addLayerAfter = function(layerId) {
var decorator = this._parent,
index = decorator._node.containedLayers.indexOf(this.id);
return decorator.addLayerAt(layerId, index + 1);
};
NestedLayer.prototype.isLast = function() {
var index = this._parent._node.containedLayers.length - 1;
return this._parent._node.containedLayers[index] === this.id;
};
NestedLayer.prototype.isFirst = function() {
return this._parent._node.containedLayers[0] === this.id;
};
NestedLayer.prototype.moveLayerForward = function() {
return this.moveLayer(true);
};
NestedLayer.prototype.moveLayerBackward = function() {
return this.moveLayer();
};
NestedLayer.prototype.moveLayer = function(forward) {
var decorator = this._parent,
index = decorator._node.containedLayers.indexOf(this.id),
client = decorator.client,
msg;
decorator._node.containedLayers.splice(index, 1);
if (forward) {
index = Math.max(0, index - 1);
} else {
index++;
}
decorator._node.containedLayers.splice(index, 0, this.id);
msg = `Swapping nested layers at ${index} and ${forward ? index-1 : index+1}`;
client.startTransaction(msg);
decorator._updateNestedIndices();
client.completeTransaction();
};
NestedLayer.prototype.onLastNodeRemoved = function() {
var decorator = this._parent,
index = decorator._node.containedLayers.indexOf(this.id),
msg = `Removing nested layer of ${decorator._node.name} at position ${index}`;
decorator.client.startTransaction(msg);
decorator.client.deleteNode(this.id);
decorator.client.completeTransaction();
};
this.updateNestedTerritory();
};
_.extend(ContainerLayerDecorator.prototype, LayerDecorator.prototype);
ContainerLayerDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
ContainerLayerDecorator.prototype._updateNestedIndices = function() {
this._node.containedLayers.forEach((layerId, index) => {
// Set the layer's member registry to it's index
this.client.setMemberRegistry(
this._node.id,
layerId,
CONSTANTS.CONTAINED_LAYER_SET,
CONSTANTS.CONTAINED_LAYER_INDEX,
index
);
});
};
ContainerLayerDecorator.prototype.addLayerAt = function(baseId, index) {
var client = this.client,
parentId = this._node.id,
archNode,
newId,
msg;
// Get the index of the given layer
index = Math.max(index, 0);
archNode = client.getAllMetaNodes()
.find(node => node.getAttribute('name') === 'Architecture');
// Create a new Architecture node in the given node
msg = `Adding layer to ${this._node.name} at position ${index}`;
client.startTransaction(msg);
newId = client.createNode({
parentId: parentId,
baseId: archNode.getId()
});
// Create the selected layer
client.createNode({
parentId: newId,
baseId: baseId
});
client.addMember(parentId, newId, CONSTANTS.CONTAINED_LAYER_SET);
this._node.containedLayers.splice(index, 0, newId);
this._updateNestedIndices();
client.completeTransaction();
};
ContainerLayerDecorator.prototype.condense = function() {
// hide the nested layers
this.$el.attr('class', 'centering-offset condense');
this.removeCreateNestedBtn();
return LayerDecorator.prototype.condense.apply(this, arguments);
};
ContainerLayerDecorator.prototype.updateNestedTerritory = function() {
// Add the nested layers and update
if (!this._nestedTerritoryUI) {
this._nestedTerritoryUI = this.client.addUI(this, this._containedEvents.bind(this));
}
this._territory = {};
this._node.containedLayers.forEach(id => this._territory[id] = {children: 0});
this.client.updateTerritory(this._nestedTerritoryUI, this._territory);
};
ContainerLayerDecorator.prototype._containedEvents = function(events) {
for (var i = events.length; i--;) {
switch (events[i].etype) {
case GME_CONSTANTS.TERRITORY_EVENT_LOAD:
if (!this.nestedLayers[events[i].eid]) {
this.createNestedWidget(events[i].eid);
}
break;
case GME_CONSTANTS.TERRITORY_EVENT_UNLOAD:
this.removeNestedWidget(events[i].eid);
break;
}
}
if (events.length > 1) { // if more than just 'complete' event
this.updateExpand();
}
};
ContainerLayerDecorator.prototype.update = function(node) {
var attrsUpdated = false,
attrs = this._attributes;
this._node = node;
// Update the attributes
this.setAttributes();
attrsUpdated = !_.isEqual(attrs, this._attributes);
// Check for a new nested layer
var hasNewLayers = this._node.containedLayers
.filter(id => !this.nestedLayers[id])
.length > 0;
if (hasNewLayers) {
this.updateNestedTerritory();
} else {
// Update the order of the nested layers
if (this._selected) {
this.expand();
} else {
this.condense();
}
}
// Only reset fieldsWidth if the attribute has gotten larger
if (attrsUpdated) {
this.fieldsWidth = null;
}
};
ContainerLayerDecorator.prototype.updateExpand = function() {
if (this.expanded) {
this.expand();
}
};
ContainerLayerDecorator.prototype.createNestedWidget = function(id) {
if (!this.$nested) {
this.$nested = this.$el.append('g')
.attr('class', 'nested-layers');
}
this.nestedLayers[id] = new NestedLayer({
$container: this.$nested,
parent: this,
client: this.client,
logger: this.logger,
onRefresh: this.onNestedRefresh,
id: id
});
return this.nestedLayers[id];
};
ContainerLayerDecorator.prototype.removeNestedWidget = function(id) {
this.nestedLayers[id].destroy();
delete this.nestedLayers[id];
this.updateExpand();
};
ContainerLayerDecorator.prototype._renderInfo = function(top, width) {
var isAnUpdate = this.expanded,
y = top;
// Add the attribute fields
this.clearFields();
this.$attributes = this.$el.append('g')
.attr('fill', '#222222');
if (!isAnUpdate) {
this.$attributes.attr('opacity', 0);
}
y = this.createAttributeFields(y, width);
y = this.createPointerFields(y, width);
if (y !== top) {
y += this.ROW_HEIGHT/2;
}
return y;
};
ContainerLayerDecorator.prototype.expand = function() {
// This should be rendered with the attributes
var height,
width,
// Attributes
initialY = 25,
isAnUpdate = this.expanded,
NAME_MARGIN = 15,
nestedMargin = 15, // minimum
margin = 5,
y = margin + initialY,
x = margin,
i;
// Shift name down
this.$name.attr('y', 20);
// Add the nested children
var ids = this._node.containedLayers.filter(id => this.nestedLayers[id]),
totalNestedWidth = 0,
maxNestedHeight = 0,
fieldWidth,
widget;
if (ids.length === 0) {
maxNestedHeight = CreateNestedBtn.SIZE * 2;
} else {
for (i = 0; i < ids.length; i++) {
widget = this.nestedLayers[ids[i]].widget;
totalNestedWidth += widget.getSvgWidth() * ZOOM;
maxNestedHeight = Math.max(widget.getSvgHeight() * ZOOM, maxNestedHeight);
// Update the buttons (in case of reorder)
this.nestedLayers[ids[i]].refreshButtons();
}
}
fieldWidth = this.fieldsWidth + 3 * NAME_MARGIN;
width = Math.max(
this.nameWidth + 2 * NAME_MARGIN,
this.size.width,
fieldWidth,
totalNestedWidth + (ids.length + 1) * nestedMargin
);
// Render attributes
y = this._renderInfo(y, fieldWidth);
y += nestedMargin;
// Update width, height
height = y + maxNestedHeight + nestedMargin;
// Equally space the nested widgets
nestedMargin = (width - totalNestedWidth)/(ids.length + 1);
x = nestedMargin - width/2;
for (i = 0; i < ids.length; i++) {
this.nestedLayers[ids[i]].$el
.attr('transform', `translate(${x}, ${y}) scale(${ZOOM})`);
x += this.nestedLayers[ids[i]].widget.getSvgWidth() * ZOOM + nestedMargin;
}
this.removeCreateNestedBtn();
if (ids.length === 0) {
// Add the 'create nested layer' button if no nested layers
this.$createNestedBtn = new CreateNestedBtn({
context: this,
$pEl: this.$el,
y: y + CreateNestedBtn.SIZE
});
}
this.$body
.transition()
.attr('x', -width/2)
.attr('y', 0)
.attr('rx', 0)
.attr('ry', 0)
.attr('width', width)
.attr('height', height)
.each('end', () => {
if (!isAnUpdate) {
this.$attributes.attr('opacity', 1);
this.$el.attr('class', 'centering-offset expand');
}
});
if (this.height !== height || this.width !== width) {
this.height = height;
this.width = width;
this.expanded = true;
this.$el
.attr('transform', `translate(${this.width/2}, 0)`);
this.onResize();
}
};
ContainerLayerDecorator.prototype.removeCreateNestedBtn = function() {
if (this.$createNestedBtn) {
this.$createNestedBtn.remove();
this.$createNestedBtn = null;
}
};
ContainerLayerDecorator.prototype.destroyNested = function() {
Object.keys(this.nestedLayers).forEach(id => this.nestedLayers[id].destroy());
this.nestedLayers = {};
if (this.$nested) {
this.$nested.remove();
this.$nested = this.$el.append('g')
.attr('class', 'nested-layers');
}
};
ContainerLayerDecorator.prototype.destroy = function() {
LayerDecorator.prototype.destroy.call(this);
if (this._nestedTerritoryUI) {
this.client.removeUI(this._nestedTerritoryUI);
this._nestedTerritoryUI = null;
}
this.destroyNested();
};
var CreateNestedBtn = function(params) {
params.title = 'Add nested layer';
Buttons.Add.call(this, params);
};
CreateNestedBtn.SIZE = Buttons.Add.SIZE;
CreateNestedBtn.prototype = Object.create(Buttons.Add.prototype);
CreateNestedBtn.prototype._onClick = function() {
// Call addLayerAfter and prompt for a layer
this.promptLayer()
.then(layerId => this.addLayerAt(layerId, 0));
};
ContainerLayerDecorator.prototype.expandAll = function() {
this.expand();
// For each of the nested layers, expand all their nodes
Object.keys(this.nestedLayers)
.forEach(id => this.nestedLayers[id].widget.expandAllNodes());
};
ContainerLayerDecorator.prototype.condenseAll = function() {
this.condense();
// For each of the nested layers, expand all their nodes
Object.keys(this.nestedLayers)
.forEach(id => this.nestedLayers[id].widget.expandAllNodes(true));
};
return ContainerLayerDecorator;
});
@@ -0,0 +1,175 @@
/*globals define, _ */
define([
'panels/ArchEditor/ArchEditorControl',
'widgets/ArchEditor/ArchEditorWidget',
'widgets/EasyDAG/Buttons'
], function(
ArchEditor,
ArchEditorWidget,
Buttons
) {
var nop = () => {};
var NestedLayer = function(opts) {
this.$el = opts.$container.append('g')
.attr('class', 'nested-layer');
this.id = opts.id;
this._parent = opts.parent;
this.logger = opts.logger;
this.refreshButtons = _.debounce(this.updateButtons.bind(this), 100);
this.$outline = this.$el.append('rect') // for hover detection
.attr('fill-opacity', 0)
.attr('x', 0)
.attr('y', 0);
this.$content = this.$el.append('g');
this.initHover();
this.widget = new ArchEditorWidget({
logger: this.logger.fork('ArchWidget'),
autoCenter: false,
svg: this.$content
});
this.widget.setTitle =
this.widget.updateEmptyMsg = nop;
this.onRefresh = opts.onRefresh;
this.widget.refreshExtras = this.onWidgetRefresh.bind(this);
this.control = new ArchEditor({
logger: this.logger.fork('ArchControl'),
client: opts.client,
embedded: true,
widget: this.widget
});
this.control._onUnload = id => {
ArchEditor.prototype._onUnload.call(this.control, id);
// If it was the last node, remove it
var node = this.control._client.getNode(this.id);
if (node.getChildrenIds().length === 0) {
this.onLastNodeRemoved();
}
};
// hack :(
this.control.$btnModelHierarchyUp = {
show: nop,
hide: nop
};
this.widget.active = true;
this.control.selectedObjectChanged(this.id);
};
NestedLayer.prototype.initHover = function() {
this.$hover = this.$el.append('g')
.attr('class', 'hover-items');
this.$el.on('mouseenter', this.onHover.bind(this));
this.$el.on('mouseleave', this.onUnhover.bind(this));
// Buttons
this.$leftBtn = new Buttons.Add({
hide: true,
icon: this.isFirst() ? 'plus' : 'chevron-left',
$pEl: this.$hover
});
this.$rightBtn = new Buttons.Add({
hide: true,
icon: this.isLast() ? 'plus' : 'chevron-right',
$pEl: this.$hover
});
this.$deleteBtn = new Buttons.DeleteOne({
hide: true,
title: 'Delete',
$pEl: this.$hover
});
this.$leftBtn._onClick = this.clickLeft.bind(this);
this.$rightBtn._onClick = this.clickRight.bind(this);
this.$deleteBtn._onClick = () => this.onLastNodeRemoved();
this.$leftHint = this.$leftBtn.$el.append('title');
this.$rightHint = this.$rightBtn.$el.append('title');
this.refreshButtons();
};
NestedLayer.prototype.updateButtons = function() {
this.$leftBtn.icon = this.isFirst() ? 'plus' : 'chevron-left';
this.$rightBtn.icon = this.isLast() ? 'plus' : 'chevron-right';
this.$leftHint.text(this.isFirst() ?
'Add nested layer' :
'Move nested layer left'
);
this.$rightHint.text(this.isLast() ?
'Add nested layer' :
'Move nested layer right'
);
this.$leftBtn.render();
this.$rightBtn.render();
};
NestedLayer.prototype.clickLeft = function() {
if (this.isFirst()) {
this.promptLayer()
.then(layerId => this.addLayerBefore(layerId));
} else {
this.moveLayerForward();
}
this.onUnhover();
};
NestedLayer.prototype.promptLayer = function() {
var nodes = this.widget.getValidInitialNodes();
return this.widget.promptLayer(nodes)
.then(selected => selected.node.id);
};
NestedLayer.prototype.clickRight = function() {
if (this.isLast()) {
this.promptLayer()
.then(layerId => this.addLayerAfter(layerId));
} else {
this.moveLayerBackward();
}
this.onUnhover();
};
NestedLayer.prototype.onHover = function() {
this.refreshButtons();
this.$hover.attr('class', 'hover-items hovered');
};
NestedLayer.prototype.onUnhover = function() {
this.$hover.attr('class', 'hover-items unhovered');
};
NestedLayer.prototype.onWidgetRefresh = function() {
var width = this.widget.getSvgWidth(),
height = this.widget.getSvgHeight();
this.$outline
.attr('width', width)
.attr('height', height);
this.$leftBtn.$el.attr('transform', `translate(0, ${height/2})`);
this.$rightBtn.$el
.attr('transform', `translate(${width}, ${height/2})`);
this.onRefresh();
};
NestedLayer.prototype.destroy = function() {
this.control.destroy();
this.widget.destroy();
this.$el.remove();
};
return NestedLayer;
});
@@ -47,11 +47,11 @@ define([
// create the outputId node
outputId = this._createOutputNode(baseId);
} else {
this.client.makePointer(outputId, CONSTANTS.POINTER_BASE, baseId);
this.client.setPointer(outputId, CONSTANTS.POINTER_BASE, baseId);
}
// Copy the data content to the output node
hash = target.getAttribute('data');
this.client.setAttributes(outputId, 'data', hash);
this.client.setAttribute(outputId, 'data', hash);
};
DcOpDecorator.prototype._createOutputNode = function(baseId) {
@@ -69,7 +69,7 @@ define([
return metaType.getAttribute('name') === 'Outputs';
});
return this.client.createChild({
return this.client.createNode({
baseId: baseId,
parentId: outputCntrId
});
@@ -77,11 +77,11 @@ define([
// Create a nested "architecture" node and set the ptr target to it
baseId = base.getId();
this.client.startTransaction(msg);
tgtId = this.client.createChild({
tgtId = this.client.createNode({
parentId: this._node.id,
baseId: baseId
});
this.client.setAttributes(tgtId, 'name', `${ptr} (${this._node.name})`);
this.client.setAttribute(tgtId, 'name', `${ptr} (${this._node.name})`);
this.savePointer(ptr, tgtId);
this.client.completeTransaction();
WebGMEGlobal.State.registerActiveObject(tgtId);
@@ -95,7 +95,7 @@ define([
// 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.deleteNode(currentId);
this.client.completeTransaction();
this.logger.info(`Removed ${ptr} and deleted target (${currentId})`);
} else {
@@ -34,7 +34,13 @@ define([
this.enableTooltip(this._node.baseName, 'dark');
}
DecoratorBase.prototype.initialize.call(this);
this.$name.on('dblclick', this.editName.bind(this));
this.$name.on('click', () => {
// Operations must already be selected. Otherwise, they will animate
// after the edit name box is created and it will be placed incorrectly
if (this.expanded || !this.isOperation()) {
this.editName();
}
});
};
OpIntDecorator.prototype.AttributeField = AttributeField;
@@ -102,8 +108,8 @@ define([
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.delAttributeMeta(this._node.id, name);
this.client.delAttribute(this._node.id, name);
this.client.completeTransaction();
};
@@ -129,14 +135,14 @@ define([
if (name !== desc.name) { // Renaming attribute
if (name) {
this.client.removeAttributeSchema(this._node.id, name);
this.client.delAttributes(this._node.id, name);
this.client.delAttributeMeta(this._node.id, name);
this.client.delAttribute(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.setAttributeMeta(this._node.id, name, schema);
this.client.setAttribute(this._node.id, name, desc.defaultValue);
this.client.completeTransaction();
};
@@ -2,9 +2,11 @@
/*jshint browser: true, camelcase: false*/
define([
'deepforge/Constants',
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
'css!./OperationDecorator.EasyDAGWidget.css'
], function (
CONSTANTS,
DecoratorBase
) {
@@ -13,6 +15,7 @@ define([
var OperationDecorator,
NAME_MARGIN = 25,
DECORATOR_ID = 'OperationDecorator',
OPERATION_COLORS = {},
PORT_TOOLTIP_OPTS = {
tipJoint: 'left',
removeElementsOnHide: true,
@@ -24,8 +27,10 @@ define([
// - highlight ports
// - unhighlight ports
// - report the location of specific ports
OPERATION_COLORS[CONSTANTS.OP.OUTPUT] = '#b0bec5';
OPERATION_COLORS[CONSTANTS.OP.INPUT] = '#b0bec5';
OperationDecorator = function (options) {
options.color = options.color || '#78909c';
options.color = OPERATION_COLORS[options.node.name] || options.color || '#78909c';
DecoratorBase.call(this, options);
this.id = this._node.id;
@@ -73,14 +78,15 @@ define([
OperationDecorator.prototype.showPorts = function(ids, areInputs) {
var allPorts = areInputs ? this._node.inputs : this._node.outputs,
ports = ids ? allPorts.filter(port => ids.indexOf(port.id) > -1) : allPorts,
x = -this.width/2,
dx = this.width/(ports.length+1),
dx = this.width/(allPorts.length+1),
y = areInputs ? 0 : this.height; // (this.height/2);
ports.forEach(port => {
allPorts.forEach(port => {
x += dx;
this.renderPort(port, x, y, areInputs);
if (!ids || ids.indexOf(port.id) > -1) {
this.renderPort(port, x, y, areInputs);
}
});
};
@@ -117,7 +123,7 @@ define([
tooltip = new Opentip(portIcon[0][0], PORT_TOOLTIP_OPTS);
tooltip.setContent(port.name);
portIcon.on('mouseenter', () => tooltip.show());
portIcon.on('mouseout', () => tooltip.hide());
portIcon.on('mouseleave', () => tooltip.hide());
this.$portTooltips[port.id] = tooltip;
};
+12
Ver Arquivo
@@ -0,0 +1,12 @@
.ui-layout-center .layout-center {
left: 40px;
height: 100%;
width: 100%;
position: absolute;
}
.ui-layout-sidebar {
top: 64px;
width: 40px;
bottom: 27px;
}
+57
Ver Arquivo
@@ -0,0 +1,57 @@
/*globals define, */
define([
'layout/CHFLayout/CHFLayout/CHFLayout',
'text!./templates/SidebarLayout.html',
'css!./SidebarLayout.css'
], function(
CHFLayout,
SidebarTemplate
) {
'use strict';
var SidebarLayout = function(params) {
params = params || {};
params.template = SidebarTemplate;
CHFLayout.call(this, params);
};
SidebarLayout.prototype = Object.create(CHFLayout.prototype);
SidebarLayout.prototype.getComponentId = function () {
return 'SidebarLayout';
};
/**
* Initialize the html page. This example is using the jQuery Layout plugin.
*
* @return {undefined}
*/
SidebarLayout.prototype.init = function() {
CHFLayout.prototype.init.apply(this, arguments);
this._sidebarPanel = this._body.find('div.ui-layout-sidebar');
this._centerPanel = this._body.find('div.layout-center');
};
/**
* Add a panel to a given container. This is defined in the corresponding
* layout config JSON file.
*
* @param {Panel} panel
* @param {String} container
* @return {undefined}
*/
SidebarLayout.prototype.addToContainer = function(panel, container) {
if (container === 'sidebar') {
this._sidebarPanel.append(panel.$pEl);
} else {
CHFLayout.prototype.addToContainer.apply(this, arguments);
}
};
SidebarLayout.prototype._onCenterResize = function() {
var width = this._centerPanel.width() - this._sidebarPanel.width();
this._canvas.setSize(width, this._centerPanel.height());
};
return SidebarLayout;
});
@@ -0,0 +1,7 @@
<div class="ui-layout-center" style="position: relative;">
<div class="layout-center"></div>
<div class="float"></div>
</div>
<div class="ui-layout-sidebar"></div>
<div class="ui-layout-north"></div>
<div class="ui-layout-south"></div>
+1 -1
Ver Arquivo
@@ -92,7 +92,7 @@ define([
this.logger.info(`${tuple[0]} version info:\n${projVersion} ` +
`(project)\n${latest} (latest)`);
return latest !== projVersion;
return projVersion < latest;
});
return Q.all(tuples.map(tuple => this.uploadSeed.apply(this, tuple)));
+4 -1
Ver Arquivo
@@ -173,7 +173,10 @@ define([
name,
i = 2;
basename = basename.replace(/[^\da-zA-Z_]/g, '_');
basename = basename
.replace(/^\s*/, '')
.replace(/\s*$/, '')
.replace(/[^\da-zA-Z_]/g, '_');
name = basename;
// Get a unique name wrt the tags and the other executions
+281 -19
Ver Arquivo
@@ -65,7 +65,14 @@
"affine"
],
"setters": {},
"defaults": {},
"types": {
"eps": "number",
"momentum": "number"
},
"defaults": {
"momentum": 0.1,
"eps": 0.00001
},
"type": "Misc"
},
{
@@ -82,6 +89,35 @@
"defaults": {},
"type": "Misc"
},
{
"name": "Bottle",
"baseType": "Container",
"params": [
"module",
"nInputDim",
"nOutputDim"
],
"setters": {},
"types": {
"nInputDim": "number",
"module": "nn.Module"
},
"defaults": {
"nInputDim": 2
},
"type": "Container"
},
{
"name": "CAdd",
"baseType": "Module",
"params": [
"params"
],
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
},
{
"name": "CAddTable",
"baseType": "Module",
@@ -102,6 +138,24 @@
"defaults": {},
"type": "Misc"
},
{
"name": "CMaxTable",
"baseType": "Module",
"params": [],
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
},
{
"name": "CMinTable",
"baseType": "Module",
"params": [],
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
},
{
"name": "CMul",
"baseType": "Module",
@@ -175,7 +229,7 @@
"setters": {},
"types": {},
"defaults": {},
"type": "Containers"
"type": "Simple"
},
{
"name": "ConcatTable",
@@ -184,7 +238,7 @@
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
"type": "Container"
},
{
"name": "Contiguous",
@@ -296,13 +350,16 @@
"params": [
"p",
"v1",
"inplace"
"inplace",
"stochasticInference"
],
"setters": {},
"types": {
"p": "number"
"p": "number",
"stochasticInference": "boolean"
},
"defaults": {
"stochasticInference": false,
"p": 0.5
},
"type": "Simple"
@@ -357,8 +414,11 @@
{
"name": "GradientReversal",
"baseType": "Module",
"params": [],
"params": [
"lambda"
],
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
},
@@ -382,7 +442,8 @@
"baseType": "Module",
"params": [
"min_value",
"max_value"
"max_value",
"inplace"
],
"setters": {},
"types": {
@@ -508,9 +569,7 @@
{
"name": "Log",
"baseType": "Module",
"params": [
"inputSize"
],
"params": [],
"setters": {},
"types": {},
"defaults": {},
@@ -865,7 +924,7 @@
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
"type": "Container"
},
{
"name": "ParallelCriterion",
@@ -885,7 +944,7 @@
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
"type": "Container"
},
{
"name": "PartialLinear",
@@ -900,6 +959,17 @@
"defaults": {},
"type": "Misc"
},
{
"name": "PixelShuffle",
"baseType": "Module",
"params": [
"upscaleFactor"
],
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
},
{
"name": "Power",
"baseType": "Module",
@@ -939,6 +1009,17 @@
"defaults": {},
"type": "Transfer"
},
{
"name": "ReLU6",
"baseType": "Module",
"params": [
"inplace"
],
"setters": {},
"types": {},
"defaults": {},
"type": "Transfer"
},
{
"name": "Replicate",
"baseType": "Module",
@@ -1156,6 +1237,18 @@
"defaults": {},
"type": "Convolution"
},
{
"name": "SpatialClassNLLCriterion",
"baseType": "Criterion",
"params": [
"weights",
"sizeAverage"
],
"setters": {},
"types": {},
"defaults": {},
"type": "Criterion"
},
{
"name": "SpatialContrastiveNormalization",
"baseType": "Module",
@@ -1279,6 +1372,56 @@
},
"type": "Convolution"
},
{
"name": "SpatialDilatedConvolution",
"baseType": "SpatialConvolution",
"params": [
"nInputPlane",
"nOutputPlane",
"kW",
"kH",
"dW",
"dH",
"padW",
"padH",
"dilationW",
"dilationH"
],
"setters": {},
"types": {
"dilationW": "number",
"dilationH": "number"
},
"defaults": {
"dilationH": 1,
"dilationW": 1
},
"type": "Convolution"
},
{
"name": "SpatialDilatedMaxPooling",
"baseType": "SpatialMaxPooling",
"params": [
"kW",
"kH",
"dW",
"dH",
"padW",
"padH",
"dilationW",
"dilationH"
],
"setters": {},
"types": {
"dilationW": "number",
"dilationH": "number"
},
"defaults": {
"dilationH": 1,
"dilationW": 1
},
"type": "Misc"
},
{
"name": "SpatialDivisiveNormalization",
"baseType": "Module",
@@ -1301,13 +1444,16 @@
"name": "SpatialDropout",
"baseType": "Module",
"params": [
"p"
"p",
"stochasticInference"
],
"setters": {},
"types": {
"p": "number"
"p": "number",
"stochasticInference": "boolean"
},
"defaults": {
"stochasticInference": false,
"p": 0.5
},
"type": "Convolution"
@@ -1387,6 +1533,14 @@
"defaults": {},
"type": "Convolution"
},
{
"name": "SpatialLogSoftMax",
"baseType": "Module",
"params": [],
"setters": {},
"defaults": {},
"type": "Misc"
},
{
"name": "SpatialMaxPooling",
"baseType": "Module",
@@ -1497,6 +1651,17 @@
},
"type": "Convolution"
},
{
"name": "SpatialUpSamplingBilinear",
"baseType": "Module",
"params": [
"params"
],
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
},
{
"name": "SpatialUpSamplingNearest",
"baseType": "Module",
@@ -1627,6 +1792,22 @@
"defaults": {},
"type": "Convolution"
},
{
"name": "TemporalDynamicKMaxPooling",
"baseType": "Module",
"params": [
"minK",
"factor"
],
"setters": {},
"types": {
"factor": "number"
},
"defaults": {
"factor": 0
},
"type": "Misc"
},
{
"name": "TemporalMaxPooling",
"baseType": "Module",
@@ -1764,16 +1945,81 @@
"type": "Convolution"
},
{
"name": "VolumetricDropout",
"baseType": "Module",
"name": "VolumetricDilatedConvolution",
"baseType": "VolumetricConvolution",
"params": [
"p"
"nInputPlane",
"nOutputPlane",
"kT",
"kW",
"kH",
"dT",
"dW",
"dH",
"padT",
"padW",
"padH",
"dilationT",
"dilationW",
"dilationH"
],
"setters": {},
"types": {
"p": "number"
"dilationT": "number",
"dilationW": "number",
"dilationH": "number"
},
"defaults": {
"dilationH": 1,
"dilationW": 1,
"dilationT": 1
},
"type": "Misc"
},
{
"name": "VolumetricDilatedMaxPooling",
"baseType": "VolumetricMaxPooling",
"params": [
"kT",
"kW",
"kH",
"dT",
"dW",
"dH",
"padT",
"padW",
"padH",
"dilationT",
"dilationW",
"dilationH"
],
"setters": {},
"types": {
"dilationT": "number",
"dilationW": "number",
"dilationH": "number"
},
"defaults": {
"dilationH": 1,
"dilationW": 1,
"dilationT": 1
},
"type": "Misc"
},
{
"name": "VolumetricDropout",
"baseType": "Module",
"params": [
"p",
"stochasticInference"
],
"setters": {},
"types": {
"p": "number",
"stochasticInference": "boolean"
},
"defaults": {
"stochasticInference": false,
"p": 0.5
},
"type": "Convolution"
@@ -1864,6 +2110,22 @@
"defaults": {},
"type": "Convolution"
},
{
"name": "VolumetricReplicationPadding",
"baseType": "Module",
"params": [
"pleft",
"pright",
"ptop",
"pbottom",
"pfront",
"pback"
],
"setters": {},
"types": {},
"defaults": {},
"type": "Misc"
},
{
"name": "WeightedEuclidean",
"baseType": "Module",
@@ -1887,4 +2149,4 @@
"defaults": {},
"type": "Criterion"
}
]
]
+146
Ver Arquivo
@@ -0,0 +1,146 @@
/*globals define*/
define([
'deepforge/Constants'
], function(
CONSTANTS
) {
var ExecuteJob = function() {
};
ExecuteJob.prototype[CONSTANTS.GRAPH_CREATE] = function (job, id) {
var graph,
name = Array.prototype.slice.call(arguments, 2).join(' '),
jobId = this.core.getPath(job);
id = jobId + '/' + id;
this.logger.info(`Creating graph ${id} named ${name}`);
// Check if the graph already exists
graph = this._getExistingMetadata(jobId, 'Graph', name);
if (!graph) {
graph = this.createNode('Graph', job);
if (name) {
this.setAttribute(graph, 'name', name);
}
this.createIdToMetadataId[graph] = id;
}
this._metadata[id] = graph;
};
ExecuteJob.prototype[CONSTANTS.GRAPH_LABEL_AXIS.X] = function (job, id) {
var name = Array.prototype.slice.call(arguments, 2).join(' '),
jobId = this.core.getPath(job),
graph;
id = jobId + '/' + id;
this.logger.info(`Labeling the x-axis of ${id}: ${name}`);
graph = this._metadata[id];
this.setAttribute(graph, 'xlabel', name);
};
ExecuteJob.prototype[CONSTANTS.GRAPH_LABEL_AXIS.Y] = function (job, id) {
var name = Array.prototype.slice.call(arguments, 2).join(' '),
jobId = this.core.getPath(job),
graph;
id = jobId + '/' + id;
this.logger.info(`Labeling the y-axis of ${id}: ${name}`);
graph = this._metadata[id];
this.setAttribute(graph, 'ylabel', name);
};
ExecuteJob.prototype[CONSTANTS.GRAPH_PLOT] = function (job, id, x, y) {
var jobId = this.core.getPath(job),
nonNum = /[^\d-\.]*/g,
line,
points;
id = jobId + '/' + id;
this.logger.info(`Adding point ${x}, ${y} to ${id}`);
line = this._metadata[id];
if (!line) {
this.logger.warn(`Can't add point to non-existent line: ${id}`);
return;
}
// Clean the points by removing and special characters
x = x.replace(nonNum, '');
y = y.replace(nonNum, '');
points = this.getAttribute(line, 'points');
points += `${x},${y};`;
this.setAttribute(line, 'points', points);
};
ExecuteJob.prototype[CONSTANTS.GRAPH_CREATE_LINE] = function (job, graphId, id) {
var jobId = this.core.getPath(job),
graph = this._metadata[jobId + '/' + graphId],
name = Array.prototype.slice.call(arguments, 3).join(' '),
line;
// Create a 'line' node in the given Graph metadata node
name = name.replace(/\s+$/, '');
line = this.createNode('Line', graph);
this.setAttribute(line, 'name', name);
this._metadata[jobId + '/' + id] = line;
this.createIdToMetadataId[line] = jobId + '/' + id;
};
ExecuteJob.prototype[CONSTANTS.IMAGE.BASIC] =
ExecuteJob.prototype[CONSTANTS.IMAGE.UPDATE] =
ExecuteJob.prototype[CONSTANTS.IMAGE.CREATE] = function (job, hash, imgId) {
var name = Array.prototype.slice.call(arguments, 3).join(' '),
imageNode = this._getImageNode(job, imgId, name);
this.setAttribute(imageNode, 'data', hash);
};
ExecuteJob.prototype[CONSTANTS.IMAGE.NAME] = function (job, imgId) {
var name = Array.prototype.slice.call(arguments, 2).join(' '),
imageNode = this._getImageNode(job, imgId, name);
this.setAttribute(imageNode, 'name', name);
};
ExecuteJob.prototype._getImageNode = function (job, imgId, name) {
var jobId = this.core.getPath(job),
id = jobId + '/IMAGE/' + imgId,
imageNode = this._metadata[id]; // Look for the metadata imageNode
if (!imageNode) {
// Check if the imageNode already exists
imageNode = this._getExistingMetadata(jobId, 'Image', name);
if (!imageNode) {
this.logger.info(`Creating image ${id} named ${name}`);
imageNode = this.createNode('Image', job);
this.setAttribute(imageNode, 'name', name);
this.createIdToMetadataId[imageNode] = id;
}
this._metadata[id] = imageNode;
}
return imageNode;
};
ExecuteJob.prototype._getExistingMetadata = function (jobId, type, name) {
var oldMetadata = this._oldMetadataByName[jobId] && this._oldMetadataByName[jobId][type],
node,
id;
if (oldMetadata && oldMetadata[name]) {
id = oldMetadata[name];
node = this._markForDeletion[jobId][id];
delete this._markForDeletion[jobId][id];
this.createdMetadataIds[jobId].push(id); // used for resuming jobs
}
return node || null;
};
return ExecuteJob;
});
+377
Ver Arquivo
@@ -0,0 +1,377 @@
/*globals define*/
define([
'plugin/PluginBase',
'common/storage/constants',
'q',
'common/util/assert'
], function(
PluginBase,
STORAGE_CONSTANTS,
Q,
assert
) {
var CREATE_PREFIX = 'created_node_',
INDEX = 1;
var ExecuteJob = function() {
this.forkNameBase = null;
this.runningJobHashes = [];
this._currentSave = Q();
};
ExecuteJob.prototype.getCreateId = function () {
return CREATE_PREFIX + (++INDEX);
};
ExecuteJob.prototype.isCreateId = function (id) {
return (typeof id === 'string') && (id.indexOf(CREATE_PREFIX) === 0);
};
ExecuteJob.prototype.createNode = function (baseType, parent) {
var id = this.getCreateId(),
parentId = this.isCreateId(parent) ? parent : this.core.getPath(parent);
this.logger.info(`Creating ${id} of type ${baseType} in ${parentId}`);
assert(this.META[baseType], `Cannot create node w/ unrecognized type: ${baseType}`);
this.creations[id] = {
base: baseType,
parent: parentId
};
return id;
};
ExecuteJob.prototype.deleteNode = function (nodeId) {
this.deletions.push(nodeId);
};
ExecuteJob.prototype.delAttribute = function (node, attr) {
return this.setAttribute(node, attr, null);
};
ExecuteJob.prototype.setAttribute = function (node, attr, value) {
var nodeId;
if (this.isCreateId(node)) {
nodeId = node;
} else {
nodeId = this.core.getPath(node);
assert(typeof nodeId === 'string', `Cannot set attribute of ${nodeId}`);
}
if (value !== null) {
this.logger.info(`Setting ${attr} of ${nodeId} to ${value}`);
} else {
this.logger.info(`Deleting ${attr} of ${nodeId}`);
}
if (!this.changes[nodeId]) {
this.changes[nodeId] = {};
}
this.changes[nodeId][attr] = value;
};
ExecuteJob.prototype.getAttribute = function (node, attr) {
var nodeId;
assert(this.deletions.indexOf(nodeId) === -1,
`Cannot get ${attr} from deleted node ${nodeId}`);
// Check if it was newly created
if (this.isCreateId(node)) {
nodeId = node;
assert(this.creations[nodeId], `Creation node not updated: ${nodeId}`);
node = this.META[this.creations[nodeId].base];
} else {
nodeId = this.core.getPath(node);
}
// Check the most recent changes, then the currentChanges, then the model
var value = this._getValueFrom(nodeId, attr, node, this.changes) ||
this._getValueFrom(nodeId, attr, node, this.currentChanges);
if (value) {
return value;
}
return this.core.getAttribute(node, attr);
};
ExecuteJob.prototype._getValueFrom = function (nodeId, attr, node, changes) {
var base;
if (changes[nodeId] && changes[nodeId][attr] !== undefined) {
// If deleted the attribute, get the default (inherited) value
if (changes[nodeId][attr] === null) {
base = this.isCreateId(nodeId) ? node : this.core.getBase(node);
return this.getAttribute(base, attr);
}
return changes[nodeId][attr];
}
};
ExecuteJob.prototype._applyNodeChanges = function (node, changes) {
var attr,
value;
this.logger.info(`About to apply changes for ${this.core.getPath(node)}`);
for (var i = changes.length; i--;) {
attr = changes[i][0];
value = changes[i][1];
if (value !== null) {
this.logger.info(`Setting ${attr} to ${value} (${this.core.getPath(node)})`);
this.core.setAttribute(node, attr, value);
} else {
this.core.delAttribute(node, attr);
}
}
return node;
};
ExecuteJob.prototype.applyModelChanges = function () {
return this.applyCreations()
.then(() => this.applyChanges())
.then(() => this.applyDeletions());
};
ExecuteJob.prototype.applyChanges = function () {
var nodeIds = Object.keys(this.changes),
attrs,
value,
changes,
promises = [],
changesFor = {},
id,
promise;
this.logger.info('Collecting changes to apply in commit');
for (var i = nodeIds.length; i--;) {
changes = [];
attrs = Object.keys(this.changes[nodeIds[i]]);
for (var a = attrs.length; a--;) {
value = this.changes[nodeIds[i]][attrs[a]];
changes.push([attrs[a], value]);
}
changesFor[nodeIds[i]] = changes;
assert(changes, `changes are invalid for ${nodeIds[i]}: ${changes}`);
assert(!this.isCreateId(nodeIds[i]),
`Creation id not resolved to actual id: ${nodeIds[i]}`);
promise = this.core.loadByPath(this.rootNode, nodeIds[i]);
promises.push(promise);
}
this.currentChanges = this.changes;
this.changes = {};
// Need to differentiate between read/write changes.
this.logger.info(`About to apply changes for ${promises.length} nodes`);
return Q.all(promises)
.then(nodes => {
for (var i = nodes.length; i--;) {
id = this.core.getPath(nodes[i]);
assert(nodes[i], `node is ${nodes[i]} (${nodeIds[i]})`);
this._applyNodeChanges(nodes[i], changesFor[id]);
}
// Local model is now up-to-date. No longer need currentChanges
this.currentChanges = {};
});
};
ExecuteJob.prototype.applyCreations = function () {
var nodeIds = Object.keys(this.creations),
tiers = this.createCreationTiers(nodeIds),
creations = this.creations,
newIds = {},
promise = Q(),
tier;
this.logger.info('Applying node creations');
for (var i = 0; i < tiers.length; i++) {
tier = tiers[i];
// Chain the promises, loading each tier sequentially
promise = promise.then(this.applyCreationTier.bind(this, creations, newIds, tier));
}
this.creations = {};
return promise;
};
ExecuteJob.prototype.applyCreationTier = function (creations, newIds, tier) {
var promises = [],
parentId,
node;
for (var j = tier.length; j--;) {
node = creations[tier[j]];
assert(node, `Could not find create info for ${tier[j]}`);
parentId = newIds[node.parent] || node.parent;
promises.push(this.applyCreation(tier[j], node.base, parentId));
}
return Q.all(promises).then(nodes => {
// Record the newIds so they can be used to resolve creation ids
// in subsequent tiers
for (var i = tier.length; i--;) {
newIds[tier[i]] = this.core.getPath(nodes[i]);
}
});
};
// Figure out the dependencies between nodes to create.
// eg, if newId1 is to be created in newId2, then newId2 will
// be in an earlier tier than newId1. Essentially a topo-sort
// on a tree structure
ExecuteJob.prototype.createCreationTiers = function (nodeIds) {
var tiers = [],
prevTier = {},
tier = {},
id,
prevLen,
i;
// Create first tier (created inside existing nodes)
for (i = nodeIds.length; i--;) {
id = nodeIds[i];
if (!this.isCreateId(this.creations[id].parent)) {
tier[id] = true;
nodeIds.splice(i, 1);
}
}
prevTier = tier;
tiers.push(Object.keys(tier));
// Now, each tier consists of the nodes to be created inside a
// node from the previous tier
while (nodeIds.length) {
prevLen = nodeIds.length;
tier = {};
for (i = nodeIds.length; i--;) {
id = nodeIds[i];
if (prevTier[this.creations[id].parent]) {
tier[id] = true;
nodeIds.splice(i, 1);
}
}
prevTier = tier;
tiers.push(Object.keys(tier));
// Every iteration should find at least one node
assert(prevLen > nodeIds.length,
`Created empty create tier! Remaining: ${nodeIds.join(', ')}`);
}
return tiers;
};
ExecuteJob.prototype.applyCreation = function (tmpId, baseType, parentId) {
var base = this.META[baseType],
nodeId,
id;
this.logger.info(`Applying creation of ${tmpId} (${baseType}) in ${parentId}`);
assert(!this.isCreateId(parentId),
`Did not resolve parent id: ${parentId} for ${tmpId}`);
assert(base, `Invalid base type: ${baseType}`);
return this.core.loadByPath(this.rootNode, parentId)
.then(parent => this.core.createNode({base, parent}))
.then(node => { // Update the _metadata records
id = this.createIdToMetadataId[tmpId];
delete this.createIdToMetadataId[tmpId];
this._metadata[id] = node;
// Update creations
nodeId = this.core.getPath(node);
if (this.changes[tmpId]) {
assert(!this.changes[nodeId],
`Newly created node cannot already have changes! (${nodeId})`);
this.changes[nodeId] = this.changes[tmpId];
delete this.changes[tmpId];
}
return node;
});
};
ExecuteJob.prototype.applyDeletions = function () {
var deletions = this.deletions;
this.deletions = [];
return Q.all(deletions.map(id => this.core.loadByPath(this.rootNode, id)))
.then(nodes => {
for (var i = nodes.length; i--;) {
this.core.deleteNode(nodes[i]);
}
});
};
// Override 'save' to notify the user on fork
ExecuteJob.prototype.save = function (msg) {
this._currentSave = this._currentSave
.then(() => this.updateForkName(this.forkNameBase))
.then(() => this.applyModelChanges())
.then(() => PluginBase.prototype.save.call(this, msg))
.then(result => {
this.logger.info(`Save finished w/ status: ${result.status}`);
if (result.status === STORAGE_CONSTANTS.FORKED) {
return this.onSaveForked(result.forkName);
} else if (result.status === STORAGE_CONSTANTS.MERGED) {
this.logger.debug('Merged changes. About to update plugin nodes');
return this.updateNodes();
}
});
return this._currentSave;
};
ExecuteJob.prototype.onSaveForked = function (forkName) {
var name = this.getAttribute(this.activeNode, 'name'),
msg = `"${name}" execution has forked to "${forkName}"`;
this.currentForkName = forkName;
this.logManager.fork(forkName);
this.runningJobHashes.forEach(jobId => this.originManager.fork(jobId, forkName));
this.sendNotification(msg);
};
ExecuteJob.prototype.updateNodes = function (hash) {
var activeId = this.core.getPath(this.activeNode);
hash = hash || this.currentHash;
return Q.ninvoke(this.project, 'loadObject', hash)
.then(commitObject => {
return this.core.loadRoot(commitObject.root);
})
.then(rootObject => {
this.rootNode = rootObject;
return this.core.loadByPath(rootObject,activeId);
})
.then(activeObject => this.activeNode = activeObject)
.then(() => {
var metaNames = Object.keys(this.META);
return Q.all(metaNames.map(name => this.updateMetaNode(name)));
})
.then(() => {
var mdNodes,
mdIds;
mdIds = Object.keys(this._metadata)
.filter(id => !this.isCreateId(this._metadata[id]));
mdNodes = mdIds.map(id => this.core.getPath(this._metadata[id]))
.map(nodeId => this.core.loadByPath(this.rootNode, nodeId));
return Q.all(mdNodes).then(nodes => {
for (var i = nodes.length; i--;) {
this._metadata[mdIds[i]] = nodes[i];
}
});
});
};
ExecuteJob.prototype.updateMetaNode = function (name) {
var id = this.core.getPath(this.META[name]);
return this.core.loadByPath(this.rootNode, id).then(node => this.META[name] = node);
};
return ExecuteJob;
});
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+171 -69
Ver Arquivo
@@ -6,6 +6,7 @@ define([
'plugin/ExecuteJob/ExecuteJob/ExecuteJob',
'common/storage/constants',
'common/core/constants',
'deepforge/Constants',
'q',
'text!./metadata.json',
'underscore'
@@ -13,6 +14,7 @@ define([
CreateExecution,
ExecuteJob,
STORAGE_CONSTANTS,
GME_CONSTANTS,
CONSTANTS,
Q,
pluginMetadata,
@@ -31,9 +33,9 @@ define([
var ExecutePipeline = function () {
// Call base class' constructor.
CreateExecution.call(this);
ExecuteJob.call(this);
this.pluginMetadata = pluginMetadata;
this._currentSave = Q();
this.changes = {};
this.currentChanges = {}; // read-only changes being applied
this.creations = {};
@@ -99,7 +101,8 @@ define([
* @param {function(string, plugin.PluginResult)} callback - the result callback
*/
ExecutePipeline.prototype.main = function (callback) {
var startPromise;
var startPromise,
runId;
this.initRun();
if (this.core.isTypeOf(this.activeNode, this.META.Pipeline)) {
@@ -120,47 +123,131 @@ define([
this.currentForkName = null;
startPromise
.then(() => this.core.loadSubTree(this.activeNode))
.then(subtree => {
var children = subtree
.filter(n => this.core.getParent(n) === this.activeNode);
.then(() => this.core.loadSubTree(this.activeNode))
.then(subtree => {
var children;
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
children = subtree
.filter(n => this.core.getParent(n) === this.activeNode);
this.logger.debug('Clearing old results');
return this.clearResults();
})
.then(() => this.executePipeline())
.fail(e => this.logger.error(e));
this.pipelineName = this.getAttribute(this.activeNode, 'name');
this.forkNameBase = this.pipelineName;
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
// Detect if resuming execution
runId = this.getAttribute(this.activeNode, 'runId');
return this.isResuming().then(resuming => {
if (resuming) {
this.currentRunId = runId;
this.startExecHeartBeat();
return this.resumePipeline();
}
return this.startPipeline();
});
})
.fail(e => this.logger.error(e));
};
// Override 'save' to prevent race conditions while saving
ExecutePipeline.prototype.save = function (msg) {
// When 'save' is called, it should still finish any current save op
// before continuing
this._currentSave = this._currentSave
.then(() => this.updateForkName(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();
ExecutePipeline.prototype.isResuming = function () {
var currentlyRunning = this.getAttribute(this.activeNode, 'status') === 'running',
runId = this.getAttribute(this.activeNode, 'runId');
if (runId && currentlyRunning) {
// Verify that it is on the correct branch
return this.originManager.getOrigin(runId)
.then(origin => {
if (origin && origin.branch === this.branchName) {
return this.pulseClient.check(runId)
// If it is dead (not unknown!), then resume
.then(status => status === CONSTANTS.PULSE.DEAD);
} else {
return false;
}
});
}
return Q().then(() => false);
};
ExecutePipeline.prototype.resumePipeline = function () {
var nodes = Object.keys(this.nodes).map(id => this.nodes[id]),
allJobs = nodes.filter(node => this.core.isTypeOf(node, this.META.Job)),
name = this.getAttribute(this.activeNode, 'name'),
status,
jobs = {
success: [],
failed: [],
running: [],
pending: []
};
this.logger.info(`Resuming pipeline execution: ${this.currentRunId}`);
// Get all completed jobs' operations and update records for these
for (var i = allJobs.length; i--;) {
status = this.getAttribute(allJobs[i], 'status');
if (!jobs[status]) {
jobs[status] = [];
}
// If any running jobs are missing jobIds, set them to pending
if (status === 'running' && !this.canResumeJob(allJobs[i])) {
jobs.pending.push(allJobs[i]);
} else {
jobs[status].push(allJobs[i]);
}
}
// Remove finished jobs from incomingCounts
jobs.success.concat(jobs.failed, jobs.running)
.map(job => this.core.getPath(job))
.forEach(id => delete this.incomingCounts[id]);
return Q.all(allJobs.map(job => this.recordOldMetadata(job, true)))
.then(() => Q.all(jobs.success.map(job => this.getOperation(job))))
.then(ops => ops.forEach(op => this.updateJobCompletionRecords(op)))
.then(() => this.save(`Resuming pipeline execution: ${name}`))
.then(() => {
if (jobs.running.length) { // Resume all running jobs
return Q.all(jobs.running.map(job => this.resumeJob(job)));
} else if (this.completedCount === this.totalCount) {
return this.onPipelineComplete();
} else {
// If none are running, try to start the next ones
return this.executeReadyOperations();
}
})
.fail(err => this._callback(err));
};
});
ExecutePipeline.prototype.startPipeline = function () {
var rand = Math.floor(Math.random()*10000),
commit = this.commitHash.replace('#', '');
return this._currentSave;
this.logger.debug('Clearing old results');
this.currentRunId = `Pipeline_${commit}_${Date.now()}_${rand}`;
// Record the execution origin
this.originManager.record(this.currentRunId, {
nodeId: this.core.getPath(this.activeNode),
job: 'N/A',
execution: this.getAttribute(this.activeNode, 'name')
});
this.startExecHeartBeat();
return this.clearResults()
.then(() => this.executePipeline())
.fail(e => this.logger.error(e));
};
ExecutePipeline.prototype.onSaveForked = function (forkName) {
// Update the origin on fork
this.originManager.fork(this.currentRunId, forkName);
return ExecuteJob.prototype.onSaveForked.call(this, forkName);
};
ExecutePipeline.prototype.updateNodes = function (hash) {
@@ -211,6 +298,7 @@ define([
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.setAttribute(this.activeNode, 'runId', this.currentRunId);
this.core.delAttribute(this.activeNode, 'endTime');
return this.save(`Initializing ${this.pipelineName} for execution`);
};
@@ -288,10 +376,10 @@ define([
};
ExecutePipeline.prototype.getSiblingIdContaining = function (nodeId) {
var parentId = this.core.getPath(this.activeNode) + CONSTANTS.PATH_SEP,
var parentId = this.core.getPath(this.activeNode) + GME_CONSTANTS.PATH_SEP,
relid = nodeId.replace(parentId, '');
return parentId + relid.split(CONSTANTS.PATH_SEP).shift();
return parentId + relid.split(GME_CONSTANTS.PATH_SEP).shift();
};
ExecutePipeline.prototype.executePipeline = function() {
@@ -349,7 +437,8 @@ define([
msg += 'finished!';
}
this.isDeleted().then(isDeleted => {
return this.isDeleted().then(isDeleted => {
this.stopExecHeartBeat();
if (!isDeleted) {
this.logger.debug(`Pipeline "${name}" complete!`);
@@ -432,10 +521,9 @@ define([
ExecutePipeline.prototype.onOperationComplete = function (opNode) {
var name = this.getAttribute(opNode, 'name'),
nextPortIds = this.getOperationOutputIds(opNode),
jNode = this.core.getParent(opNode),
resultPorts,
jobId = this.core.getPath(jNode),
counts,
hasReadyOps;
// Set the operation to 'success'!
@@ -445,38 +533,14 @@ define([
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}`);
counts = this.updateJobCompletionRecords(opNode);
this.save(`Operation "${name}" in ${this.pipelineName} completed successfully`)
.then(() => {
// Transport the data from the outputs to any connected inputs
// - Get all the connections from each outputId
// - Get the corresponding dst outputs
// - Use these new ids for checking 'hasReadyOps'
resultPorts = nextPortIds.map(id => this.inputPortsFor[id])
.reduce((l1, l2) => l1.concat(l2), []);
hasReadyOps = counts.indexOf(0) > -1;
resultPorts
.map((id, i) => [this.nodes[id], this.nodes[nextPortIds[i]]])
.forEach(pair => { // [ resultPort, nextPort ]
var result = pair[0],
next = pair[1],
hash = this.getAttribute(result, 'data');
this.logger.info(`forwarding data (${hash}) from ${this.core.getPath(result)} ` +
`to ${this.core.getPath(next)}`);
this.setAttribute(next, 'data', hash);
this.logger.info(`Setting ${jobId} data to ${hash}`);
});
// For all the nextPortIds, decrement the corresponding operation's incoming counts
hasReadyOps = nextPortIds.map(id => this.getSiblingIdContaining(id))
.reduce((l1, l2) => l1.concat(l2), [])
// decrement the incoming counts for each operation id
.map(opId => --this.incomingCounts[opId])
.indexOf(0) > -1;
this.completedCount++;
this.logger.debug(`Operation "${name}" completed. ` +
`${this.totalCount - this.completedCount} remaining.`);
if (hasReadyOps) {
@@ -487,9 +551,47 @@ define([
});
};
ExecutePipeline.prototype.updateJobCompletionRecords = function (opNode) {
var nextPortIds = this.getOperationOutputIds(opNode),
resultPorts,
counts;
// Transport the data from the outputs to any connected inputs
// - Get all the connections from each outputId
// - Get the corresponding dst outputs
// - Use these new ids for checking 'hasReadyOps'
resultPorts = nextPortIds.map(id => this.inputPortsFor[id]) // dst -> src port
.reduce((l1, l2) => l1.concat(l2), []);
resultPorts
.map((id, i) => [this.nodes[id], this.nodes[nextPortIds[i]]])
.forEach(pair => { // [ resultPort, nextPort ]
var result = pair[0],
next = pair[1],
hash = this.getAttribute(result, 'data');
this.logger.info(`forwarding data (${hash}) from ${this.core.getPath(result)} ` +
`to ${this.core.getPath(next)}`);
this.setAttribute(next, 'data', hash);
//this.logger.info(`Setting ${jobId} data to ${hash}`);
});
// For all the nextPortIds, decrement the corresponding operation's incoming counts
counts = nextPortIds.map(id => this.getSiblingIdContaining(id))
.reduce((l1, l2) => l1.concat(l2), [])
// decrement the incoming counts for each operation id
.map(opId => --this.incomingCounts[opId]);
this.completedCount++;
return counts;
};
ExecutePipeline.prototype.getOperationOutputIds = function(node) {
var jobId = this.getSiblingIdContaining(this.core.getPath(node));
// Map the job to it's output ports
return this.outputsOf[jobId] || [];
};
+925
Ver Arquivo
@@ -0,0 +1,925 @@
/*globals define */
/*jshint node:true, browser:true*/
define([
'text!./metadata.json',
'text!./deepforge.ejs',
'./format',
'plugin/PluginBase',
'deepforge/plugin/PtrCodeGen',
'deepforge/Constants',
'blob/BlobConfig',
'underscore',
'q'
], function (
pluginMetadata,
DeepForgeBaseCode,
FORMATS,
PluginBase,
PtrCodeGen,
CONSTANTS,
BlobConfig,
_,
Q
) {
'use strict';
pluginMetadata = JSON.parse(pluginMetadata);
var HEADER_LENGTH = 60,
SKIP_ATTRS = {
lineOffset: true,
code: true
},
RESERVED = /^(and|break|do|else|elseifend|false|for|function|if|in|local|nil|not|orrepeat|return|then|true|until|while|print)$/,
DeepForgeTpl = _.template(DeepForgeBaseCode);
/**
* Initializes a new instance of Export.
* @class
* @augments {PluginBase}
* @classdesc This class represents the plugin Export.
* @constructor
*/
var Export = function () {
// Call base class' constructor.
PluginBase.call(this);
this.initRecords();
};
/**
* 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}
*/
Export.metadata = pluginMetadata;
// Prototypical inheritance from PluginBase.
Export.prototype = Object.create(PluginBase.prototype);
Export.prototype.constructor = Export;
Export.prototype.initRecords = function() {
this.pluginMetadata = pluginMetadata;
this._srcIdFor = {}; // input path -> output data node path
this._nameFor = {}; // input path -> opname
this._outputNames = {};
this._baseNameFor = {};
this._dataNameFor = {};
this._instanceNames = {};
this._opBaseNames = {};
this._fnNameFor = {};
this._functions = {}; // function definitions for the operations
// topo sort stuff
this._nextOps = {};
this._incomingCnts = {};
this._operations = {};
this.activeNodeId = null;
this.activeNodeDepth = null;
this.isInputOp = {};
this._portCache = {};
this.inputNode = {};
this.outputDataToOpId = {};
this.isOutputOp = {};
};
/**
* 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
*/
Export.prototype.main = function (callback) {
this.initRecords();
// Get all the children and call generate exec file
this.activeNodeId = this.core.getPath(this.activeNode);
this.activeNodeDepth = this.activeNodeId.split('/').length + 1;
if (this.isMetaTypeOf(this.activeNode, this.META.Execution)) {
this.activeNodeDepth++;
}
return this.core.loadChildren(this.activeNode)
.then(nodes => this.generateOutputFiles(nodes))
.catch(err => callback(err))
.then(hash => {
this.result.addArtifact(hash);
this.result.setSuccess(true);
callback(null, this.result);
})
.fail(err => callback(err));
};
Export.prototype.getCurrentConfig = function () {
var config = PluginBase.prototype.getCurrentConfig.call(this);
config.staticInputs = config.staticInputs || [];
return config;
};
Export.prototype.getExporterFor = function (name) {
var Exporter = function() {},
format = FORMATS[name],
exporter;
Exporter.prototype = this;
exporter = new Exporter();
if (typeof format === 'function') {
exporter.main = format;
} else {
_.extend(exporter, format);
}
return exporter;
};
Export.prototype.generateOutputFiles = function (children) {
var name = this.core.getAttribute(this.activeNode, 'name');
return this.createCodeSections(children)
.then(sections => {
// Get the selected format
var config = this.getCurrentConfig(),
format = config.format || 'Basic CLI',
exporter,
staticInputs,
files;
this.logger.info(`About to retrieve ${config.format} exporter`);
exporter = this.getExporterFor(format);
staticInputs = config.staticInputs.map(id => {
var opId = id.split('/').splice(0, this.activeNodeDepth).join('/'),
port = this._portCache[id];
return {
portId: id,
id: opId,
hash: this.core.getAttribute(port, 'data'),
name: this._nameFor[opId]
};
});
this.logger.info('Invoking exporter "main" function...');
try {
files = exporter.main(sections, staticInputs, config.extensionConfig);
} catch (e) {
this.logger.error(`Exporter failed: ${e.toString()}`);
throw e;
}
// If it returns a string, just put a single file
if (typeof files === 'string') {
return this.blobClient.putFile(`${name}.lua`, files);
} else { // filename -> content
var artifact = this.blobClient.createArtifact(name),
objects = {};
Object.keys(files).forEach(key => {
if (BlobConfig.hashRegex.test(files[key])) {
objects[key] = files[key];
delete files[key];
}
});
return artifact.addFiles(files)
.then(() => artifact.addObjectHashes(objects))
.then(() => artifact.save());
}
});
};
Export.prototype.createCodeSections = function (children) {
// Convert opNodes' jobs to the nested operations
var opNodes,
nodes;
return this.unpackJobs(children)
.then(_nodes => {
nodes = _nodes;
opNodes = nodes
.filter(node => this.isMetaTypeOf(node, this.META.Operation));
// Sort the connections to come first
nodes
.map(node => [
node,
this.isMetaTypeOf(node, this.META.Transporter) ? -1 : 1
])
.sort((a, b) => a[1] < b[1] ? -1 : 1);
return Q.all(nodes.map(node => this.registerNode(node)));
})
.then(() => Q.all(opNodes
.filter(n => {
var id = this.core.getPath(n);
return !this.isInputOp[id];
})
.map(node => this.createOperation(node)))
)
.then(operations => {
var opDict = {},
firstOpIds;
firstOpIds = opNodes.map(n => this.core.getPath(n))
.filter(id => !this._incomingCnts[id]);
operations.forEach(op => opDict[op.id] = op);
// Toposort!
return this.sortOperations(opDict, firstOpIds);
})
.then(operations => this.generateCodeSections(operations))
.fail(err => this.logger.error(err));
};
Export.prototype.unpackJobs = function (nodes) {
return Q.all(
nodes.map(node => {
if (!this.isMetaTypeOf(node, this.META.Job)) {
return node;
}
return this.core.loadChildren(node)
.then(children =>
children.find(c => this.isMetaTypeOf(c, this.META.Operation))
);
})
);
};
Export.prototype.sortOperations = function (operationDict, opIds) {
var nextIds = [],
sorted = opIds,
dstIds,
id;
if (!opIds.length) {
return [];
}
// Decrement all next ops
dstIds = opIds.map(id => this._nextOps[id])
.reduce((l1, l2) => l1.concat(l2), []);
for (var i = dstIds.length; i--;) {
id = dstIds[i];
if (--this._incomingCnts[id] === 0) {
nextIds.push(id);
}
}
// append
return sorted
.map(id => operationDict[id])
.filter(op => !!op)
.concat(this.sortOperations(operationDict, nextIds));
};
Export.prototype.generateCodeSections = function(sortedOps) {
// Create the code sections:
// - operation definitions
// - pipeline definition
// - main
var code = {},
baseIds = [],
outputOps = [],
mainOps = [];
// Define the operation functions...
code.operations = {};
for (var i = 0; i < sortedOps.length; i++) {
if (this.isInputOp[sortedOps[i].id]) {
continue;
}
if (!this.isOutputOp[sortedOps[i].id]) {
if (!baseIds.includes(sortedOps[i].baseId)) { // new definition
code.operations[sortedOps[i].basename] = this.defineOperationFn(sortedOps[i]);
baseIds.push(sortedOps[i].baseId);
}
mainOps.push(sortedOps[i]);
} else {
outputOps.push(sortedOps[i]);
}
}
// Define the pipeline function
code.pipelines = this.definePipelineFn(mainOps, outputOps);
// Define the serializers/deserializers
this.addCodeSerializers(code);
// Define the main input names
code.pipelineName = Object.keys(code.pipelines)[0];
code.pipelineInputNames = Object.keys(this.isInputOp).map(id => this._nameFor[id]);
// Add custom class definitions
this.addCustomClasses(code);
// Add custom layer definitions
this.addCustomLayers(code);
return code;
};
// expose this utility function to format extensions
var indent = Export.prototype.indent = function(text, spaces) {
spaces = spaces || 3;
return text.replace(/^/mg, new Array(spaces+1).join(' '));
};
Export.prototype.defineOperationFn = function(operation) {
var lines = [],
args = operation.inputNames || [];
// Create the function definition
args.unshift('attributes');
// Add the refs to the end
args = args.concat(operation.refNames);
args = args.join(', ');
lines.push(`local function ${operation.basename}(${args})`);
lines.push(indent(operation.code));
lines.push('end');
return lines.join('\n');
};
Export.prototype.definePipelineFn = function(sortedOps, outputOps) {
var inputArgs = Object.keys(this.isInputOp).map(id => this._nameFor[id]),
name = this.core.getAttribute(this.activeNode, 'name'),
safename = getUniqueName(name, this._opBaseNames),
results = [],
result = {},
returnStat,
fnbody;
// Call each function in order, with the respective attributes, etc
fnbody = sortedOps.map(op => this.getOpInvocation(op)).join('\n');
// Create the return statement
results.push('\n\nresults = {}');
outputOps.map(op => this.getOutputPair(op))
.forEach(pair => results.push(`results['${pair[0]}'] = ${pair[1]}`));
results.push('return results');
returnStat = results.join('\n');
// Merge the fnbody, return statement and the function def
result[safename] = `local function ${safename} (${inputArgs.join(', ')})\n` +
`${indent(fnbody + returnStat)}\nend`;
return result;
};
Export.prototype.getOutputPair = function(operation) {
var input = operation.inputValues[0].slice(),
value;
// Get the src operation name and data value name
input[0] += '_results';
value = input.join('.');
return [this._nameFor[operation.id], value];
};
Export.prototype.addCodeSerializers = function(sections) {
var loadNodes = {},
saveNodes = {};
// Add the serializer fn names for each input
sections.serializerFor = {};
sections.deserializerFor = {};
Object.keys(this.isOutputOp).map(id => {
var name = this._nameFor[id];
sections.serializerFor[name] = `__save['${name}']`;
});
// Add the serializer definitions
Object.keys(this.isInputOp).forEach(id => {
var node = this.inputNode[id],
name = this._nameFor[id];
loadNodes[id] = node;
sections.deserializerFor[name] = `__load['${this._nameFor[id]}']`;
});
sections.deserializers = this.createTorchFnDict(
'__load',
loadNodes,
'deserialize',
'path'
);
// Add the deserializer definitions
Object.keys(this.outputDataToOpId).forEach(dataId => {
var opId = this.outputDataToOpId[dataId];
// The key is used for the output name resolution. The
// value is used for the serialization fn look-up. So,
// the key is the output operation id and the value is
// the data port connected to the output operation
saveNodes[opId] = this._portCache[this._srcIdFor[dataId]];
});
sections.serializers = this.createTorchFnDict(
'__save',
saveNodes,
'serialize',
'path, data'
);
// Add a saveOutputs method for convenience
sections.serializeOutputsDef = [
'local function __saveOutputs(data)',
indent(Object.keys(this.isOutputOp).map(id => {
var name = this._nameFor[id];
return [
`print('saving ${name}...')`,
`${sections.serializerFor[name]}('${name}', data['${name}'])`
].join('\n');
}).join('\n')),
'end'
].join('\n');
sections.serializeOutputs = '__saveOutputs(outputs)';
};
Export.prototype.createTorchFnDict = function(name, nodeDict, attr, args) {
return [
`local ${name} = {}`,
Object.keys(nodeDict).map(id => {
var node = nodeDict[id];
return [
`${name}['${this._nameFor[id]}'] = function(${args})`,
indent(this.core.getAttribute(node, attr)),
'end'
].join('\n');
}).join('\n')
].join('\n');
};
Export.prototype.addCustomClasses = function(sections) {
var metaDict = this.core.getAllMetaNodes(this.rootNode),
isClass,
metanodes,
classNodes,
inheritanceLvl = {};
this.logger.info('Creating custom layer file...');
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
isClass = this.getTypeDictFor('Complex', metanodes);
// Store the dependencies for each class
sections.classDependencies = {};
classNodes = metanodes.filter(node => {
var base = this.core.getBase(node),
baseId,
deps = [],
name,
count = 1;
// Count the sets back to a class node
while (base) {
deps.push(this.core.getAttribute(base, 'name'));
baseId = this.core.getPath(base);
if (isClass[baseId]) {
inheritanceLvl[this.core.getPath(node)] = count;
name = this.core.getAttribute(node, 'name');
sections.classDependencies[name] = deps;
return true;
}
base = this.core.getBase(base);
count++;
}
return false;
});
// Get the code definitions for each
sections.classes = {};
classNodes
.sort((a, b) => {
var aId = this.core.getPath(a),
bId = this.core.getPath(b);
return inheritanceLvl[aId] > inheritanceLvl[bId];
})
.forEach(node => {
var name = this.core.getAttribute(node, 'name'),
code = this.core.getAttribute(node, 'code');
sections.classes[name] = code;
});
// order classes by dependency
sections.orderedClasses = Object.keys(sections.classes)
.sort((a, b) => {
// if a depends on b, switch them (return 1)
if (sections.classDependencies[a].includes(b)) {
return 1;
}
return -1;
});
};
Export.prototype.addCustomLayers = function(sections) {
var metaDict = this.core.getAllMetaNodes(this.rootNode),
isCustomLayer,
metanodes,
customLayers;
this.logger.info('Creating custom layer file...');
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
isCustomLayer = this.getTypeDictFor('CustomLayer', metanodes);
customLayers = metanodes.filter(node =>
this.core.getMixinPaths(node).some(id => isCustomLayer[id]));
// Get the code definitions for each
sections.layers = {};
customLayers
.map(layer => [
this.core.getAttribute(layer, 'name'),
this.core.getAttribute(layer, 'code')
])
.forEach(pair => sections.layers[pair[0]] = pair[1]);
};
Export.prototype.getTypeDictFor = function (name, metanodes) {
var isType = {};
// Get all the custom layers
for (var i = metanodes.length; i--;) {
if (this.core.getAttribute(metanodes[i], 'name') === name) {
isType[this.core.getPath(metanodes[i])] = true;
}
}
return isType;
};
var toAttrString = function(attr) {
if (/^\d+\.?\d*$/.test(attr) || /^(true|false|nil)$/.test(attr)) {
return attr;
}
return `"${attr}"`;
};
Export.prototype.getOpInvocation = function(op) {
var lines = [],
attrs,
refInits = [],
args;
attrs = '{' +
Object.keys(op.attributes).map(key => `${key}=${toAttrString(op.attributes[key])}`)
.join(',') +
'}';
lines.push(`local ${op.name}_attrs = ${attrs}`);
args = (op.inputValues || [])
.map(val => val instanceof Array ? `${val[0]}_results.${val[1]}` : val);
args.unshift(op.name + '_attrs');
// Create the ref init functions
refInits = op.refs.map((code, index) => {
return [
`local function create_${op.refNames[index]}()`,
indent(code),
'end'
].join('\n');
});
lines = lines.concat(refInits);
args = args.concat(op.refNames.map(name => `create_${name}()`));
args = args.join(', ');
lines.push(`local ${op.name}_results = ${op.basename}(${args})`);
return lines.join('\n');
};
Export.prototype.getOutputName = function(node) {
var basename = this.core.getAttribute(node, 'saveName');
return getUniqueName(basename, this._outputNames, true);
};
Export.prototype.getVariableName = function (/*node*/) {
var c = Object.keys(this.isInputOp).length;
if (c !== 1) {
return `input${c}`;
}
return 'input';
};
Export.prototype.registerNode = function (node) {
if (this.isMetaTypeOf(node, this.META.Operation)) {
return this.registerOperation(node);
} else if (this.isMetaTypeOf(node, this.META.Transporter)) {
return this.registerTransporter(node);
}
};
var getUniqueName = function(namebase, takenDict, unsafeAllowed) {
var name,
i = 2,
isUnsafe = function(name) {
return !unsafeAllowed && RESERVED.test(name);
};
if (!unsafeAllowed) {
namebase = namebase.replace(/[^A-Za-z\d]/g, '_');
}
name = namebase;
// Get a unique operation name
while (takenDict[name] || isUnsafe(name)) {
name = namebase + '_' + i;
i++;
}
takenDict[name] = true;
return name;
};
Export.prototype.registerOperation = function (node) {
var name = this.core.getAttribute(node, 'name'),
id = this.core.getPath(node),
base = this.core.getBase(node),
baseId = this.core.getPath(base),
baseName = this.core.getAttribute(base, 'name');
// If it is an Input/Output operation, assign it a variable name
if (baseName === CONSTANTS.OP.INPUT) {
this.isInputOp[id] = node;
name = this.getVariableName(node);
} else if (baseName === CONSTANTS.OP.OUTPUT) {
this.isOutputOp[id] = node;
name = this.getOutputName(node);
} else {
// get a unique operation instance name
name = getUniqueName(name, this._instanceNames);
}
this._nameFor[id] = name;
// get a unique operation base name
if (!this._fnNameFor[baseId]) {
name = this.core.getAttribute(base, 'name');
name = getUniqueName(name, this._opBaseNames);
this._fnNameFor[baseId] = name;
}
// For operations, register all output data node names by path
return this.core.loadChildren(node)
.then(cntrs => {
var outputs = cntrs.find(n => this.isMetaTypeOf(n, this.META.Outputs)),
inputs = cntrs.find(n => this.isMetaTypeOf(n, this.META.Inputs));
return Q.all([inputs, outputs].map(cntr => this.core.loadChildren(cntr)));
})
.then(data => {
var inputs = data[0],
outputs = data[1];
// Get the input type
outputs.forEach(output => {
var dataId = this.core.getPath(output);
name = this.core.getAttribute(output, 'name');
this._dataNameFor[dataId] = name;
this._portCache[dataId] = output;
});
inputs.forEach(input =>
this._portCache[this.core.getPath(input)] = input
);
// Extra recording for input/output nodes in the pipeline
if (this.isInputOp[id]) {
this.inputNode[id] = outputs[0];
} else if (this.isOutputOp[id]) {
this.outputDataToOpId[this.core.getPath(inputs[0])] = id;
}
});
};
Export.prototype.registerTransporter = function (node) {
var outputData = this.core.getPointerPath(node, 'src'),
inputData = this.core.getPointerPath(node, 'dst'),
srcOpId = this.getOpIdFor(outputData),
dstOpId = this.getOpIdFor(inputData);
this._srcIdFor[inputData] = outputData;
// Store the next operation ids for the op id
if (!this._nextOps[srcOpId]) {
this._nextOps[srcOpId] = [];
}
this._nextOps[srcOpId].push(dstOpId);
// Increment the incoming counts for each dst op
this._incomingCnts[dstOpId] = this._incomingCnts[dstOpId] || 0;
this._incomingCnts[dstOpId]++;
};
Export.prototype.getOpIdFor = function (dataId) {
var ids = dataId.split('/'),
depth = ids.length;
ids.splice(this.activeNodeDepth - depth);
return ids.join('/');
};
// For each operation...
// - unpack the inputs from prev ops
// - add the attributes table (if used)
// - check for '\<attributes\>' in code
// - add the references
// - generate the code
// - replace the `return <thing>` w/ `<ref-name> = <thing>`
Export.prototype.createOperation = function (node) {
var id = this.core.getPath(node),
baseId = this.core.getPath(this.core.getBase(node)),
attrNames = this.core.getValidAttributeNames(node),
operation = {};
operation.name = this._nameFor[id];
operation.basename = this._fnNameFor[baseId];
operation.baseId = baseId;
operation.id = id;
operation.code = this.core.getAttribute(node, 'code');
operation.attributes = {};
for (var i = attrNames.length; i--;) {
if (!SKIP_ATTRS[attrNames[i]]) {
operation.attributes[attrNames[i]] = this.core.getAttribute(node, attrNames[i]);
}
}
// Get all the input names (and sources)
return this.core.loadChildren(node)
.then(containers => {
var inputs;
inputs = containers
.find(cntr => this.isMetaTypeOf(cntr, this.META.Inputs));
this.logger.info(`${operation.name} has ${containers.length} cntrs`);
return this.core.loadChildren(inputs);
})
.then(data => {
// Get the input names and sources
var inputNames = data.map(d => this.core.getAttribute(d, 'name')),
ids = data.map(d => this.core.getPath(d)),
srcIds = ids.map(id => this._srcIdFor[id]);
operation.inputNames = inputNames || [];
operation.inputValues = inputNames.map((name, i) => {
var id = srcIds[i],
srcDataName = this._dataNameFor[id],
srcOpId = this.getOpIdFor(id),
srcOpName = this._nameFor[srcOpId];
if (this.isInputOp[srcOpId]) {
return this._nameFor[srcOpId];
} else {
return [srcOpName, srcDataName];
}
});
return operation;
})
.then(operation => {
// For each reference, run the plugin and retrieve the generated code
operation.refNames = [];
if (!this.isInputOp[operation.id]) {
operation.refNames = this.core.getPointerNames(node)
.filter(name => name !== 'base');
}
var refs = operation.refNames
.map(ref => [ref, this.core.getPointerPath(node, ref)]);
return Q.all(
refs.map(pair => this.genPtrSnippet.apply(this, pair))
);
})
.then(codeFiles => {
operation.refs = codeFiles;
return operation;
});
};
Export.prototype.genPtrSnippet = function (ptrName, pId) {
return this.getPtrCodeHash(pId)
.then(hash => this.blobClient.getObjectAsString(hash));
};
Export.prototype.createHeader = function (title, length) {
var len;
title = ` ${title} `;
length = length || HEADER_LENGTH;
len = Math.max(
Math.floor((length - title.length)/2),
2
);
return [
'',
title,
''
].join(new Array(len+1).join('-')) + '\n';
};
Export.prototype.genOperationCode = function (operation) {
var header = this.createHeader(`"${operation.name}" Operation`),
codeParts = [],
body = [];
codeParts.push(header);
codeParts.push(`local ${operation.name}_results`);
codeParts.push('do');
if (operation.inputs.length) {
body.push(operation.inputs.join('\n'));
}
if (operation.refs.length) {
body.push(operation.refs.join('\n'));
}
body.push(operation.code);
codeParts.push(indent(body.join('\n')));
codeParts.push('end');
codeParts.push('');
operation.code = codeParts.join('\n');
return operation;
};
_.extend(Export.prototype, PtrCodeGen.prototype);
// Extra utilities for export types
Export.prototype.INIT_CLASSES_FN = '__init_classes';
Export.prototype.INIT_LAYERS_FN = '__init_layers';
Export.prototype.getAllDefinitions = function (sections) {
var code = [],
classes,
initClassFn,
initLayerFn;
classes = sections.orderedClasses
// Create fns from the classes
.map(name => this.indent(sections.classes[name])).join('\n');
initClassFn = [
`local function ${this.INIT_CLASSES_FN}()`,
this.indent(classes),
'end'
].join('\n');
code = code.concat(initClassFn);
// wrap the layers in a function
initLayerFn = [
`local function ${this.INIT_LAYERS_FN}()`,
this.indent(_.values(sections.layers).join('\n\n')),
'end'
].join('\n');
code = code.concat(initLayerFn);
// Add operation fn definitions
code = code.concat(_.values(sections.operations));
code = code.concat(_.values(sections.pipelines));
// define deserializers, serializers
code.push(sections.deserializers);
code.push(sections.serializers);
code.push(this.getDeepforgeObject());
code.push('deepforge.initialize()');
code.push(sections.serializeOutputsDef);
return code.join('\n\n');
};
Export.prototype.getDeepforgeObject = function (content) {
content = content || {};
content.initCode = content.initCode || `${this.INIT_CLASSES_FN}()\n${' '}${this.INIT_LAYERS_FN}()`;
return DeepForgeTpl(content);
};
return Export;
});
+47
Ver Arquivo
@@ -0,0 +1,47 @@
-- Instantiate the deepforge object
deepforge = {}
function deepforge.initialize()
require 'nn'
require 'rnn'
<%= initCode %>
end
-- Graph support
torch.class('deepforge.Graph')
function deepforge.Graph:__init(name)
-- nop
end
torch.class('deepforge._Line')
function deepforge._Line:__init(graphId, name, opts)
-- nop
end
function deepforge._Line:add(x, y)
-- nop
end
function deepforge.Graph:line(name, opts)
return deepforge._Line(self.id, name, opts)
end
-- Image support
function deepforge.image(name, tensor)
-- nop
end
torch.class('deepforge.Image')
function deepforge.Image:__init(name, tensor)
-- nop
end
function deepforge.Image:update(tensor)
-- nop
end
function deepforge.Image:title(name)
-- nop
end
+13
Ver Arquivo
@@ -0,0 +1,13 @@
/* globals define*/
// The supported export formats and metadata
define([
'./formats/cli/cli'
], function(
Format0
) {
return {
'Basic CLI': Format0
};
});
+21
Ver Arquivo
@@ -0,0 +1,21 @@
<% // Add default format
formats.unshift({ name: 'cli', main: 'cli.js', displayName: 'Basic CLI' })
%>
/* globals define*/
// The supported export formats and metadata
define([
<%= formats.map(function(format) {
return ' \'./formats/' + format.name + '/' +
path.basename(format.main.replace(/\.js$/, '')) + '\''
})
.join(',\n') %>
], function(
<%= formats.map(function(f, index) { return ' Format' + index; }).join(',\n') %>
) {
return {
<%= formats.map(function(f, index) {
return ' \'' + f.displayName + '\': Format' + index;
}).join(',\n') %>
};
});
+103
Ver Arquivo
@@ -0,0 +1,103 @@
/*globals define*/
// Simple torch cli for the given pipeline
define([
], function(
) {
var TOBOOLEAN =
`local function toboolean(str)
if str == 'true' then
return true
elseif str == 'false' then
return false
end
end`;
var CliExporter = {};
CliExporter.deserializersFromString = function(sections) {
var hasBool = false;
// Add serializers given cli string input
Object.keys(this.isInputOp).forEach(id => {
var node = this.inputNode[id],
base = this.core.getBase(node),
type = this.core.getAttribute(base, 'name'),
name = this._nameFor[id];
if (type === 'boolean') {
hasBool = true;
sections.deserializerFor[name] = 'toboolean';
} else if (type === 'number') {
sections.deserializerFor[name] = 'tonumber';
} else if (type === 'string') {
sections.deserializerFor[name] = 'tostring';
}
});
if (hasBool) {
sections.deserializers += '\n' + TOBOOLEAN;
}
return sections;
};
CliExporter.main = function (sections, staticInputs) {
var code = [];
// Update deserializers for cli input
this.deserializersFromString(sections);
// Define all the operations, pipelines, etc
// 'getAllDefinitions' is provided as part of the public api
code.push(this.getAllDefinitions(sections));
// Command line specific stuff
var files = {},
main,
args,
staticNames = staticInputs.map(input => input.name),
varDefs,
index = 1;
// Create some names for the inputs
args = sections.pipelineInputNames.map(name => `${sections.deserializerFor[name]}(${name})`);
main = `local outputs = ${sections.pipelineName}(${args.join(', ')})`;
// Grab the args from the cli
code.push(sections.pipelineInputNames.map((name, index) => {
return `local ${name} = arg[${index + 1}]`;
}).join('\n'));
// Add the hash for each of the static inputs and reference them
staticInputs.forEach(input => {
files[`res/${input.name}`] = input.hash;
});
varDefs = staticNames.map(name => {
return `local ${name} = './res/${name}'`;
});
// Grab the remaining args from the cli
varDefs = varDefs.concat(sections.pipelineInputNames.map(name => {
if (!staticNames.includes(name)) {
return `local ${name} = arg[${index++}]`;
}
}));
// Add the main fn
code.push(varDefs.join('\n'));
code.push(main);
// Save outputs to disk
code.push(sections.serializeOutputs);
files['init.lua'] = code.join('\n\n');
// if no extra assets, just return the main file
return staticInputs.length ? files : files['init.lua'];
};
return CliExporter;
});
+14
Ver Arquivo
@@ -0,0 +1,14 @@
{
"id": "Export",
"name": "Export",
"version": "1.0.0",
"description": "",
"icon": {
"class": "glyphicon glyphicon-cog",
"src": ""
},
"disableServerSideExecution": false,
"disableBrowserSideExecution": false,
"writeAccessRequired": false,
"configStructure": []
}
+7
Ver Arquivo
@@ -0,0 +1,7 @@
local function toboolean(str)
if str == 'true' then
return true
elseif str == 'false' then
return false
end
end
@@ -6,13 +6,15 @@ define([
'SimpleNodes/Constants',
'deepforge/layer-args',
'deepforge/utils',
'deepforge/Constants',
'underscore',
'text!./metadata.json'
], function (
PluginBase,
Constants,
SimpleNodeConstants,
createLayerDict,
utils,
Constants,
_,
metadata
) {
@@ -47,6 +49,11 @@ define([
this.LayerDict = createLayerDict(this.core, this.META);
this.uniqueId = 2;
this.varnames = {net: true};
this.definitions = [
'require \'nn\'',
'require \'rnn\''
];
return PluginBase.prototype.main.apply(this, arguments);
};
@@ -65,15 +72,10 @@ define([
};
GenerateArchitecture.prototype.createOutputFiles = function (tree) {
var layers = tree[Constants.CHILDREN],
var layers = tree[SimpleNodeConstants.CHILDREN],
result = {},
code = '';
this.definitions = [
'require \'nn\'',
'require \'rnn\''
];
// Add an index to each layer
layers.forEach((l, index) => l[INDEX] = index);
@@ -125,12 +127,61 @@ define([
};
GenerateArchitecture.prototype.createLayer = function (layer) {
var args = this.createArgString(layer);
return `nn.${layer.name}${args}`;
var args = this.createArgString(layer),
def = `nn.${layer.name}${args}`,
type = layer.base.base.name,
memberIds,
node,
name,
children,
id;
// Check if it is a container and has the 'addLayers' set
// If so, it should sort them by their registry 'index' and add
// each nested architecture's code to the given container
if (type === 'Container') {
// Get the members of the 'addLayers' set
memberIds = {};
id = layer[SimpleNodeConstants.NODE_PATH];
node = this._nodeCache[id];
this.core.getMemberPaths(node, Constants.CONTAINED_LAYER_SET)
.forEach(id => memberIds[id] = true);
// Get the (sorted) children
children = layer[SimpleNodeConstants.CHILDREN]
.map(child => { // get (child, index) tuples
var index = null;
id = child[SimpleNodeConstants.NODE_PATH];
if (memberIds[id]) {
index = this.core.getMemberRegistry(node,
Constants.CONTAINED_LAYER_SET, id, Constants.CONTAINED_LAYER_INDEX);
}
return [child, index];
})
.filter(pair => pair[1] !== null) // remove non-members
.sort((a, b) => a[1] < b[1] ? -1 : 1) // sort by 'index'
.map(pair => pair[0]);
var addedLayerDefs = '',
firstLayer;
for (var i = 0; i < children.length; i++) {
id = children[i][SimpleNodeConstants.NODE_PATH];
// Get the children!
firstLayer = children[i][SimpleNodeConstants.CHILDREN][0];
name = this.getVarName(utils.abbr(layer.name + '_' + i));
addedLayerDefs += this.createSequential(firstLayer, name).code;
def += `:add(${name})`;
}
this.hoist(addedLayerDefs);
}
return def;
};
GenerateArchitecture.prototype.createSequential = function (layer, name) {
var next = layer[Constants.NEXT][0],
var next = layer[SimpleNodeConstants.NEXT][0],
args,
snippet,
snippets,
@@ -142,7 +193,7 @@ define([
while (layer) {
// if there is only one successor, just add the given layer
if (layer[Constants.PREV].length > 1) { // sequential layers are over
if (layer[SimpleNodeConstants.PREV].length > 1) { // sequential layers are over
next = layer; // the given layer will be added by the caller
break;
} else { // add the given layer
@@ -151,11 +202,11 @@ define([
}
while (layer && layer[Constants.NEXT].length > 1) { // concat/parallel
while (layer && layer[SimpleNodeConstants.NEXT].length > 1) { // concat/parallel
// if there is a fork, recurse and add a concat layer
this.logger.debug(`detected fork of size ${layer[Constants.NEXT].length}`);
snippets = layer[Constants.NEXT].map(nlayer =>
this.logger.debug(`detected fork of size ${layer[SimpleNodeConstants.NEXT].length}`);
snippets = layer[SimpleNodeConstants.NEXT].map(nlayer =>
this.createSequential(nlayer, this.getVarName('net')));
code += '\n' + snippets.map(snippet => snippet.code).join('\n');
@@ -183,7 +234,7 @@ define([
`concat_${layer[INDEX]}:add(${snippet.name})`)
.join('\n') + `\n\n${name}:add(concat_${layer[INDEX]})`;
next = layer[Constants.NEXT][0];
next = layer[SimpleNodeConstants.NEXT][0];
} else {
next = null; // no next layers
}
@@ -203,7 +254,7 @@ define([
}
layer = next;
next = layer && layer[Constants.NEXT][0];
next = layer && layer[SimpleNodeConstants.NEXT][0];
}
return {
@@ -218,14 +269,14 @@ define([
var content = layer[arg];
if (typeof content === 'object') { // layer as arg
if (content[Constants.CHILDREN].length) {
if (content[SimpleNodeConstants.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);
layers = this.genRawArchCode(layer[arg][SimpleNodeConstants.CHILDREN], name);
} catch (e) {
this.logger.error(`Layer arg creation failed: ${e}`);
return null;
@@ -244,7 +295,7 @@ define([
GenerateArchitecture.prototype.createArgString = function (layer) {
var setters = this.LayerDict[layer.name].setters,
setterNames = Object.keys(this.LayerDict[layer.name].setters),
base = layer[Constants.BASE],
base = layer[SimpleNodeConstants.BASE],
desc,
fn,
layerCode,
@@ -1,344 +0,0 @@
/*globals define, _*/
/*jshint node:true, browser:true*/
/**
* Generated by PluginGenerator 1.7.0 from webgme on Sat Jun 04 2016 18:01:54 GMT-0500 (CDT).
* A plugin that inherits from the PluginBase. To see source code documentation about available
* properties and methods visit %host%/docs/source/PluginBase.html.
*/
define([
'text!./metadata.json',
'plugin/PluginBase',
'deepforge/plugin/PtrCodeGen',
'q'
], function (
pluginMetadata,
PluginBase,
PtrCodeGen,
Q
) {
'use strict';
pluginMetadata = JSON.parse(pluginMetadata);
var HEADER_LENGTH = 60;
/**
* Initializes a new instance of GenerateExecFile.
* @class
* @augments {PluginBase}
* @classdesc This class represents the plugin GenerateExecFile.
* @constructor
*/
var GenerateExecFile = function () {
// Call base class' constructor.
PluginBase.call(this);
this.pluginMetadata = pluginMetadata;
this._srcIdFor = {}; // input path -> output data node path
this._nameFor = {}; // input path -> opname
this._dataNameFor = {};
this._opNames = {};
// topo sort stuff
this._nextOps = {};
this._incomingCnts = {};
this._operations = {};
this.activeNodeId = null;
this.activeNodeDepth = null;
};
/**
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
* This is also available at the instance at this.pluginMetadata.
* @type {object}
*/
GenerateExecFile.metadata = pluginMetadata;
// Prototypical inheritance from PluginBase.
GenerateExecFile.prototype = Object.create(PluginBase.prototype);
GenerateExecFile.prototype.constructor = GenerateExecFile;
/**
* Main function for the plugin to execute. This will perform the execution.
* Notes:
* - Always log with the provided logger.[error,warning,info,debug].
* - Do NOT put any user interaction logic UI, etc. inside this method.
* - callback always has to be called even if error happened.
*
* @param {function(string, plugin.PluginResult)} callback - the result callback
*/
GenerateExecFile.prototype.main = function (callback) {
// Get all the children and call generate exec file
this.activeNodeId = this.core.getPath(this.activeNode);
this.activeNodeDepth = this.activeNodeId.split('/').length + 1;
if (this.isMetaTypeOf(this.activeNode, this.META.Execution)) {
this.activeNodeDepth++;
}
return this.core.loadChildren(this.activeNode)
.then(nodes => this.createExecFile(nodes))
.then(code => this.blobClient.putFile('init.lua', code))
.then(hash => {
this.result.addArtifact(hash);
this.result.setSuccess(true);
callback(null, this.result);
})
.fail(err => callback(err));
};
GenerateExecFile.prototype.createExecFile = function (children) {
// Convert opNodes' jobs to the nested operations
var opNodes,
nodes;
return this.unpackJobs(children)
.then(_nodes => {
nodes = _nodes;
opNodes = nodes
.filter(node => this.isMetaTypeOf(node, this.META.Operation));
return Q.all(nodes.map(node => this.registerNameAndData(node)));
})
.then(() => Q.all(opNodes.map(node => this.createOperation(node))))
.then(operations => {
var nextIds = opNodes.map(n => this.core.getPath(n))
.filter(id => !this._incomingCnts[id]);
operations.forEach(op => this._operations[op.id] = op);
// Toposort and concat!
return this.combineOpNodes(nextIds);
})
.fail(err => this.logger.error(err));
};
GenerateExecFile.prototype.unpackJobs = function (nodes) {
return Q.all(
nodes.map(node => {
if (!this.isMetaTypeOf(node, this.META.Job)) {
return node;
}
return this.core.loadChildren(node)
.then(children =>
children.find(c => this.isMetaTypeOf(c, this.META.Operation))
);
})
);
};
GenerateExecFile.prototype.combineOpNodes = function (opIds) {
var nextIds = [],
dstIds,
code,
id;
// Combine all nodes with incoming cnts of 0
code = opIds.map(id => this._operations[id].code).join('\n');
// Decrement all next ops
dstIds = opIds.map(id => this._nextOps[id])
.reduce((l1, l2) => l1.concat(l2), []);
for (var i = dstIds.length; i--;) {
id = dstIds[i];
if (--this._incomingCnts[id] === 0) {
nextIds.push(id);
}
}
// append
return [
code,
nextIds.length ? this.combineOpNodes(nextIds) : ''
].join('\n');
};
GenerateExecFile.prototype.registerNameAndData = function (node) {
var name = this.core.getAttribute(node, 'name'),
id = this.core.getPath(node),
basename = name,
i = 2;
if (this.isMetaTypeOf(node, this.META.Operation)) {
// Get a unique operation name
while (this._opNames[name]) {
name = basename + '_' + i;
i++;
}
// register the unique name
this._opNames[name] = true;
this._nameFor[id] = name;
// For operations, register all output data node names by path
return this.core.loadChildren(node)
.then(cntrs => {
var cntr = cntrs.find(n => this.isMetaTypeOf(n, this.META.Outputs));
return this.core.loadChildren(cntr);
})
.then(outputs => {
outputs.forEach(output => {
var dataId = this.core.getPath(output);
name = this.core.getAttribute(output, 'name');
this._dataNameFor[dataId] = name;
});
});
// For each input data node, register the associated output id
} else if (this.isMetaTypeOf(node, this.META.Transporter)) {
var outputData = this.core.getPointerPath(node, 'src'),
inputData = this.core.getPointerPath(node, 'dst'),
srcOpId = this.getOpIdFor(outputData),
dstOpId = this.getOpIdFor(inputData);
this._srcIdFor[inputData] = outputData;
// Store the next operation ids for the op id
if (!this._nextOps[srcOpId]) {
this._nextOps[srcOpId] = [];
}
this._nextOps[srcOpId].push(dstOpId);
// Increment the incoming counts for each dst op
this._incomingCnts[dstOpId] = this._incomingCnts[dstOpId] || 0;
this._incomingCnts[dstOpId]++;
}
};
GenerateExecFile.prototype.getOpIdFor = function (dataId) {
var ids = dataId.split('/'),
depth = ids.length;
ids.splice(this.activeNodeDepth - depth);
return ids.join('/');
};
// For each operation...
// - unpack the inputs from prev ops
// - add the attributes table (if used)
// - check for '\<attributes\>' in code
// - add the references
// - generate the code
// - replace the `return <thing>` w/ `<ref-name> = <thing>`
GenerateExecFile.prototype.createOperation = function (node) {
var id = this.core.getPath(node),
operation = {};
operation.name = this._nameFor[id];
operation.id = id;
operation.code = this.core.getAttribute(node, 'code');
// Update the 'code' attribute
// Change the last return statement to assign the results to a table
operation.code = this.assignResultToVar(operation.code,
`${operation.name}_results`);
// Get all the input names (and sources)
return this.core.loadChildren(node)
.then(containers => {
var inputs;
inputs = containers
.find(cntr => this.isMetaTypeOf(cntr, this.META.Inputs));
this.logger.info(`${name} has ${containers.length} cntrs`);
return this.core.loadChildren(inputs);
})
.then(data => {
// Get the input names and sources
var inputNames = data.map(d => this.core.getAttribute(d, 'name')),
ids = data.map(d => this.core.getPath(d)),
srcIds = ids.map(id => this._srcIdFor[id]);
operation.inputs = inputNames.map((name, i) => {
var id = srcIds[i],
srcDataName = this._dataNameFor[id],
srcOpId = this.getOpIdFor(id),
srcOpName = this._nameFor[srcOpId];
return `local ${name} = ${srcOpName}_results.${srcDataName}`;
});
return operation;
})
.then(operation => {
// For each reference, run the plugin and retrieve the generated code
operation.refNames = this.core.getPointerNames(node)
.filter(name => name !== 'base');
var refs = operation.refNames
.map(ref => [ref, this.core.getPointerPath(node, ref)]);
return Q.all(
refs.map(pair => this.genPtrSnippet.apply(this, pair))
);
})
.then(codeFiles => {
operation.refs = codeFiles;
this.genOperationCode(operation);
return operation;
});
};
GenerateExecFile.prototype.genPtrSnippet = function (ptrName, pId) {
return this.getPtrCodeHash(pId)
.then(hash => this.blobClient.getObjectAsString(hash))
.then(code => this.createHeader(`creating ${ptrName}`, 40) + '\n' +
this.assignResultToVar(code, ptrName));
};
GenerateExecFile.prototype.createHeader = function (title, length) {
var len;
title = ` ${title} `;
length = length || HEADER_LENGTH;
len = Math.max(
Math.floor((length - title.length)/2),
2
);
return [
'',
title,
''
].join(new Array(len+1).join('-')) + '\n';
};
GenerateExecFile.prototype.genOperationCode = function (operation) {
var header = this.createHeader(`"${operation.name}" Operation`),
codeParts = [];
codeParts.push(header);
if (operation.inputs.length) {
codeParts.push(operation.inputs.join('\n'));
}
if (operation.refs.length) {
codeParts.push(operation.refs.join('\n'));
}
codeParts.push(operation.code);
codeParts.push('');
operation.code = codeParts.join('\n');
return operation;
};
GenerateExecFile.prototype.assignResultToVar = function (code, name) {
var i = code.lastIndexOf('return');
return code.substring(0, i) +
code.substring(i)
.replace('return', `local ${name} = `);
};
_.extend(GenerateExecFile.prototype, PtrCodeGen.prototype);
return GenerateExecFile;
});
+568
Ver Arquivo
@@ -0,0 +1,568 @@
/*globals define*/
/*jshint node:true, browser:true*/
define([
'./templates/index',
'q',
'underscore',
'deepforge/Constants',
'deepforge/plugin/Operation',
'deepforge/plugin/PtrCodeGen',
'text!./metadata.json',
'plugin/PluginBase'
], function (
Templates,
Q,
_,
CONSTANTS,
OperationHelpers,
PtrCodeGen,
pluginMetadata,
PluginBase
) {
'use strict';
pluginMetadata = JSON.parse(pluginMetadata);
var OUTPUT_INTERVAL = 1500,
STDOUT_FILE = 'job_stdout.txt',
SKIP_ATTRIBUTES = [
'code',
'stdout',
'execFiles',
'jobId',
'secret',
CONSTANTS.LINE_OFFSET,
CONSTANTS.DISPLAY_COLOR
];
/**
* Initializes a new instance of GenerateJob.
* @class
* @augments {PluginBase}
* @classdesc This class represents the plugin GenerateJob.
* @constructor
*/
var GenerateJob = 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}
*/
GenerateJob.metadata = pluginMetadata;
// Prototypical inheritance from PluginBase.
GenerateJob.prototype = Object.create(PluginBase.prototype);
GenerateJob.prototype.constructor = GenerateJob;
/**
* 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
*/
GenerateJob.prototype.main = function (callback) {
var files,
artifactName,
artifact,
data = {},
inputs,
name,
opId;
name = this.getAttribute(this.activeNode, 'name');
opId = this.core.getPath(this.activeNode);
return this.createOperationFiles(this.activeNode)
.then(results => {
this.logger.info('Created operation files!');
files = results;
artifactName = `${name}_${opId.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)
.fail(() => {
throw Error(`BLOB_FETCH_FAILED:${input}`);
});
})
);
})
.then(mds => {
// Record the large files
var inputData = {},
runsh = '# Bash script to download data files and run job\n' +
'if [ -z "$DEEPFORGE_URL" ]; then\n echo "Please set DEEPFORGE_URL and' +
' re-run:"\n echo "" \n echo " DEEPFORGE_URL=http://my.' +
'deepforge.server.com:8080 bash run.sh"\n echo ""\n exit 1\nfi\n';
mds.forEach((metadata, i) => {
// add the hashes for each input
var input = inputs[i],
hash = files.inputAssets[input],
dataPath = 'inputs/' + input + '/data',
url = this.blobClient.getRelativeDownloadURL(hash);
inputData[dataPath] = {
req: hash,
cache: metadata.content
};
// Add to the run.sh file
runsh += `wget $DEEPFORGE_URL${url} -O ${dataPath}\n`;
});
delete files.inputAssets;
files['input-data.json'] = JSON.stringify(inputData, null, 2);
runsh += 'th init.lua';
files['run.sh'] = runsh;
// Add pointer assets
Object.keys(files.ptrAssets)
.forEach(path => data[path] = files.ptrAssets[path]);
// Add the executor config
return this.getOutputs(this.activeNode);
})
.then(outputArgs => {
var config,
outputs,
fileList,
ptrFiles = Object.keys(files.ptrAssets),
file;
delete files.ptrAssets;
fileList = Object.keys(files).concat(ptrFiles);
outputs = outputArgs.map(pair => pair[0])
.map(name => {
return {
name: name,
resultPatterns: [`outputs/${name}`]
};
});
outputs.push(
{
name: 'stdout',
resultPatterns: [STDOUT_FILE]
},
{
name: name + '-all-files',
resultPatterns: fileList
}
);
config = {
cmd: 'node',
args: ['start.js'],
outputInterval: OUTPUT_INTERVAL,
resultArtifacts: outputs
};
files['executor_config.json'] = JSON.stringify(config, null, 4);
// 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.result.setSuccess(true);
this.result.addArtifact(hash);
callback(null, this.result);
})
.fail(err => {
this.result.setSuccess(false);
callback(err, this.result);
});
};
GenerateJob.prototype.createOperationFiles = function (node) {
var files = {};
// For each operation, generate the output files:
// inputs/<arg-name>/init.lua (respective data deserializer)
// pointers/<name>/init.lua (result of running the main plugin on pointer target - may need a rename)
// outputs/<name>/ (make dirs for each of the outputs)
// outputs/init.lua (serializers for data outputs)
//
// attributes.lua (returns lua table of operation attributes)
// init.lua (main file -> calls main and serializes outputs)
// <name>.lua (entry point -> calls main operation code)
// add the given files
this.logger.info('About to create dist execution files');
files['start.js'] = _.template(Templates.START)(CONSTANTS);
return this.createEntryFile(node, files)
.then(() => this.createClasses(node, files))
.then(() => this.createCustomLayers(node, files))
.then(() => this.createInputs(node, files))
.then(() => this.createOutputs(node, files))
.then(() => this.createMainFile(node, files))
.then(() => {
this.createAttributeFile(node, files);
return Q.ninvoke(this, 'createPointers', node, files);
})
.fail(err => {
this.logger.error(err);
throw err;
});
};
GenerateJob.prototype.createEntryFile = function (node, files) {
this.logger.info('Creating entry files...');
return this.getOutputs(node)
.then(outputs => {
var name = this.getAttribute(node, 'name'),
content = {};
// inputs and outputs
content.name = name;
content.outputs = outputs;
files['init.lua'] = _.template(Templates.ENTRY)(content);
// Create the deepforge file
files['deepforge.lua'] = _.template(Templates.DEEPFORGE)(CONSTANTS);
});
};
GenerateJob.prototype.createClasses = function (node, files) {
var metaDict = this.core.getAllMetaNodes(this.rootNode),
isClass,
metanodes,
classNodes,
inheritanceLvl = {},
code;
this.logger.info('Creating custom layer file...');
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
isClass = this.getTypeDictFor('Complex', metanodes);
classNodes = metanodes.filter(node => {
var base = this.core.getBase(node),
baseId,
count = 1;
// Count the sets back to a class node
while (base) {
baseId = this.core.getPath(base);
if (isClass[baseId]) {
inheritanceLvl[this.core.getPath(node)] = count;
return true;
}
base = this.core.getBase(base);
count++;
}
return false;
});
// Get the code definitions for each
// Sort by levels of inheritance...
code = classNodes.sort((a, b) => {
var aId = this.core.getPath(a),
bId = this.core.getPath(b);
return inheritanceLvl[aId] > inheritanceLvl[bId];
}).map(node =>
`require './${this.getAttribute(node, 'name')}.lua'`
).join('\n');
// Create the class files
classNodes.forEach(node => {
var name = this.getAttribute(node, 'name');
files[`classes/${name}.lua`] = this.getAttribute(node, 'code');
});
// Create the custom layers file
files['classes/init.lua'] = code;
};
GenerateJob.prototype.getTypeDictFor = function (name, metanodes) {
var isType = {};
// Get all the custom layers
for (var i = metanodes.length; i--;) {
if (this.getAttribute(metanodes[i], 'name') === name) {
isType[this.core.getPath(metanodes[i])] = true;
}
}
return isType;
};
GenerateJob.prototype.createCustomLayers = function (node, files) {
var metaDict = this.core.getAllMetaNodes(this.rootNode),
isCustomLayer,
metanodes,
customLayers,
code;
this.logger.info('Creating custom layer file...');
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
isCustomLayer = this.getTypeDictFor('CustomLayer', metanodes);
customLayers = metanodes.filter(node =>
this.core.getMixinPaths(node).some(id => isCustomLayer[id]));
// Get the code definitions for each
code = 'require \'nn\'\n\n' + customLayers
.map(node => this.getAttribute(node, 'code')).join('\n');
// Create the custom layers file
files['custom-layers.lua'] = code;
};
GenerateJob.prototype.getConnectionContainer = function () {
var container = this.core.getParent(this.activeNode);
if (this.isMetaTypeOf(container, this.META.Job)) {
container = this.core.getParent(container);
}
return container;
};
GenerateJob.prototype.getInputPortsFor = function (nodeId) {
var container = this.getConnectionContainer();
// Get the connections to this node
return this.core.loadChildren(container)
.then(children => {
return children.filter(child =>
this.core.getPointerPath(child, 'dst') === nodeId)
.map(conn => this.core.getPointerPath(conn, 'src'))[0];
});
};
GenerateJob.prototype.createInputs = function (node, files) {
var tplContents,
inputs;
this.logger.info('Retrieving inputs and deserialize fns...');
return this.getInputs(node)
.then(allInputs => {
// For each input, match the connection with the input name
// [ name, type ] => [ name, type, node ]
//
// For each input,
// - create the deserializer
// - put it in inputs/<name>/init.lua
// - copy the data asset to /inputs/<name>/init.lua
inputs = allInputs
.filter(pair => !!this.getAttribute(pair[2], 'data')); // remove empty inputs
files.inputAssets = {}; // data assets
return Q.all(inputs.map(pair => {
var name = pair[0],
node = pair[2],
nodeId = this.core.getPath(node);
// Get the deserialize function. First, try to get it from
// the source method (this guarantees that the correct
// deserialize method is used despite any auto-upcasting
return this.getInputPortsFor(nodeId)
.then(fromNodeId => this.core.loadByPath(this.rootNode, fromNodeId || nodeId))
.then(fromNode => {
var deserFn,
base,
className;
deserFn = this.getAttribute(fromNode, 'deserialize');
if (this.isMetaTypeOf(node, this.META.Complex)) {
// Complex objects are expected to define their own
// (static) deserialize factory method
base = this.core.getMetaType(node);
className = this.getAttribute(base, 'name');
deserFn = `return ${className}.deserialize(path)`;
}
return {
name: name,
code: deserFn
};
});
}));
})
.then(_tplContents => {
tplContents = _tplContents;
var hashes = inputs.map(pair => {
var hash = this.getAttribute(pair[2], 'data');
files.inputAssets[pair[0]] = hash;
return {
hash: hash,
name: pair[0]
};
});
return Q.all(hashes.map(pair =>
this.blobClient.getMetadata(pair.hash)
.fail(() => {
throw Error(`BLOB_FETCH_FAILED:${pair.name}`);
})));
})
.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;
});
};
GenerateJob.prototype.createOutputs = function (node, files) {
// For each of the output types, grab their serialization functions and
// create the `outputs/init.lua` file
this.logger.info('Creating outputs/init.lua...');
return this.getOutputs(node)
.then(outputs => {
var outputTypes = outputs
// Get the serialize functions for each
.map(tuple => {
var node = tuple[2],
serFn = this.getAttribute(node, 'serialize');
if (this.isMetaTypeOf(node, this.META.Complex)) {
// Complex objects are expected to define their own
// serialize methods
serFn = 'if data ~= nil then data:serialize(path) end';
}
return [tuple[1], serFn];
});
files['outputs/init.lua'] = _.template(Templates.SERIALIZE)({types: outputTypes});
});
};
GenerateJob.prototype.createMainFile = function (node, files) {
this.logger.info('Creating main file...');
return this.getInputs(node)
.then(inputs => {
var name = this.getAttribute(node, 'name'),
code = this.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.getAttribute(pair[2], 'data')]); // remove empty inputs
// Defined variables for each pointers
content.pointers = pointers
.map(id => [id, this.core.getPointerPath(node, id) === null]);
// Add remaining code
content.code = code;
files['main.lua'] = _.template(Templates.MAIN)(content);
// Set the line offset
var lineOffset = this.getLineOffset(files['main.lua'], code);
this.setAttribute(node, CONSTANTS.LINE_OFFSET, lineOffset);
});
};
GenerateJob.prototype.getLineOffset = function (main, snippet) {
var i = main.indexOf(snippet),
lines = main.substring(0, i).match(/\n/g);
return lines ? lines.length : 0;
};
GenerateJob.prototype.createAttributeFile = function (node, files) {
var numOrBool = /^(-?\d+\.?\d*((e|e-)\d+)?|(true|false))$/,
table;
this.logger.info('Creating attributes file...');
table = '{\n\t' + this.core.getAttributeNames(node)
.filter(attr => SKIP_ATTRIBUTES.indexOf(attr) === -1)
.map(name => {
var value = this.getAttribute(node, name);
if (!numOrBool.test(value)) {
value = `"${value}"`;
}
return [`['${name}']`, value];
})
.map(pair => pair.join(' = '))
.join(',\n\t') + '\n}';
files['attributes.lua'] = `-- attributes of ${this.getAttribute(node, 'name')}\nreturn ${table}`;
};
GenerateJob.prototype.createPointers = function (node, files, cb) {
var pointers,
nIds;
this.logger.info('Creating pointers file...');
pointers = this.core.getPointerNames(node)
.filter(name => name !== 'base')
.filter(id => this.core.getPointerPath(node, id) !== null);
nIds = pointers.map(p => this.core.getPointerPath(node, p));
files.ptrAssets = {};
Q.all(
nIds.map(nId => this.getPtrCodeHash(nId))
)
.then(resultHashes => {
var name = this.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.getAttribute(node, 'name')}: ${e.toString()}`);
return cb(e);
});
};
GenerateJob.prototype.getAttribute = function (node, attr) {
return this.core.getAttribute(node, attr);
};
GenerateJob.prototype.setAttribute = function (node, attr, value) {
return this.core.setAttribute(node, attr, value);
};
_.extend(
GenerateJob.prototype,
OperationHelpers.prototype,
PtrCodeGen.prototype
);
return GenerateJob;
});
@@ -1,6 +1,6 @@
{
"id": "GenerateExecFile",
"name": "Generate Execution File",
"id": "GenerateJob",
"name": "GenerateJob",
"version": "0.1.0",
"description": "",
"icon": {
@@ -11,4 +11,4 @@
"disableBrowserSideExecution": false,
"writeAccessRequired": false,
"configStructure": []
}
}
@@ -52,6 +52,14 @@ function Graph:line(name, opts)
return deepforge._Line(self.id, name, opts)
end
function Graph:xlabel(name)
deepforge._cmd('<%= GRAPH_LABEL_AXIS.X %>', self.id, name)
end
function Graph:ylabel(name)
deepforge._cmd('<%= GRAPH_LABEL_AXIS.Y %>', self.id, name)
end
-- Image support
local function saveImage(name, tensor)
require 'image'

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais