Merge branch 'xos_auto_scaling_app_changes' of github.com:open-cloud/xos into xos_auto_scaling_app_changes
diff --git a/xos-apps/auto-scale/gui/env/srikanth.js b/xos-apps/auto-scale/gui/env/srikanth.js
new file mode 100644
index 0000000..0489fb9
--- /dev/null
+++ b/xos-apps/auto-scale/gui/env/srikanth.js
@@ -0,0 +1,3 @@
+module.exports = {
+  host: 'http://128.104.222.16:9991'
+}
\ No newline at end of file
diff --git a/xos-apps/auto-scale/gui/src/css/style.css b/xos-apps/auto-scale/gui/src/css/style.css
index 5074c4b..260b6df 100644
--- a/xos-apps/auto-scale/gui/src/css/style.css
+++ b/xos-apps/auto-scale/gui/src/css/style.css
@@ -1,3 +1,134 @@
 body {
   padding-top: 50px;
+}
+
+.list-group-item.active > a {
+  color: white;
+}
+
+/* ANIMATION */
+.animate-repeat{
+  /*background: red;*/
+}
+
+.animate-repeat.ng-move,
+.animate-repeat.ng-enter,
+.animate-repeat.ng-leave {
+  transition:all linear 0.5s;
+}
+
+/* Element Exit */
+.animate-repeat.ng-leave.ng-leave-active,
+.animate-repeat.ng-move,
+.animate-repeat.ng-enter {
+  /*opacity:0;*/
+  /*animation:1.5s bounceOutDown ease;*/
+}
+
+/* Element Enter */
+.animate-repeat.ng-leave,
+.animate-repeat.ng-move.ng-move-active,
+.animate-repeat.ng-enter.ng-enter-active {
+  /*opacity:1;*/
+  /*animation:1.5s bounceOutUp ease;*/
+}
+
+@keyframes bounceInUp {
+  from, 60%, 75%, 90%, to {
+    animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+  }
+
+  from {
+    opacity: 0;
+    transform: translate3d(0, 3000px, 0);
+  }
+
+  60% {
+    opacity: 1;
+    transform: translate3d(0, -20px, 0);
+  }
+
+  75% {
+    transform: translate3d(0, 10px, 0);
+  }
+
+  90% {
+    transform: translate3d(0, -5px, 0);
+  }
+
+  to {
+    transform: translate3d(0, 0, 0);
+  }
+}
+
+@keyframes bounceOutUp {
+  20% {
+    transform: translate3d(0, -10px, 0);
+  }
+
+  40%, 45% {
+    opacity: 1;
+    transform: translate3d(0, 20px, 0);
+  }
+
+  to {
+    opacity: 0;
+    transform: translate3d(0, -2000px, 0);
+  }
+}
+
+/* LOADER */
+.loader {
+  font-size: 10px;
+  margin: 150px auto;
+  text-indent: -9999em;
+  width: 11em;
+  height: 11em;
+  border-radius: 50%;
+  background: #ffffff;
+  background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  position: relative;
+  -webkit-animation: load3 1.4s infinite linear;
+  animation: load3 1.4s infinite linear;
+  -webkit-transform: translateZ(0);
+  -ms-transform: translateZ(0);
+  transform: translateZ(0);
+}
+.loader:before {
+  width: 50%;
+  height: 50%;
+  background: #105E9E;
+  border-radius: 100% 0 0 0;
+  position: absolute;
+  top: 0;
+  left: 0;
+  content: '';
+}
+.loader:after {
+  background: #fff;
+  width: 75%;
+  height: 75%;
+  border-radius: 50%;
+  content: '';
+  margin: auto;
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+}
+
+@keyframes load3 {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
 }
\ No newline at end of file
diff --git a/xos-apps/auto-scale/gui/src/js/autoscaling.service.js b/xos-apps/auto-scale/gui/src/js/autoscaling.service.js
index 5af613d..2762fe7 100644
--- a/xos-apps/auto-scale/gui/src/js/autoscaling.service.js
+++ b/xos-apps/auto-scale/gui/src/js/autoscaling.service.js
@@ -1,7 +1,7 @@
 'use strict';
 
 angular.module('autoscaling')
