Permalink
Browse files

first commit

  • Loading branch information...
1 parent b064ad1 commit 5a0d53fb5d7783fdbfe2ca04ac567f014c8eed08 @neyric committed Feb 9, 2013
Showing with 14,757 additions and 3 deletions.
  1. +8 −0 LICENSE.txt
  2. +38 −3 README.md
  3. +3 −0 activities/deathbycaptcha/config.example.js
  4. +21 −0 activities/deathbycaptcha/deathbycaptcha.js
  5. +8 −0 activities/deathbycaptcha/package.json
  6. +21 −0 activities/deathbycaptcha/test.js
  7. +9 −0 activities/dynamodb/README.md
  8. +6 −0 activities/dynamodb/config.example.js
  9. +51 −0 activities/dynamodb/dynamodb.js
  10. +9 −0 activities/dynamodb/package.json
  11. +20 −0 activities/dynamodb/test.js
  12. +9 −0 activities/ec2/README.md
  13. +97 −0 activities/ec2/TODO.js
  14. +6 −0 activities/ec2/config.example.js
  15. +182 −0 activities/ec2/ec2.js
  16. +9 −0 activities/ec2/package.json
  17. +9 −0 activities/echo/echo.js
  18. +7 −0 activities/echo/package.json
  19. +60 −0 activities/feedautodiscovery/feedautodiscovery.js
  20. +13 −0 activities/feedautodiscovery/package.json
  21. +17 −0 activities/feedautodiscovery/test.js
  22. +85 −0 activities/fetchfeed/fetchfeed.js
  23. +12 −0 activities/fetchfeed/package.json
  24. +17 −0 activities/fetchfeed/test.js
  25. +42 −0 activities/google-analytics/auth.js
  26. +8 −0 activities/google-analytics/config.example.js
  27. +45 −0 activities/google-analytics/google-analytics.js
  28. +10 −0 activities/google-analytics/package.json
  29. +26 −0 activities/google-analytics/test.js
  30. +133 −0 activities/google-keyword-tool/keyword-tool.js
  31. +30 −0 activities/google/google.js
  32. +8 −0 activities/google/package.json
  33. +19 −0 activities/google/test-pagerank.js
  34. +19 −0 activities/google/test-search.js
  35. +19 −0 activities/google/test-suggest.js
  36. +4 −0 activities/hello-activity/config.example.js
  37. +13 −0 activities/hello-activity/hello.js
  38. +5 −0 activities/hello-activity/package.json
  39. +117 −0 activities/http-request/http-request.js
  40. +10 −0 activities/http-request/package.json
  41. +25 −0 activities/http-request/test.js
  42. +8 −0 activities/humantask/README.md
  43. +6 −0 activities/humantask/activityTypeOptions.json
  44. +26 −0 activities/humantask/app/views/defaultTaskView.ejs
  45. +7 −0 activities/humantask/app/views/error.ejs
  46. +3 −0 activities/humantask/app/views/finished.ejs
  47. +26 −0 activities/humantask/app/views/index.ejs
  48. +53 −0 activities/humantask/app/views/layout.ejs
  49. +3 −0 activities/humantask/app/views/unavailable.ejs
  50. +27 −0 activities/humantask/config.example.js
  51. +74 −0 activities/humantask/humantask.js
  52. +15 −0 activities/humantask/package.json
  53. +1,088 −0 activities/humantask/public/lib/bootstrap/css/bootstrap-responsive.css
  54. +9 −0 activities/humantask/public/lib/bootstrap/css/bootstrap-responsive.min.css
  55. +5,893 −0 activities/humantask/public/lib/bootstrap/css/bootstrap.css
  56. +9 −0 activities/humantask/public/lib/bootstrap/css/bootstrap.min.css
  57. BIN activities/humantask/public/lib/bootstrap/img/glyphicons-halflings-white.png
  58. BIN activities/humantask/public/lib/bootstrap/img/glyphicons-halflings.png
  59. +2,025 −0 activities/humantask/public/lib/bootstrap/js/bootstrap.js
  60. +6 −0 activities/humantask/public/lib/bootstrap/js/bootstrap.min.js
  61. BIN activities/humantask/public/lib/img/error.png
  62. BIN activities/humantask/public/lib/img/valid.png
  63. +4 −0 activities/humantask/public/lib/jquery-1.7.2.min.js
  64. +1,188 −0 activities/humantask/public/lib/jquery.validate.js
  65. +51 −0 activities/humantask/public/lib/jquery.validate.min.js
  66. +150 −0 activities/humantask/public/lib/jsonPretty.js
  67. +181 −0 activities/humantask/server.js
  68. +30 −0 activities/humantask/test.js
  69. +60 −0 activities/locationbuilder/locationbuilder.js
  70. +10 −0 activities/locationbuilder/package.json
  71. +17 −0 activities/locationbuilder/test.js
  72. +19 −0 activities/mturk/README.md
  73. +17 −0 activities/mturk/config.example.js
  74. +1 −0 activities/mturk/hits.json
  75. +55 −0 activities/mturk/layout.ejs
  76. +214 −0 activities/mturk/mturk-createhit.js
  77. +16 −0 activities/mturk/package.json
  78. +143 −0 activities/mturk/start.js
  79. +77 −0 activities/mturk/test.js
  80. +10 −0 activities/mysql/config.example.js
  81. +33 −0 activities/mysql/mysql.js
  82. +10 −0 activities/mysql/package.json
  83. +19 −0 activities/mysql/test.js
  84. +16 −0 activities/nodemailer/config.example.js
  85. +25 −0 activities/nodemailer/nodemailer.js
  86. +10 −0 activities/nodemailer/package.json
  87. +26 −0 activities/nodemailer/test.js
  88. +5 −0 activities/notifo/config.example.js
  89. +28 −0 activities/notifo/notifo.js
  90. +10 −0 activities/notifo/package.json
  91. +25 −0 activities/notifo/test.js
  92. +9 −0 activities/s3/README.md
  93. +6 −0 activities/s3/config.example.js
  94. +9 −0 activities/s3/package.json
  95. +87 −0 activities/s3/s3.js
  96. +21 −0 activities/s3/test.js
  97. +7 −0 activities/salesforce-query/config.example.js
  98. +10 −0 activities/salesforce-query/package.json
  99. +24 −0 activities/salesforce-query/salesforce-query.js
  100. +25 −0 activities/salesforce-query/test.js
  101. +4 −0 activities/sendgrid/config.example.js
  102. +10 −0 activities/sendgrid/package.json
  103. +49 −0 activities/sendgrid/sendgrid.js
  104. +26 −0 activities/sendgrid/test.js
  105. +3 −0 activities/sleep/README.md
  106. +5 −0 activities/sleep/package.json
  107. +20 −0 activities/sleep/sleep.js
  108. +10 −0 activities/soap/package.json
  109. +35 −0 activities/soap/soap.js
  110. +44 −0 activities/soap/test.js
  111. +5 −0 activities/sum/package.json
  112. +16 −0 activities/sum/sum.js
  113. +9 −0 activities/swf/README.md
  114. +6 −0 activities/swf/config.example.js
  115. +9 −0 activities/swf/package.json
  116. +69 −0 activities/swf/swf.js
  117. +21 −0 activities/swf/test.js
  118. +5 −0 activities/temboo/config.example.js
  119. +10 −0 activities/temboo/package.json
  120. +94 −0 activities/temboo/temboo.js
  121. +26 −0 activities/temboo/test.js
  122. +9 −0 activities/termextraction/package.json
  123. +40 −0 activities/termextraction/termextraction.js
  124. +20 −0 activities/termextraction/test.js
  125. +16 −0 activities/webdriver/README.md
  126. +12 −0 activities/webdriver/package.json
  127. +49 −0 activities/webdriver/test.js
  128. +49 −0 activities/webdriver/wd-test.js
  129. +78 −0 activities/webdriver/webdriver.js
  130. +10 −0 activities/xmlrpc/package.json
  131. +26 −0 activities/xmlrpc/test.js
  132. +23 −0 activities/xmlrpc/xmlrpc.js
  133. +10 −0 activities/xpathfetchpage/package.json
  134. +18 −0 activities/xpathfetchpage/test.js
  135. +59 −0 activities/xpathfetchpage/xpathfetchpage.js
  136. +10 −0 activities/yql/package.json
  137. +19 −0 activities/yql/test.js
  138. +34 −0 activities/yql/yql.js
  139. +93 −0 workflows/decompose-process/decompose-process.js
  140. BIN workflows/decompose-process/decompose-process.js.png
  141. +7 −0 workflows/decompose-process/graphOptions.json
  142. +5 −0 workflows/decompose-process/package.json
  143. +30 −0 workflows/decompose-process/split-task.html
  144. +69 −0 workflows/decompose-process/task-identification.html
  145. +43 −0 workflows/ec2-demo/ec2-demo.js
  146. BIN workflows/ec2-demo/ec2-demo.js.png
  147. +5 −0 workflows/ec2-demo/package.json
  148. +21 −0 workflows/hello-workflow/hello-workflow.js
  149. BIN workflows/hello-workflow/hello-workflow.js.png
  150. +5 −0 workflows/hello-workflow/package.json
  151. +61 −0 workflows/human-test/human-test.js
  152. +5 −0 workflows/human-test/package.json
  153. +154 −0 workflows/improve-text/improve-text.js
  154. BIN workflows/improve-text/improve-text.js.png
  155. +5 −0 workflows/parallel-test/package.json
  156. +31 −0 workflows/parallel-test/parallel-test.js
  157. BIN workflows/parallel-test/parallel-test.js.png
  158. +5 −0 workflows/simpler/package.json
  159. +45 −0 workflows/simpler/simpler.js
  160. BIN workflows/simpler/simpler.js.png
  161. +5 −0 workflows/subexample/package.json
  162. +34 −0 workflows/subexample/subexample.js
  163. BIN workflows/subexample/subexample.js.png
