luma: Polling service stability and polling UI fix

Previously the polling service would halt if encountering
an error any time in the token and HIT allocation pipeline.
This fix allows polling to log the errors and continue
trying to allocate HITs for available devices and apps.

Change-Id: Ic778e9aea11948d914286cbd18115c317f4f07d6
diff --git a/crowdstf-manager/config-example.json b/crowdstf-manager/config-example.json
index 9f85f8b..f754044 100644
--- a/crowdstf-manager/config-example.json
+++ b/crowdstf-manager/config-example.json
@@ -58,7 +58,7 @@
     // Include a %s to substitute the task time.
     "title": "Do something fun for %s minutes",
     "keywords": "usability, ui, app, android",
-    "assignmentDurationInSeconds": 1800,
+    "assignmentDurationInSeconds": 900,
     "rewardDollarsPerMinute": 0.30,
     "autoApprovalDelayInSeconds": 259200,
     "currencyCode": "USD",
diff --git a/crowdstf-manager/services/pollingService.js b/crowdstf-manager/services/pollingService.js
index a8c945e..af596ad 100644
--- a/crowdstf-manager/services/pollingService.js
+++ b/crowdstf-manager/services/pollingService.js
@@ -112,10 +112,19 @@
     stfService.generateToken(device.serial, TASK_MINS, function(err, tokenObj) {
       if (err) {
         console.error('Error generating token', err);
-        return setTimeout(pollingService.poll, POLL_INTERVAL);
-      }
 
-      tokens.push(tokenObj);
+        // Continue with the rest despite the error.
+        if (unusedDevices.length) {
+          return getToken(unusedDevices.pop());
+        }
+
+        // If we didn't issue tokens, return to polling.
+        if (!tokens.length) {
+          return setTimeout(pollingService.poll, POLL_INTERVAL);
+        }
+      } else {
+        tokens.push(tokenObj);
+      }
 
       if (unusedDevices.length) {
         return getToken(unusedDevices.pop());
@@ -125,6 +134,21 @@
               function done(err) {
                 if (err) {
                   console.error('Error creating hit', err);
+
+                  // Fallback and undo token allocation.
+                  stfService.deleteToken(tk.token, function(err) {
+                    if (err) {
+                      console.error('Error deleting token for bad hit', err);
+                    } else {
+                      console.info('Deleted token for bad HIT');
+                    }
+                  });
+
+                  // Continue creating HITs.
+                  if (tokens.length > 0) {
+                    return createHit(tokens.pop());
+                  }
+
                   return setTimeout(pollingService.poll, POLL_INTERVAL);
                 }
 
diff --git a/crowdstf-manager/services/turkHitService.js b/crowdstf-manager/services/turkHitService.js
index aa1534c..b96cff7 100644
--- a/crowdstf-manager/services/turkHitService.js
+++ b/crowdstf-manager/services/turkHitService.js
@@ -71,7 +71,7 @@
     return;
   }
 
-  var hitTitle = config.hitTitle.replace(/%s/g, taskMinutes);
+  var hitTitle = config.hit.title.replace(/%s/g, taskMinutes);
   var rewardPrice = taskMinutes * config.hit.rewardDollarsPerMinute;
   var hitHTML = jadeTemplate({
     token: token,
diff --git a/crowdstf-manager/views/modules/tokens.jade b/crowdstf-manager/views/modules/tokens.jade
index dd5c2c6..5dce6fc 100644
--- a/crowdstf-manager/views/modules/tokens.jade
+++ b/crowdstf-manager/views/modules/tokens.jade
@@ -39,7 +39,7 @@
         | {{' '}}minute token{{' '}}
         button.btn.btn-info(ng-click='generateToken(device.serial)') Get
       span(ng-if='device.token && device.tokenStatus === "unused"')
-      = 'Token Allocated'
+        = 'Token Allocated'
 
   h5 Allocated Tokens
   ul(ng-repeat='tokenObj in tokens | filter:{status:"unused"}')
diff --git a/crowdstf-manager/views/task.jade b/crowdstf-manager/views/task.jade
index b3b3db6..95f6473 100644
--- a/crowdstf-manager/views/task.jade
+++ b/crowdstf-manager/views/task.jade
@@ -8,7 +8,7 @@
 - var xmlns = 'http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchem';
 - xmlns += 'as/2011-11-11/HTMLQuestion.xsd';
 - var bstp = 'https://s3.amazonaws.com/mturk-public/bs30/css/bootstrap.min.css';
-- var logins = logins || {facebook:{}, gmail:{}, twitter:{}};
+- var logins = locals.logins || {facebook:{}, gmail:{}, twitter:{}};
 
 HTMLQuestion(xmlns=xmlns)
   HTMLContent<![CDATA[<!DOCTYPE html>