-.service('Autoscaling', function($http, $interval, $rootScope, lodash){
+.service('Autoscaling', function($http, $interval, $rootScope, lodash, $q){
 
   const pollingFrequency = 10;
   var pollinginterval;
@@ -31,15 +31,40 @@
     return list;
   };
 
-  this.getAutoscalingData = () => {
-    $http.get('/autoscaledata')
+  function requestData(url){
+
+    const deferred = $q.defer();
+
+    $http.get(url)
     .success((res) => {
-      $rootScope.$emit('autoscaling.update', this.formatData(res));
+      deferred.resolve(res);
+    })
+    .error((e) => {
+      deferred.reject(e);
     });
+
+    return deferred.promise;
+  };
+
+
+  // TODO Move to Websocket
+  this.getAutoscalingData = () => {
+
+    requestData('/autoscaledata')
+    .then((res) => {
+      $rootScope.$emit('autoscaling.update', this.formatData(res));
+    })
+    .catch((e) => {
+      $rootScope.$emit('autoscaling.error', this.formatData(e));
+    });
+
     pollinginterval = $interval(() => {
-      $http.get('/autoscaledata')
-      .success((res) => {
+      requestData('/autoscaledata')
+      .then((res) => {
         $rootScope.$emit('autoscaling.update', this.formatData(res));
+      })
+      .catch((e) => {
+        $rootScope.$emit('autoscaling.error', this.formatData(e));
       });
     }, pollingFrequency * 1000)
   };
diff --git a/xos-apps/auto-scale/gui/src/js/autoscaling_details.directive.js b/xos-apps/auto-scale/gui/src/js/autoscaling_details.directive.js
index 109e743..830b129 100644
--- a/xos-apps/auto-scale/gui/src/js/autoscaling_details.directive.js
+++ b/xos-apps/auto-scale/gui/src/js/autoscaling_details.directive.js
@@ -7,11 +7,30 @@
     controllerAs: 'vm',
     templateUrl: 'templates/service-container.tpl.html',
     controller: function($rootScope) {
+
+      this.loader = true;
+
+      // set to true when a service is manually selected
+      this.manualSelect = false;
+
+      // start polling
       Autoscaling.getAutoscalingData();
+
+      // list to polling events
       $rootScope.$on('autoscaling.update', (evt, data) => {
+        
+        if (data.length > 0) {
+          this.loader = false;
+        };
         this.printData(data);
       });
 
+      // handle errors
+      $rootScope.$on('autoscaling.error', (evt, err) => {
+        this.loader = false;
+        this.error = err.data.message;
+      });
+
       /**
       * Group resources by service and slice
       */
@@ -26,14 +45,30 @@
             // so take them out of an array
             // and keep only the sample data
             lodash.forEach(Object.keys(this.services[service][slice]), (instance) => {
-              // console.log(this.services[service][slice][instance]);
+              // TODO maintain the instance order in the array
               this.services[service][slice][instance] = this.services[service][slice][instance][0].queue;
             });
             
           })
         });
         // arbitrary set the first service in the list as the selected one
-        this.selectedService = this.services[Object.keys(this.services)[0]];
+        if(!this.manualSelect){
+          this.serviceName = Object.keys(this.services)[0];
+          this.selectedService = this.services[Object.keys(this.services)[0]];
+        }
+        else{
+          this.selectedService = this.services[this.serviceName]
+        }
+      };
+
+      /**
+      * Change the current selected service
+      */
+     
+      this.selectService = (serviceName) => {
+        this.serviceName = serviceName;
+        this.selectedService = this.services[serviceName];
+        this.manualSelect = true;
       };
     }
   };
@@ -61,9 +96,27 @@
     bindToController: true,
     controllerAs: 'vm',
     templateUrl: 'templates/slice-detail.tpl.html',
-    controller: function($scope) {
+    controller: function($scope, $timeout) {
 
-      this.chart = {};
+      this.chart = {
+        options: {
+          datasetFill: false,
+          animation: true,
+          // animationEasing: 'easeInBack'
+        }
+      };
+
+      this.chartColors = [
+        '#286090',
+        '#F7464A',
+        '#46BFBD',
+        '#FDB45C',
+        '#97BBCD',
+        '#4D5360',
+        '#8c4f9f'
+      ];
+
+      Chart.defaults.global.colours = this.chartColors;
 
       /**
       * Goes trough the array and format date to be used as labels
@@ -73,46 +126,72 @@
       */
 
       this.getLabels = (data) => {
-        return data.reduce((list, item) => {
-          let date = new Date(item.timestamp);
-          list.push(`${date.getHours()}:${(date.getMinutes()<10?'0':'') + date.getMinutes()}:${date.getSeconds()}`);
-          return list;
-        }, []);
+        // we should compare the  labels and get the last available
+        return this.prependValues(
+          data.reduce((list, item) => {
+            let date = new Date(item.timestamp);
+            list.push(`${date.getHours()}:${(date.getMinutes()<10?'0':'') + date.getMinutes()}:${date.getSeconds()}`);
+            return list;
+          }, [])
+        , '');
       };
 
       /**
+      * Prepend value if the array is less than 10 element
+      */
+      this.prependValues = (list, value) => {
+        if(list.length < 10){
+          list.unshift(value);
+          // call itself to check again
+          return this.prependValues(list, value);
+        }
+        return list;
+      }
+
+      /**
       * Convert an object of array,
       * in an array of arrays of values
       */
       this.getData = (data, instanceNames) => {
         return lodash.map(instanceNames, (item) => {
-          return lodash.reduce(data[item], (list, sample) => {
+          return this.prependValues(lodash.reduce(data[item], (list, sample) => {
             // console.log(data[item], sample);
             list.push(sample.counter_volume);
             return list;
-          }, []);
+          }, []), null);
         });
       };
 
+      this.getMostRecentSeries = (instances) => {
+        // console.log(instances);
+        const newestValues = [];
+        instances = lodash.toArray(instances)
+        lodash.forEach(instances, (values) => {
+          newestValues.push(lodash.max(values, item => new Date(item.timestamp)));
+        });
+
+        var highestValue = 0;
+        var newestInstanceIndex = lodash.findIndex(newestValues, (val) => {
+          return new Date(val.timestamp) > highestValue;
+        });
+
+        return instances[newestInstanceIndex]
+      }
+
       this.drawChart = (data) => {
 
         const instanceNames = Object.keys(data);
 
-        this.chart.labels = this.getLabels(data[instanceNames[0]]);
+        this.chart.labels = this.getLabels(this.getMostRecentSeries(data));
         this.chart.series = instanceNames;
         this.chart.data = this.getData(data, instanceNames);
-
-        console.log(this.getData(data, instanceNames));
       }
 
-      $scope.$watch(() => this.instances, (val) => {this.drawChart(val)})
+      $scope.$watch(() => this.instances, (val) => {
+        $timeout(()=>{this.chart.options.animation = false}, 1000);
+        this.drawChart(val)
+      });
 
     }
   };