View
8 LICENSE.txt
@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright (c) <2012-2013> <Eric Abouaf>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
41 README.md
@@ -1,5 +1,40 @@
-# Moved !
+# aws-swf-activities :
-This repository is deprecated.
+Collections of:
+
+ * ready-to-run activities
+ * workflows examples
+
+
+### New Module Ideas
+
+* merge human task and email task into one ( + provide different backends, either files or DynamoDB)
+
+* WebDriver activity (WIP)
+
+* mturk: options for the poller (WIP)
+
+* Document multi-activity package: 'ec2_runInstances' -> require('ec2').runInstances
+
+
+## Usage
+
+
+ # launch 5 activity workers in the activities/ directory
+ # and 2 decider workers in the workflows/ directory
+ swf-toolkit -w 5 -d 2
+
+same as
+
+ cd activities
+ swf-activity
+ cd workflows
+ swf-decider
+
+
+
+
+## License
+
+[MIT License](https://raw.github.com/neyric/aws-swf/master/LICENSE.txt)
-All the activities have been included into the [main aws-swf repository](https://github.com/neyric/aws-swf) !
View
3 activities/deathbycaptcha/config.example.js
@@ -0,0 +1,3 @@
+
+exports.username = "neyric";
+exports.password = 'xxxxxxxxx';
View
21 activities/deathbycaptcha/deathbycaptcha.js
@@ -0,0 +1,21 @@
+var deathbycaptcha = require('deathbycaptcha2');
+
+exports.worker = function (task, config) {
+
+ deathbycaptcha.credentials = {
+ username: config.username,
+ password: config.password
+ };
+
+ var input = JSON.parse(task.config.input);
+
+ deathbycaptcha.decodeUrl(input.url, 10000, function(err, result) {
+
+ task.respondCompleted({captcha_text: result.text}, function (err) {
+ if (err) { console.error(err); return; }
+ console.log("deathbycaptcha: respondComplete");
+ });
+
+ });
+
+};
View
8 activities/deathbycaptcha/package.json
@@ -0,0 +1,8 @@
+{
+ "name" : "deathbycaptcha",
+ "main" : "./deathbycaptcha.js",
+ "private": true,
+ "dependencies": {
+ "deathbycaptcha2": "1.0.0"
+ }
+}
View
21 activities/deathbycaptcha/test.js
@@ -0,0 +1,21 @@
+
+var worker = require('./deathbycaptcha').worker;
+
+var config = require('./config');
+
+
+var task = {
+
+ config: {
+ input: JSON.stringify({
+ url: "http://upload.wikimedia.org/wikipedia/commons/b/b6/Modern-captcha.jpg"
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log(results);
+ }
+};
+
+worker(task, config);
View
9 activities/dynamodb/README.md
@@ -0,0 +1,9 @@
+# Amazon DynamoDB activities
+
+Uses the AWS JavaScript SDK for Node.js
+
+## Documentation
+
+The full list of methods is available on :
+
+http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/Client.html
View
6 activities/dynamodb/config.example.js
@@ -0,0 +1,6 @@
+
+exports.accessKeyId = "XXXX";
+
+exports.secretAccessKey = "XXXX";
+
+exports.region = 'us-east-1';
View
51 activities/dynamodb/dynamodb.js
@@ -0,0 +1,51 @@
+
+var AWS = require('aws-sdk');
+
+
+function makeFct(name) {
+
+ return function (task, config) {
+
+ AWS.config.update({
+ accessKeyId: config.accessKeyId,
+ secretAccessKey: config.secretAccessKey,
+ region: config.region
+ });
+
+ var params = JSON.parse(task.config.input);
+
+ var svc = new AWS.DynamoDB();
+
+ svc.client[name](params, function (err, data) {
+ if (err) {
+ console.log(err); // an error occurred
+ task.respondFailed('Error during the DynamoDB call', err);
+
+ } else {
+ // successful response
+ // console.log( JSON.stringify(data, null, 3) );
+ task.respondCompleted(data);
+ }
+ });
+
+ };
+
+}
+
+[
+ "batchGetItem",
+ "batchWriteItem",
+ "createTable",
+ "deleteItem",
+ "deleteTable",
+ "describeTable",
+ "getItem",
+ "listTables",
+ "putItem",
+ "query",
+ "scan",
+ "updateItem",
+ "updateTable"
+].forEach(function(n) {
+ exports[n] = makeFct(n);
+});
View
9 activities/dynamodb/package.json
@@ -0,0 +1,9 @@
+{
+ "name" : "dynamodb",
+ "main" : "./dynamodb.js",
+ "version": "0.0.1",
+ "private": true,
+ "dependencies": {
+ "aws-sdk": "0.9.1-pre.2"
+ }
+}
View
20 activities/dynamodb/test.js
@@ -0,0 +1,20 @@
+
+var worker = require('./dynamodb').listTables;
+
+var config = require('./config');
+
+var task = {
+
+ config: {
+ input: JSON.stringify({
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log(JSON.stringify(results, null, 3) );
+ }
+
+};
+
+worker(task, config);
View
9 activities/ec2/README.md
@@ -0,0 +1,9 @@
+# Amazon EC2 activities
+
+Uses the AWS JavaScript SDK for Node.js
+
+## Documentation
+
+The full list of methods is available on :
+
+http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/EC2/Client.html
View
97 activities/ec2/TODO.js
@@ -0,0 +1,97 @@
+
+/*
+
+# Ouvrir une session SSH :
+ssh -i ~/.ec2/quickflow.pem bitnami@ec2-23-20-110-171.compute-1.amazonaws.com
+
+
+# Pour lancer une commande :
+ssh -i ~/.ec2/quickflow.pem bitnami@ec2-23-20-110-171.compute-1.amazonaws.com 'uptime'
+
+
+
+# USER DATA :
+
+GET http://169.254.169.254/2007-03-01/user-data
+
+*/
+
+// TODO: make default duration of the task much longer !
+
+
+exports.worker = function(task, input, cb) {
+
+ var ec2 = aws.createEC2Client(yourAccessKeyId, yourSecretAccessKey);
+
+ function pollCheckRunning(instanceId, interval) {
+
+ ec2.call("DescribeInstances", {}, function(err, result) {
+
+ if(err) { cb(err); return; }
+
+ var allInstances = [];
+
+ // XML to JSON conversion is bad on arrays
+ var reservationSets = result.reservationSet.item;
+ if( !Array.isArray(reservationSets) ) reservationSets = [reservationSets];
+
+ reservationSets.forEach(function(r) {
+ var instances = r.instancesSet.item;
+ if( !Array.isArray(instances) ) r.instancesSet.item = [r.instancesSet.item];
+ allInstances = allInstances.concat(instances);
+ });
+
+ var instances = allInstances.filter(function(i){return i.instanceId==instanceId;})
+ if(instances.length != 1) {
+ console.log("bouhh...");
+ }
+
+ var instance = instances[0];
+
+ console.log(instance.instanceState.name);
+
+ if(instance.instanceState.name == "running") {
+ console.log("EC2 instance running !");
+
+ clearInterval(interval);
+
+ cb(null, {instanceId: instanceId, instance: instance});
+
+ }
+ });
+
+ };
+
+ var instanceId,
+ reservationId;
+ ec2.call("RunInstances", {
+ ImageId: input.ImageId,
+ MinCount: 1,
+ MaxCount: 1,
+
+ UserData: "This is some cool user data\nOuyeah\n",
+ InstanceType: "m1.small",
+
+ KeyName: "quickflow",
+
+ "SecurityGroup.0": "quick-start-1"
+
+
+ }, function (err, response) {
+
+ if(err) { cb(err); return; }
+
+ console.log("Instance created !");
+ console.log( response );
+
+ reservationId = response.reservationId;
+ instanceId = response.instancesSet.item.instanceId;
+
+ var interval = setInterval(function() {
+ pollCheckRunning(instanceId, interval);
+ }, 5000);
+
+ });
+
+};
+
View
6 activities/ec2/config.example.js
@@ -0,0 +1,6 @@
+
+exports.accessKeyId = "XXXX";
+
+exports.secretAccessKey = "XXXX";
+
+exports.region = 'us-east-1';
View
182 activities/ec2/ec2.js
@@ -0,0 +1,182 @@
+
+var AWS = require('aws-sdk');
+
+
+function makeFct(name) {
+
+ return function (task, config) {
+
+ AWS.config.update({
+ accessKeyId: config.accessKeyId,
+ secretAccessKey: config.secretAccessKey,
+ region: config.region
+ });
+
+ var params = JSON.parse(task.config.input);
+
+ var svc = new AWS.EC2();
+
+ svc.client[name](params, function (err, data) {
+ if (err) {
+ console.log(err); // an error occurred
+ task.respondFailed('Error during the EC2 call', err);
+
+ } else {
+ // successful response
+ // console.log( JSON.stringify(data, null, 3) );
+ task.respondCompleted(data);
+ }
+ });
+
+ };
+
+}
+
+[
+ "activateLicense",
+ "allocateAddress",
+ "assignPrivateIpAddresses",
+ "associateAddress",
+ "associateDhcpOptions",
+ "associateRouteTable",
+ "attachInternetGateway",
+ "attachNetworkInterface",
+ "attachVolume",
+ "attachVpnGateway",
+ "authorizeSecurityGroupEgress",
+ "authorizeSecurityGroupIngress",
+ "bundleInstance",
+ "cancelBundleTask",
+ "cancelConversionTask",
+ "cancelExportTask",
+ "cancelReservedInstancesListing",
+ "cancelSpotInstanceRequests",
+ "confirmProductInstance",
+ "createCustomerGateway",
+ "createDhcpOptions",
+ "createImage",
+ "createInstanceExportTask",
+ "createInternetGateway",
+ "createKeyPair",
+ "createNetworkAcl",
+ "createNetworkAclEntry",
+ "createNetworkInterface",
+ "createPlacementGroup",
+ "createReservedInstancesListing",
+ "createRoute",
+ "createRouteTable",
+ "createSecurityGroup",
+ "createSnapshot",
+ "createSpotDatafeedSubscription",
+ "createSubnet",
+ "createTags",
+ "createVolume",
+ "createVpc",
+ "createVpnConnection",
+ "createVpnConnectionRoute",
+ "createVpnGateway",
+ "deactivateLicense",
+ "deleteCustomerGateway",
+ "deleteDhcpOptions",
+ "deleteInternetGateway",
+ "deleteKeyPair",
+ "deleteNetworkAcl",
+ "deleteNetworkAclEntry",
+ "deleteNetworkInterface",
+ "deletePlacementGroup",
+ "deleteRoute",
+ "deleteRouteTable",
+ "deleteSecurityGroup",
+ "deleteSnapshot",
+ "deleteSpotDatafeedSubscription",
+ "deleteSubnet",
+ "deleteTags",
+ "deleteVolume",
+ "deleteVpc",
+ "deleteVpnConnection",
+ "deleteVpnConnectionRoute",
+ "deleteVpnGateway",
+ "deregisterImage",
+ "describeAddresses",
+ "describeAvailabilityZones",
+ "describeBundleTasks",
+ "describeConversionTasks",
+ "describeCustomerGateways",
+ "describeDhcpOptions",
+ "describeExportTasks",
+ "describeImageAttribute",
+ "describeImages",
+ "describeInstanceAttribute",
+ "describeInstanceStatus",
+ "describeInstances",
+ "describeInternetGateways",
+ "describeKeyPairs",
+ "describeLicenses",
+ "describeNetworkAcls",
+ "describeNetworkInterfaceAttribute",
+ "describeNetworkInterfaces",
+ "describePlacementGroups",
+ "describeRegions",
+ "describeReservedInstances",
+ "describeReservedInstancesListings",
+ "describeReservedInstancesOfferings",
+ "describeRouteTables",
+ "describeSecurityGroups",
+ "describeSnapshotAttribute",
+ "describeSnapshots",
+ "describeSpotDatafeedSubscription",
+ "describeSpotInstanceRequests",
+ "describeSpotPriceHistory",
+ "describeSubnets",
+ "describeTags",
+ "describeVolumeAttribute",
+ "describeVolumeStatus",
+ "describeVolumes",
+ "describeVpcs",
+ "describeVpnConnections",
+ "describeVpnGateways",
+ "detachInternetGateway",
+ "detachNetworkInterface",
+ "detachVolume",
+ "detachVpnGateway",
+ "disableVgwRoutePropagation",
+ "disassociateAddress",
+ "disassociateRouteTable",
+ "enableVgwRoutePropagation",
+ "enableVolumeIO",
+ "getConsoleOutput",
+ "getPasswordData",
+ "importInstance",
+ "importKeyPair",
+ "importVolume",
+ "modifyImageAttribute",
+ "modifyInstanceAttribute",
+ "modifyNetworkInterfaceAttribute",
+ "modifySnapshotAttribute",
+ "modifyVolumeAttribute",
+ "monitorInstances",
+ "purchaseReservedInstancesOffering",
+ "rebootInstances",
+ "registerImage",
+ "releaseAddress",
+ "replaceNetworkAclAssociation",
+ "replaceNetworkAclEntry",
+ "replaceRoute",
+ "replaceRouteTableAssociation",
+ "reportInstanceStatus",
+ "requestSpotInstances",
+ "resetImageAttribute",
+ "resetInstanceAttribute",
+ "resetNetworkInterfaceAttribute",
+ "resetSnapshotAttribute",
+ "revokeSecurityGroupEgress",
+ "revokeSecurityGroupIngress",
+ "runInstances",
+ "startInstances",
+ "stopInstances",
+ "terminateInstances",
+ "unassignPrivateIpAddresses",
+ "unmonitorInstances"
+].forEach(function(n) {
+ exports[n] = makeFct(n);
+});
View
9 activities/ec2/package.json
@@ -0,0 +1,9 @@
+{
+ "name" : "ec2",
+ "main" : "./ec2.js",
+ "version": "0.0.1",
+ "private": true,
+ "dependencies": {
+ "aws-sdk": "0.9.1-pre.2"
+ }
+}
View
9 activities/echo/echo.js
@@ -0,0 +1,9 @@
+
+exports.worker = function (task, config) {
+
+ var result = task.config.input; // yes, echo is pretty dumb....
+
+ task.respondCompleted(result);
+
+};
+
View
7 activities/echo/package.json
@@ -0,0 +1,7 @@
+{
+ "name" : "echo",
+ "main" : "./echo.js",
+ "version": "0.0.1",
+ "private": true,
+ "description": "Most basic Activity. Output is the same as input."
+}
View
60 activities/feedautodiscovery/feedautodiscovery.js
@@ -0,0 +1,60 @@
+
+var request = require('request'),
+ htmlparser = require("htmlparser"),
+ async = require('async'),
+ xml2js = require('xml2js-expat');
+
+var feed_auto_discovery = function (url, cb) {
+ request(url, function (error, response, body) {
+ var handler = new htmlparser.DefaultHandler(function (err, dom) {
+ if (err) {
+ cb(err, []);
+ } else {
+ var links = htmlparser.DomUtils.getElements({tag_name: 'link', rel: "alternate", type: 'application/rss+xml' }, dom);
+
+ cb(null, links);
+ }
+ }, { verbose: false, ignoreWhitespace: true });
+ var parser = new htmlparser.Parser(handler);
+ parser.parseComplete(body);
+ });
+};
+
+exports.worker = function (task, config) {
+
+ var input = JSON.parse(task.config.input);
+
+ // fetch must be able to get multiple URLs
+ var urls = [];
+ if (Array.isArray(input.URL)) {
+ input.URL.forEach(function (u) {
+ urls.push(u);
+ });
+ } else {
+ urls.push(input.URL);
+ }
+
+ var feeds = [];
+ async.forEachSeries(urls, function (url, cb) {
+
+ feed_auto_discovery(url, function (err, results) {
+ feeds = feeds.concat(results);
+ cb(err, results);
+ });
+
+ }, function () {
+
+ var results = feeds.map(function (f) {
+ var o = f.attribs;
+ o["y:title"] = o.title;
+ return o;
+ });
+
+ task.respondCompleted({
+ feeds: results
+ });
+
+ });
+
+};
+
View
13 activities/feedautodiscovery/package.json
@@ -0,0 +1,13 @@
+{
+ "name" : "feedautodiscovery",
+ "main" : "./feedautodiscovery.js",
+ "version": "0.0.1",
+ "description": "feedautodiscovery module",
+ "private": true,
+ "dependencies": {
+ "xml2js-expat": "0.2.2",
+ "request": "2.11.4",
+ "async": "0.1.22",
+ "htmlparser": "1.7.6"
+ }
+}
View
17 activities/feedautodiscovery/test.js
@@ -0,0 +1,17 @@
+var worker = require('./feedautodiscovery').worker;
+
+worker({
+
+ config: {
+ input: JSON.stringify({
+ URL: "http://www.lemonde.fr"
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log(results);
+ }
+
+});
+
View
85 activities/fetchfeed/fetchfeed.js
@@ -0,0 +1,85 @@
+
+var request = require('request'),
+ xml2js = require('xml2js-expat'),
+ async = require('async');
+
+var fetch_feed = function (url, cb) {
+
+ request(url, function (error, response, body) {
+
+ console.log("Got response. Code = " + response.statusCode);
+
+ if (!error && response.statusCode === 200) {
+ var r = body;
+ var contentType = response.headers["content-type"].split(';')[0];
+ if (contentType) {
+
+ console.log("contentType = " + contentType);
+
+ // Parsing XML
+ var feed_content_types = ["text/xml", "application/rss+xml", "application/xml"];
+ if (feed_content_types.indexOf(contentType) !== -1) {
+ console.log("parsing...");
+ var parser = new xml2js.Parser();
+ parser.addListener('end', function (r) {
+ //console.log(r);
+ var result = r.channel ? r.channel.item : r.entry;
+ cb(null, result);
+ });
+ parser.addListener('error', function (r) {
+ console.log("Error parsing feed...");
+ });
+ parser.parseString(body);
+ return;
+ }
+ }
+
+ } else {
+ // TODO: we should indicate errors in the response
+ cb(null, []);
+ }
+ });
+};
+
+var fetch_feeds = function (urls, cb) {
+
+ var items = [];
+ async.forEachSeries(urls, function (url, cb) {
+
+ fetch_feed(url, function (err, result) {
+ items = items.concat(result);
+ cb(err, result);
+ });
+
+ }, function () {
+ cb(null, items);
+ });
+
+};
+
+exports.fetch_feeds = fetch_feeds;
+
+exports.worker = function (task, config) {
+
+ var input = JSON.parse(task.config.input);
+
+ // fetch must be able to get multiple URLs
+ var urls = [];
+ if (Array.isArray(input.URL)) {
+ input.URL.forEach(function (u) {
+ urls.push(u);
+ });
+ } else {
+ urls.push(input.URL);
+ }
+
+ console.log(urls);
+
+ fetch_feeds(urls, function (err, items) {
+ task.respondCompleted({
+ entries: items
+ });
+ });
+
+};
+
View
12 activities/fetchfeed/package.json
@@ -0,0 +1,12 @@
+{
+ "name" : "fetchfeed",
+ "main" : "./fetchfeed.js",
+ "version": "0.0.1",
+ "description": "Fetch feed module",
+ "private": true,
+ "dependencies": {
+ "async": "0.1.22",
+ "xml2js-expat": "0.2.2",
+ "request": "2.11.4"
+ }
+}
View
17 activities/fetchfeed/test.js
@@ -0,0 +1,17 @@
+var worker = require('./fetchfeed').worker;
+
+worker({
+
+ config: {
+ input: JSON.stringify({
+ URL: "http://www.lemonde.fr/rss/une.xml"
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log(results);
+ }
+
+});
+
View
42 activities/google-analytics/auth.js
@@ -0,0 +1,42 @@
+
+var oauth = require('oauth'),
+ querystring = require('querystring');
+
+var config = require('./config');
+
+var scope = "https://www.googleapis.com/auth/analytics.readonly";
+
+var redirect_uri = 'http://localhost/oauth2callback';
+
+var oa = new oauth.OAuth2(config.client_id,
+ config.client_secret,
+ "https://accounts.google.com/o",
+ "/oauth2/auth",
+ "/oauth2/token");
+
+var authorizeUrl = oa.getAuthorizeUrl({scope: scope, response_type: 'code', redirect_uri: redirect_uri});
+
+var exec = require('child_process').exec,
+ child;
+
+child = exec('open "' + authorizeUrl + '"', function (error, stdout, stderr) {});
+
+var http = require('http');
+http.createServer(function (req, res) {
+
+ var code = querystring.parse(req.url.split('?')[1]).code;
+
+ oa.getOAuthAccessToken(code, {grant_type: 'authorization_code', redirect_uri: redirect_uri}, function (err, access_token, refresh_token) {
+ if (err) {
+ res.writeHead(500, {'Content-Type': 'text/plain'});
+ res.end(err);
+ } else {
+ //console.log('access token: ' + access_token + '\n');
+ //console.log('refresh token: ' + refresh_token);
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end(access_token);
+ }
+ process.exit(0);
+ });
+
+}).listen(80, '127.0.0.1');
View
8 activities/google-analytics/config.example.js
@@ -0,0 +1,8 @@
+
+
+exports.client_id = "XXXXXX.apps.googleusercontent.com";
+exports.client_secret = "XXXXXXX";
+
+// TODO: change at each login !
+exports.access_token = "XXXXX";
+
View
45 activities/google-analytics/google-analytics.js
@@ -0,0 +1,45 @@
+// Cf DOC :
+// https://developers.google.com/analytics/devguides/reporting/core/v3/reference
+// https://developers.google.com/analytics/devguides/reporting/core/dimsmets
+
+var oauth = require('oauth'),
+ querystring = require('querystring');
+
+exports.worker = function (task, config) {
+
+ var scope = "https://www.googleapis.com/auth/analytics.readonly";
+
+ var oa = new oauth.OAuth2(config.client_id,
+ config.client_secret,
+ "https://accounts.google.com/o",
+ "/oauth2/auth",
+ "/oauth2/token");
+
+ // TODO: in task input !
+ var q = {
+ "ids": "ga:24583775",
+ "start-date": "2011-01-01",
+ "end-date": "2012-06-30",
+ "metrics": "ga:visits",
+ "dimensions": "ga:nthMonth",
+ "filters": "ga:pagePath=@FRYROC"
+ };
+
+ oa.getProtectedResource(
+ "https://www.googleapis.com/analytics/v3/data/ga?" + querystring.stringify(q),
+ config.access_token,
+ function (error, data, response) {
+
+ if (error) {
+ console.log(error);
+ // TODO: taskFailed
+ return;
+ }
+
+ var feed = JSON.parse(data);
+ task.respondCompleted(feed);
+ }
+ );
+
+};
+
View
10 activities/google-analytics/package.json
@@ -0,0 +1,10 @@
+{
+ "name" : "google-analytics",
+ "main" : "./google-analytics.js",
+ "version": "0.0.1",
+ "description": "Basic Anayltics query",
+ "private": true,
+ "dependencies": {
+ "oauth": "0.9.8"
+ }
+}
View
26 activities/google-analytics/test.js
@@ -0,0 +1,26 @@
+
+var worker = require('./google-analytics').worker;
+
+var config = require('./config');
+
+
+var task = {
+
+ config: {
+ input: JSON.stringify({
+ "ids": "ga:24583775",
+ "start-date": "2011-01-01",
+ "end-date": "2012-06-30",
+ "metrics": "ga:visits",
+ "dimensions": "ga:nthMonth",
+ "filters": "ga:pagePath=@FRYROC"
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log(results);
+ }
+};
+
+worker(task, config);
View
133 activities/google-keyword-tool/keyword-tool.js
@@ -0,0 +1,133 @@
+
+var phantom = require('node-phantom'),
+ prompt = require('prompt');
+
+
+function getResults(page, ph) {
+
+ page.evaluate(function(){
+
+ /*function parseTable(table) {
+ var results = [];
+ var searchRows = table.children[2].rows;
+
+ for(var i = 0 ; i < searchRows.length; i++) {
+ var searchRow = searchRows[i],
+ cells = searchRow.cells;
+
+ try {
+ var keyword = cells[1].children[0].children[0].children[0].children[1].children[0].innerHTML.replace('<b>','').replace('</b>', '');
+ }
+ catch(ex) {}
+
+ try {
+ var concurence = cells[2].children[0].children[0].children[0].innerHTML;
+ }
+ catch(ex) {}
+
+ try {
+ var concurenceLevel = cells[2].children[0].children[0].children[0].title;
+ }
+ catch(ex) {}
+
+ try {
+ var worldSearchVolume = cells[3].children[0].children[0].innerHTML.replace(/&nbsp;/g, '');
+ }
+ catch(ex) {}
+
+ try {
+ var zoneSearchVolume = cells[4].children[0].children[0].innerHTML.replace(/&nbsp;/g, '');
+ }
+ catch(ex) {}
+
+ results.push({
+ keyword: keyword,
+ concurence: concurence,
+ concurenceLevel: concurenceLevel,
+ worldSearchVolume: worldSearchVolume,
+ zoneSearchVolume: zoneSearchVolume
+ });
+
+ }
+ return {
+ children: !!table.children,
+ searchRows: !!searchRows,
+ results: results
+ };
+ }
+
+ try {
+ var tables = document.getElementsByClassName('sBPB');
+ var data = {
+ search: parseTable(tables[0])
+ };
+ if (tables.length > 1) {
+ data.ideas = parseTable(tables[1]);
+ }
+
+ return data;
+
+ } catch(ex) {
+ return ex.message;
+ }*/
+
+
+ return String(window.location);
+
+ }, function(err, res) {
+
+ console.log(JSON.stringify(res, null, 3));
+
+ ph.exit(function(){
+ console.log("Phantom exited !!");
+ console.log("Have results ? ", typeof res !== "string");
+ });
+
+ });
+
+}
+
+
+
+function openKeywordTool(page, ph) {
+
+ page.render(__dirname+"/p.png", function() {
+
+ prompt.start();
+ prompt.get(['captcha'], function (err, result) {
+
+ page.evaluate(eval("(function() {"+
+ "document.getElementsByClassName('gwt-TextBox')[0].value = "+JSON.stringify(result.captcha)+";"+
+ "document.getElementsByClassName('sJBB')[0].value = 'defibrillator\\ndefibrillator toaster\\ndefibrillator toaster for sale';"+
+ "var button = document.getElementsByClassName('gwt-Button')[0];"+
+ "var e = document.createEvent('MouseEvents');"+
+ "e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);"+
+ "button.dispatchEvent(e);"+
+ "})"), function(err, res) {
+
+ setTimeout(function() {
+ page.render(__dirname+"/b.png", function() {
+ getResults(page, ph);
+ });
+ }, 10000);
+
+ });
+
+ });
+
+ });
+
+}
+
+
+var url = "https://adwords.google.com/o/KeywordTool";
+phantom.create(function(err, ph) {
+ ph.createPage (function(err, page) {
+ page.open(url, function (err, status) {
+ setTimeout(function() {
+ openKeywordTool(page, ph);
+ },10000);
+ });
+ });
+});
+
View
30 activities/google/google.js
@@ -0,0 +1,30 @@
+var google = require('google-tools');
+
+function makeFct(name) {
+ return function (task, config) {
+ var params = JSON.parse(task.config.input);
+
+ google[name](params, function(err, r) {
+
+ if(err) {
+ console.log(err);
+ // TODO: task failed
+ task.respondFailed('Error in google.'+name, err);
+ return;
+ }
+
+ task.respondCompleted(r);
+ });
+ };
+}
+
+[
+ "pagerank",
+ "search",
+ "pagerankAvg",
+ "position",
+ "suggest",
+ "deep_suggest"
+].forEach(function(m) {
+ exports[m] = makeFct(m);
+});
View
8 activities/google/package.json
@@ -0,0 +1,8 @@
+{
+ "name" : "google",
+ "main" : "./google.js",
+ "private": true,
+ "dependencies": {
+ "google-tools": "0.0.1"
+ }
+}
View
19 activities/google/test-pagerank.js
@@ -0,0 +1,19 @@
+
+var worker = require('./google').pagerank;
+
+
+var task = {
+
+ config: {
+ input: JSON.stringify({
+ url: "http://www.clicrdv.com"
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log( JSON.stringify(results, null, 3) );
+ }
+};
+
+worker(task);
View
19 activities/google/test-search.js
@@ -0,0 +1,19 @@
+
+var worker = require('./google').search;
+
+
+var task = {
+
+ config: {
+ input: JSON.stringify({
+ q: "service mapping description"
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log( JSON.stringify(results, null, 3) );
+ }
+};
+
+worker(task);
View
19 activities/google/test-suggest.js
@@ -0,0 +1,19 @@
+
+var worker = require('./google').suggest;
+
+
+var task = {
+
+ config: {
+ input: JSON.stringify({
+ q: "roger federer"
+ })
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log( JSON.stringify(results, null, 3) );
+ }
+};
+
+worker(task);
View
4 activities/hello-activity/config.example.js
@@ -0,0 +1,4 @@
+
+// This file contains the "global" settings for this activity
+
+exports.name = "neyric";
View
13 activities/hello-activity/hello.js
@@ -0,0 +1,13 @@
+/**
+ * Activity to demonstrate the config.js usage.
+ * This activity doesn't take any argument.
+ */
+exports.worker = function (task, config) {
+
+ console.log("Starting hello-activity : " + JSON.stringify(config, null, 3));
+
+ task.respondCompleted({
+ greeting: "Hello " + config.name + " !"
+ });
+
+};
View
5 activities/hello-activity/package.json
@@ -0,0 +1,5 @@
+{
+ "name" : "hello-activity",
+ "private": true,
+ "main" : "./hello.js"
+}
View
117 activities/http-request/http-request.js
@@ -0,0 +1,117 @@
+var http = require('http'),
+ https = require('https'),
+ querystring = require('querystring'),
+ URL = require('url'),
+ xml2js = require('xml2js-expat');
+
+exports.worker = function (task, config) {
+
+ var input = JSON.parse(task.config.input);
+
+ var url = URL.parse(input.url);
+ var method = input.method || "GET";
+
+ var port = url.port;
+ if (!port) { port = 80; }
+
+ var encoding = input.encoding || "application/x-www-form-urlencoded";
+
+ var ssl = false;
+ if (input.url.substr(0, 5) === "https") {
+ ssl = true;
+ port = 443;
+ }
+
+ //console.log( url );
+
+ //var client = http.createClient(port, url.hostname, ssl);
+
+ var path = url.pathname || '/';
+
+ if (method === "GET" || method === "DELETE") {
+ path += '?' + querystring.stringify(input.data);
+ }
+
+ var headers = {
+ 'host': url.hostname,
+ 'accept': '*/*'
+ };
+
+ var body;
+ if (method === "POST") {
+ if (encoding === "application/x-www-form-urlencoded") {
+ body = querystring.stringify(input.data);
+ } else if (encoding === "application/json") {
+ body = JSON.stringify(input.data);
+ }
+ headers["content-length"] = body.length;
+ headers["content-type"] = encoding;
+ }
+
+ var options = {
+ port: port,
+ host: url.hostname,
+ method: method,
+ path: path,
+ headers: headers
+ };
+
+ if (url.auth) {
+ options.auth = url.auth;
+ }
+
+ console.log(options);
+
+ var request = (ssl ? https : http).request(options);
+
+ if (method === "POST" || method === "PUT") {
+ request.write(body);
+ }
+
+ request.end();
+
+ request.on('response', function (response) {
+
+ /*console.log("Got response !");
+ console.log('STATUS: ' + response.statusCode);
+ console.log('HEADERS: ' + JSON.stringify(response.headers) );*/
+
+ response.setEncoding('utf8');
+ var complete = "";
+ response.on('data', function (chunk) {
+ complete += chunk;
+ });
+ response.on('end', function () {
+
+ var r = complete;
+
+ // content-type:
+ var contentType = response.headers["content-type"].split(';')[0];
+ if (contentType) {
+
+ //console.log("Content-Type "+contentType);
+
+ // Parsing JSON
+ var json_content_types = ["application/json", "text/javascript", "application/javascript"];
+ if (json_content_types.indexOf(contentType) !== -1) {
+ r = JSON.parse(complete);
+ }
+
+ // Parsing XML
+ if (contentType === "text/xml") {
+ var parser = new xml2js.Parser();
+ parser.addListener('end', function (r) {
+ task.respondCompleted({headers: response.headers, body: r});
+ });
+ parser.parseString(complete);
+ return;
+ }
+ }
+
+ task.respondCompleted({headers: response.headers, body: r});
+
+ });
+
+ });
+};
+
View
10 activities/http-request/package.json
@@ -0,0 +1,10 @@
+{
+ "name" : "http-request",
+ "main" : "./http-request.js",
+ "description": "Performs a HTTP or HTTPS request",
+ "version": "0.0.1",
+ "private": true,
+ "dependencies": {
+ "xml2js-expat": "0.2.2"
+ }
+}
View
25 activities/http-request/test.js
@@ -0,0 +1,25 @@
+
+var worker = require('./http-request').worker;
+
+var task = {
+
+ config: {
+
+ input: JSON.stringify({
+ url: "http://query.yahooapis.com/v1/public/yql",
+ data: {
+ q: 'show tables',
+ format: 'json'
+ }
+ })
+
+ },
+
+ respondCompleted: function (results) {
+ console.log("Done !");
+ console.log(JSON.stringify(results));
+ }
+
+};
+
+worker(task);
View
8 activities/humantask/README.md
@@ -0,0 +1,8 @@
+# humantask
+
+
+## TODO
+
+* use aws-sdk instead of dynamo
+
+
View
6 activities/humantask/activityTypeOptions.json
@@ -0,0 +1,6 @@
+{
+ "heartbeatTimeout": "NONE",
+ "scheduleToCloseTimeout": "NONE",
+ "scheduleToStartTimeout": "NONE",
+ "startToCloseTimeout": "NONE"
+}
View
26 activities/humantask/app/views/defaultTaskView.ejs
@@ -0,0 +1,26 @@
+<script src="/lib/jsonPretty.js"></script>
+
+<h1>Default humantask template</h1>
+
+<p>Input Data for this task :</p>
+
+<pre id="preData"></pre>
+<script>
+var taskData = <%- activityTask.input %>;
+document.getElementById('preData').innerHTML = taskData.toPrettyJSONString();
+</script>
+
+<br /><br />
+
+ <input type='submit' class='btn btn-primary' value="Complete !" />
+</form>
+
+ <form method="POST" action="/<%- (activityTask.taskToken) %>/cancel">
+ <input type='submit' class='btn' value="Cancel !" />
+ </form>
+
+ <form method="POST" action="/<%- (activityTask.taskToken) %>/fail">
+ <input type='submit' class='btn' value="Fail !" />
+
+
+<!--script type='text/javascript' src='https://s3.amazonaws.com/mturk-public/externalHIT_v1.js'></script></head><body><form name="mturk_form" method="post" id="mturk_form" action="https://www.mturk.com/mturk/externalSubmit"><input type="hidden" value="" name="assignmentId" id="assignmentId" /-->
View
7 activities/humantask/app/views/error.ejs
@@ -0,0 +1,7 @@
+<h1>Error</h1>
+
+
+<pre>
+<%= JSON.stringify(error) %>
+</pre>
+
View
3 activities/humantask/app/views/finished.ejs
@@ -0,0 +1,3 @@
+<h1>Thank you !</h1>
+
+<p>Your task is finished, you can now close this page.</p>
View
26 activities/humantask/app/views/index.ejs
@@ -0,0 +1,26 @@
+
+<h2>Tasks</h2>
+
+<p><strong>Here is the list of pending HumanTask activities.</strong></p>
+
+<ul>
+ <% tasks.forEach(function(task){ %>
+ <!-- TODO: mettre un lien uniquement si humantask -->
+ <li>
+ <% if(task.activityType == "humantask") { %><a href='/activity/<%= querystring_escape(task.taskToken) %>'><% } %><%= task.activityId+' ('+task.activityType+')' %><% if(task.activityType == "humantask") { %></a><% } %></li>
+ <% }) %>
+</ul>
+
+
+<% if( tasks.length === 0) { %>
+
+<p>Nothing to do...</p>
+
+
+<script>
+setTimeout(function() {
+ window.location = window.location;
+}, 3000);
+</script>
+
+<% } %>
View
53 activities/humantask/app/views/layout.ejs
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Human Task</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="">
+ <meta name="author" lang="en" content="Eric Abouaf" />
+ <meta name="copyright" content="Copyright 2011-2012 Eric Abouaf" />
+
+ <!-- Le styles -->
+ <link href="/lib/bootstrap/css/bootstrap.css" rel="stylesheet">
+ <style>
+ body {
+ padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
+ }
+ </style>
+ <link href="/lib/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
+
+ <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+
+ <!-- Le fav and touch icons -->
+
+
+ <link rel="shortcut icon" href="/favicon.ico" />
+ <link rel="icon" href="/favicon.ico" sizes="32x32" />
+ <!--link rel="shortcut icon" href="/lib/bootstrap/ico/favicon.ico">
+ <link rel="apple-touch-icon-precomposed" sizes="144x144" href="/lib/bootstrap/ico/apple-touch-icon-144-precomposed.png">
+ <link rel="apple-touch-icon-precomposed" sizes="114x114" href="/lib/bootstrap/ico/apple-touch-icon-114-precomposed.png">
+ <link rel="apple-touch-icon-precomposed" sizes="72x72" href="/lib/bootstrap/ico/apple-touch-icon-72-precomposed.png">
+ <link rel="apple-touch-icon-precomposed" href="/lib/bootstrap/ico/apple-touch-icon-57-precomposed.png"-->
+
+ <script src="/lib/jquery-1.7.2.min.js"></script>
+ <script src="/lib/bootstrap/js/bootstrap.min.js"></script>
+
+ </head>
+
+ <body>
+
+ <div class="container">
+
+ <form method="POST" action="/<%- taskToken %>/completed">
+
+ <%- body %>
+
+ </form>
+
+ </div> <!-- /container -->
+ </body>
+</html>
View
3 activities/humantask/app/views/unavailable.ejs
@@ -0,0 +1,3 @@
+<h1>Task not available</h1>
+
+<p>This task does not exist or has already been terminated.</p>
View
27 activities/humantask/config.example.js
@@ -0,0 +1,27 @@
+
+exports.mailer_transport = {
+ service: "Gmail",
+ auth: {
+ user: "xxxx",
+ pass: "xxx"
+ }
+};
+
+exports.awsCredentials = {
+ accessKeyId: "xxxx",
+ secretAccessKey: "xxxxxx"
+};
+
+exports.server = {
+ port: 3000,
+ ip: 'localhost',
+ host: 'localhost'
+};
+
+exports.defaultNotification = {
+ "to": "example@gmail.com",
+ "subject": "A new task is waiting to be done",
+ "from": "AWS-SWF <example@example.com>"
+};
+
+exports.region = 'us-east-1';
View
74 activities/humantask/humantask.js
@@ -0,0 +1,74 @@
+var nodemailer = require('nodemailer'),
+ dynamo = require('dynamo'),
+ querystring = require('querystring');
+
+
+function sendNotification(notification, taskToken, config) {
+
+ var smtpTransport = nodemailer.createTransport("SMTP", config.mailer_transport);
+
+ var mailOptions = {
+
+ from: notification.from || config.defaultNotification.from,
+ to: notification.to || config.defaultNotification.to,
+ subject: notification.subject || config.defaultNotification.subject,
+
+
+ text: "Hello world",
+ html: "New task : <a href='http://" + config.server.host + ":" + config.server.port + "/activity/"+querystring.escape(taskToken)+"'>Click here to do the task !</a>"
+ };
+
+ // send mail with defined transport object
+ smtpTransport.sendMail(mailOptions, function(error, response){
+ if(error){
+ console.log(error);
+ } else {
+ console.log("HUMANTASK WORKER email notification sent: " + response.message);
+ }
+ // if you don't want to use this transport object anymore, uncomment following line
+ smtpTransport.close(); // shut down the connection pool, no more messages
+ });
+
+}
+
+
+exports.worker = function (task, config) {
+
+ var input = JSON.parse(task.config.input);
+
+ var client = dynamo.createClient(config.awsCredentials);
+ var db = client.get("us-east-1"); // TODO: in config
+
+ // insert the task in DynamoDB
+ var o = {};
+ for(var k in task.config) {
+ if( typeof task.config[k] == "string" ) {
+ o[k] = { S: task.config[k] };
+ }
+ /*else if(k == "activityType") {
+ o[k] = { S: task.config[k].name };
+ }*/
+ else {
+ o[k] = { S: JSON.stringify(task.config[k]) };
+ }
+ }
+
+ db.putItem({
+ TableName: "Task",
+ Item: o
+ }, function(err, data) {
+
+ if(err) {
+ console.log(err);
+ task.respondFailed(err, "");
+ return;
+ }
+
+ // Send email notification
+ if(input["email-notification"] || config.defaultNotification) {
+ sendNotification(input["email-notification"] ||  {}, task.config.taskToken, config);
+ }
+
+ });
+
+};
View
15 activities/humantask/package.json
@@ -0,0 +1,15 @@
+{
+ "name" : "humantask",
+ "version": "0.0.1",
+ "main" : "./humantask.js",
+ "private": true,
+ "dependencies": {
+ "dynamo": "0.2.13",
+ "nodemailer": "0.3.20",
+ "express": "3.0.6",
+ "mu2": "0.5.17",
+ "ejs": "0.8.3",
+ "express-ejs-layouts": "0.3.1",
+ "aws-sdk": "0.9.1-pre.2"
+ }
+}
View
1,088 activities/humantask/public/lib/bootstrap/css/bootstrap-responsive.css
@@ -0,0 +1,1088 @@
+/*!
+ * Bootstrap Responsive v2.2.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+.clearfix {
+ *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.hidden {
+ display: none;
+ visibility: hidden;
+}
+
+.visible-phone {
+ display: none !important;
+}
+
+.visible-tablet {
+ display: none !important;
+}
+
+.hidden-desktop {
+ display: none !important;
+}
+
+.visible-desktop {
+ display: inherit !important;
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important ;
+ }
+ .visible-tablet {
+ display: inherit !important;
+ }
+ .hidden-tablet {
+ display: none !important;
+ }
+}
+
+@media (max-width: 767px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important;
+ }
+ .visible-phone {
+ display: inherit !important;
+ }
+ .hidden-phone {
+ display: none !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .row {
+ margin-left: -30px;
+ *zoom: 1;
+ }
+ .row:before,
+ .row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row:after {
+ clear: both;
+ }
+ [class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 30px;
+ }
+ .container,
+ .navbar-static-top .container,
+ .navbar-fixed-top .container,
+ .navbar-fixed-bottom .container {
+ width: 1170px;
+ }
+ .span12 {
+ width: 1170px;
+ }
+ .span11 {
+ width: 1070px;
+ }
+ .span10 {
+ width: 970px;
+ }
+ .span9 {
+ width: 870px;
+ }
+ .span8 {
+ width: 770px;
+ }
+ .span7 {
+ width: 670px;
+ }
+ .span6 {
+ width: 570px;
+ }
+ .span5 {
+ width: 470px;
+ }
+ .span4 {
+ width: 370px;
+ }
+ .span3 {
+ width: 270px;
+ }
+ .span2 {
+ width: 170px;
+ }
+ .span1 {
+ width: 70px;
+ }
+ .offset12 {
+ margin-left: 1230px;
+ }
+ .offset11 {
+ margin-left: 1130px;
+ }
+ .offset10 {
+ margin-left: 1030px;
+ }
+ .offset9 {
+ margin-left: 930px;
+ }
+ .offset8 {
+ margin-left: 830px;
+ }
+ .offset7 {
+ margin-left: 730px;
+ }
+ .offset6 {
+ margin-left: 630px;
+ }
+ .offset5 {
+ margin-left: 530px;
+ }
+ .offset4 {
+ margin-left: 430px;
+ }
+ .offset3 {
+ margin-left: 330px;
+ }
+ .offset2 {
+ margin-left: 230px;
+ }
+ .offset1 {
+ margin-left: 130px;
+ }
+ .row-fluid {
+ width: 100%;
+ *zoom: 1;
+ }
+ .row-fluid:before,
+ .row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row-fluid:after {
+ clear: both;
+ }
+ .row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.564102564102564%;
+ *margin-left: 2.5109110747408616%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+ }
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.564102564102564%;
+ }
+ .row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+ }
+ .row-fluid .span11 {
+ width: 91.45299145299145%;
+ *width: 91.39979996362975%;
+ }
+ .row-fluid .span10 {
+ width: 82.90598290598291%;
+ *width: 82.8527914166212%;
+ }
+ .row-fluid .span9 {
+ width: 74.35897435897436%;
+ *width: 74.30578286961266%;
+ }
+ .row-fluid .span8 {
+ width: 65.81196581196582%;
+ *width: 65.75877432260411%;
+ }
+ .row-fluid .span7 {
+ width: 57.26495726495726%;
+ *width: 57.21176577559556%;
+ }
+ .row-fluid .span6 {
+ width: 48.717948717948715%;
+ *width: 48.664757228587014%;
+ }
+ .row-fluid .span5 {
+ width: 40.17094017094017%;
+ *width: 40.11774868157847%;
+ }
+ .row-fluid .span4 {
+ width: 31.623931623931625%;
+ *width: 31.570740134569924%;
+ }
+ .row-fluid .span3 {
+ width: 23.076923076923077%;
+ *width: 23.023731587561375%;
+ }
+ .row-fluid .span2 {
+ width: 14.52991452991453%;
+ *width: 14.476723040552828%;
+ }
+ .row-fluid .span1 {
+ width: 5.982905982905983%;
+ *width: 5.929714493544281%;
+ }
+ .row-fluid .offset12 {
+ margin-left: 105.12820512820512%;
+ *margin-left: 105.02182214948171%;
+ }
+ .row-fluid .offset12:first-child {
+ margin-left: 102.56410256410257%;
+ *margin-left: 102.45771958537915%;
+ }
+ .row-fluid .offset11 {
+ margin-left: 96.58119658119658%;
+ *margin-left: 96.47481360247316%;
+ }
+ .row-fluid .offset11:first-child {
+ margin-left: 94.01709401709402%;
+ *margin-left: 93.91071103837061%;
+ }
+ .row-fluid .offset10 {
+ margin-left: 88.03418803418803%;
+ *margin-left: 87.92780505546462%;
+ }
+ .row-fluid .offset10:first-child {
+ margin-left: 85.47008547008548%;
+ *margin-left: 85.36370249136206%;
+ }
+ .row-fluid .offset9 {
+ margin-left: 79.48717948717949%;
+ *margin-left: 79.38079650845607%;
+ }
+ .row-fluid .offset9:first-child {
+ margin-left: 76.92307692307693%;
+ *margin-left: 76.81669394435352%;
+ }
+ .row-fluid .offset8 {
+ margin-left: 70.94017094017094%;
+ *margin-left: 70.83378796144753%;
+ }
+ .row-fluid .offset8:first-child {
+ margin-left: 68.37606837606839%;
+ *margin-left: 68.26968539734497%;
+ }
+ .row-fluid .offset7 {
+ margin-left: 62.393162393162385%;
+ *margin-left: 62.28677941443899%;
+ }
+ .row-fluid .offset7:first-child {
+ margin-left: 59.82905982905982%;
+ *margin-left: 59.72267685033642%;
+ }
+ .row-fluid .offset6 {
+ margin-left: 53.84615384615384%;