-});
-
-//  TODO
-//  [x] repeat service name in a menu
-//  [x] create a directive that receive a service
-//  [ ] print a chart for every slice
-//  [ ] print a line in the chart for every instance
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/xos-apps/auto-scale/gui/src/templates/service-container.tpl.html b/xos-apps/auto-scale/gui/src/templates/service-container.tpl.html
index e5b964c..34cdcca 100644
--- a/xos-apps/auto-scale/gui/src/templates/service-container.tpl.html
+++ b/xos-apps/auto-scale/gui/src/templates/service-container.tpl.html
@@ -3,16 +3,24 @@
     <h1>Service detail</h1>
   </div>
 </div>
-
 <div class="row">
+  <div class="col-sm-12" ng-show="vm.loader">
+    <div class="loader"></div>
+  </div>
   <div class="col-sm-3">
     <ul class="list-group">
-      <li class="list-group-item" ng-repeat="(service, slices) in vm.services">
-        <a href="">{{service}}</a>
+      <li class="list-group-item" ng-class="{active: vm.serviceName == service}" ng-repeat="(service, slices) in vm.services">
+        <a href="" ng-click="vm.selectService(service)">{{service}}</a>
       </li>
     </ul>
   </div>
-  <div class="col-sm-9">
+
+  <div class="col-sm-9 repeat-container" ng-show="!vm.error">
     <service-detail service="vm.selectedService"></service-detail>
   </div>
+  <div class="col-sm-9" ng-show="vm.error">
+    <div class="alert alert-danger">
+      {{vm.error}}
+    </div>
+  </div>
 </div>
\ No newline at end of file
diff --git a/xos-apps/auto-scale/gui/src/templates/service-detail.tpl.html b/xos-apps/auto-scale/gui/src/templates/service-detail.tpl.html
index 6bd5406..f0e3636 100644
--- a/xos-apps/auto-scale/gui/src/templates/service-detail.tpl.html
+++ b/xos-apps/auto-scale/gui/src/templates/service-detail.tpl.html
@@ -1,5 +1,4 @@
-<!-- <pre>{{vm.service | json}}</pre> -->
-<div class="panel panel-default" ng-repeat="(slice, instances) in vm.service">
+<div class="animate-repeat panel panel-default" ng-repeat="(slice, instances) in vm.service">
   <div class="panel-heading">
     <h3 class="panel-title">{{slice}}</h3>
   </div>
diff --git a/xos-apps/auto-scale/gui/src/templates/slice-detail.tpl.html b/xos-apps/auto-scale/gui/src/templates/slice-detail.tpl.html
index d950afe..6e63d5b 100644
--- a/xos-apps/auto-scale/gui/src/templates/slice-detail.tpl.html
+++ b/xos-apps/auto-scale/gui/src/templates/slice-detail.tpl.html
@@ -1,3 +1,4 @@
 <canvas id="line" class="chart chart-line" chart-data="vm.chart.data"
-  chart-labels="vm.chart.labels" chart-legend="true" chart-series="vm.chart.series">
+  chart-labels="vm.chart.labels" chart-legend="true" chart-series="vm.chart.series" 
+  chart-options="vm.chart.options">
 </canvas>
\ No newline at end of file