Improved testbed plugin to support facility_name and testbed_name filters
authorJordan Augé <jordan.auge@lip6.fr>
Fri, 8 Aug 2014 09:28:11 +0000 (11:28 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Thu, 14 Aug 2014 13:37:12 +0000 (15:37 +0200)
manifoldapi/static/js/manifold.js
manifoldapi/static/js/plugin.js
plugins/testbeds/__init__.py
plugins/testbeds/static/js/testbeds.js
plugins/testbeds/templates/testbeds.html
portal/sliceresourceview.py

index 59cb4bb..74fb5d9 100644 (file)
@@ -671,7 +671,7 @@ function QueryStore() {
         });
 
         var end = new Date().getTime();
-        console.log("APPLY FILTERS took", end - start, "ms");
+        console.log("APPLY FILTERS [", filters, "] took", end - start, "ms");
 
     }
 
index 22e4be5..82a3cd0 100644 (file)
@@ -19,6 +19,108 @@ ManifoldApp.filter('offset', function() {
   };
 });
 
+// http://stackoverflow.com/questions/19992090/angularjs-group-by-directive
+ManifoldApp.filter('groupBy', ['$parse', function ($parse) {
+    return function (list, group_by) {
+
+        var filtered = [];
+        var prev_item = null;
+        var group_changed = false;
+        // this is a new field which is added to each item where we append "_CHANGED"
+        // to indicate a field change in the list
+        //was var new_field = group_by + '_CHANGED'; - JB 12/17/2013
+        var new_field = 'group_by_CHANGED';
+
+        // loop through each item in the list
+        angular.forEach(list, function (item) {
+
+            group_changed = false;
+
+            // if not the first item
+            if (prev_item !== null) {
+
+                // check if any of the group by field changed
+
+                //force group_by into Array
+                group_by = angular.isArray(group_by) ? group_by : [group_by];
+
+                //check each group by parameter
+                for (var i = 0, len = group_by.length; i < len; i++) {
+                    if ($parse(group_by[i])(prev_item) !== $parse(group_by[i])(item)) {
+                        group_changed = true;
+                    }
+                }
+
+
+            }// otherwise we have the first item in the list which is new
+            else {
+                group_changed = true;
+            }
+
+            // if the group changed, then add a new field to the item
+            // to indicate this
+            if (group_changed) {
+                item[new_field] = true;
+            } else {
+                item[new_field] = false;
+            }
+
+            filtered.push(item);
+            prev_item = item;
+
+        });
+
+        return filtered;
+    };
+}]);
+
+// https://github.com/angular-ui/angular-ui-OLDREPO/blob/master/modules/filters/unique/unique.js
+/**
+ * Filters out all duplicate items from an array by checking the specified key
+ * @param [key] {string} the name of the attribute of each object to compare for uniqueness
+ if the key is empty, the entire object will be compared
+ if the key === false then no filtering will be performed
+ * @return {array}
+ */
+ManifoldApp.filter('unique', function () {
+
+  return function (items, filterOn) {
+
+    if (filterOn === false) {
+      return items;
+    }
+
+    if ((filterOn || angular.isUndefined(filterOn)) && angular.isArray(items)) {
+      var hashCheck = {}, newItems = [];
+
+      var extractValueToCompare = function (item) {
+        if (angular.isObject(item) && angular.isString(filterOn)) {
+          return item[filterOn];
+        } else {
+          return item;
+        }
+      };
+
+      angular.forEach(items, function (item) {
+        var valueToCheck, isDuplicate = false;
+
+        for (var i = 0; i < newItems.length; i++) {
+          if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) {
+            isDuplicate = true;
+            break;
+          }
+        }
+        if (!isDuplicate) {
+          newItems.push(item);
+        }
+
+      });
+      items = newItems;
+    }
+    return items;
+  };
+});
+
 // INHERITANCE
 // http://alexsexton.com/blog/2010/02/using-inheritance-patterns-to-organize-large-jquery-applications/
 // We will use John Resig's proposal
index c72f667..436dc0e 100644 (file)
@@ -2,13 +2,11 @@ from unfold.plugin import Plugin
 
 class TestbedsPlugin(Plugin):
     
-    def __init__ (self, query=None, query_networks=None, **settings):
+    def __init__ (self, query=None, **settings):
         Plugin.__init__ (self, **settings)
 
         # Until we have a proper way to access queries in Python
         self.query              = query
-        self.query_networks          = query_networks
-        self.query_networks_uuid     = query_networks.query_uuid if query_networks else None
 
     def template_file (self):
         return "testbeds.html"
@@ -28,7 +26,7 @@ class TestbedsPlugin(Plugin):
         # query_uuid will pass self.query results to the javascript
         # and will be available as "record" in :
         # on_new_record: function(record)
-        return ['plugin_uuid', 'domid', 'query_uuid', 'query_networks_uuid']
+        return ['plugin_uuid', 'domid', 'query_uuid']
 
     def export_json_settings (self):
         return True
index e21446c..e628a67 100644 (file)
         /* Plugin instance */
         $scope.instance = null;
 
+        $scope.facility_names = Array();
+        $scope.testbed_names = new Object();
+
         /* Models */
-        $scope.testbeds = Array();
+        //$scope.testbeds = Array();
+        $scope._facility_active = new Object();
+        $scope._testbed_active  = new Object();
+
+        $scope.is_facility_active = function(facility)
+        {
+            return (($scope._facility_active[facility] === undefined) || $scope._facility_active[facility]);
+        };
+
+        $scope.is_testbed_active = function(facility, testbed)
+        {
+            return (($scope._testbed_active[facility] === undefined) || 
+                    ($scope._testbed_active[facility][testbed] === undefined) || 
+                    ($scope._testbed_active[facility][testbed]));
+        };
 
+        $scope.set_facility_active = function(facility, value)
+        {
+            $scope._facility_active[facility] = value;
+        };
+
+        $scope.set_testbed_active = function(facility, testbed, value)
+        {
+            if ($scope._testbed_active[facility] === undefined)
+                $scope._testbed_active[facility] = new Object();
+            $scope._testbed_active[facility][testbed] = value;
+        };
+    
         /* Click event */
-        $scope.select = function(testbed)
+
+        $scope.select_facility = function(facility)
         {
             var selected, prev_selected, num, num_selected, num_prev_selected, filter;
 
-            prev_selected = $.map($scope.testbeds, function(x, i) {
-                return x.active ? x.network_hrn : null;
+            prev_selected = $.map($scope.facility_names, function(x, i) {
+                return $scope.is_facility_active(x) ? x : null;
             });
 
-            testbed.active = !testbed.active;
+            $scope.set_facility_active(facility, ! $scope.is_facility_active(facility));
 
-            selected = $.map($scope.testbeds, function(x, i) {
-                return x.active ? x.network_hrn : null;
+            selected = $.map($scope.facility_names, function(x, i) {
+                return $scope.is_facility_active(x) ? x : null;
             });
 
-            num = $scope.testbeds.length;
+            num = $scope.facility_names.length;
             prev_num_selected = prev_selected.length;
             num_selected = selected.length;
 
-            
             if ((prev_num_selected != 0) && (prev_num_selected != num)) {
                 // Remove previous filter
-                filter = ['network_hrn', 'included', prev_selected];
+                filter = ['facility_name', 'included', prev_selected];
                 manifold.raise_event($scope.instance.options.query_uuid, FILTER_REMOVED, filter);
             }
 
             if (num_selected != num) {
-                filter = ['network_hrn', 'included', selected];
+                filter = ['facility_name', 'included', selected];
+                manifold.raise_event($scope.instance.options.query_uuid, FILTER_ADDED, filter);
+            }
+        };
+
+        $scope.select_testbed = function(facility, testbed)
+        {
+            var selected, prev_selected, num, num_selected, num_prev_selected, filter;
+
+            prev_selected = Array();
+            $.each($scope.facility_names, function(i, facility_name) {
+                $.each($scope.testbed_names[facility_name], function(j, testbed_name) {
+                    if ($scope.is_testbed_active(facility_name, testbed_name)) {
+                        // XXX We should have a joint facility/testbed filter
+                        prev_selected.push(testbed_name);
+                    }
+                });
+
+            });
+
+            $scope.set_testbed_active(facility, testbed, ! $scope.is_testbed_active(facility, testbed));
+
+            selected = Array();
+            $.each($scope.facility_names, function(i, facility_name) {
+                $.each($scope.testbed_names[facility_name], function(j, testbed_name) {
+                    if ($scope.is_testbed_active(facility_name, testbed_name)) {
+                        // XXX We should have a joint facility/testbed filter
+                        selected.push(testbed_name);
+                    }
+                });
+
+            });
+
+            num = 0;
+            $.each($scope.facility_names, function(i, facility_name) {
+                num += $scope.testbed_names[facility_name].length;
+            });
+            prev_num_selected = prev_selected.length;
+            num_selected = selected.length;
+
+            if ((prev_num_selected != 0) && (prev_num_selected != num)) {
+                // Remove previous filter
+                // XXX We should have a joint facility/testbed filter
+                filter = ['testbed_name', 'included', prev_selected];
+                manifold.raise_event($scope.instance.options.query_uuid, FILTER_REMOVED, filter);
+            }
+
+            if (num_selected != num) {
+                // XXX We should have a joint facility/testbed filter
+                filter = ['testbed_name', 'included', selected];
                 manifold.raise_event($scope.instance.options.query_uuid, FILTER_ADDED, filter);
             }
         };
             this._super(options, element);
 
             /* Member variables */
-            this.filters = Array();
             this.testbeds = Array();
 
             this._get_scope().instance = this;
         /* HANDLERS */
 
         /* When a filter is added/removed, update the list of filters local to the plugin */
+        /*
         on_filter_added: function(filter)
         {
             this.filters.push(filter);
                 }
             }
         },
-
+        */
         // ... be sure to list all events here
 
-        on_networks_query_done: function()
+        on_query_done: function()
         {
-             this.set_networks();
-        },
+            var scope, query_ext, resources;
+            scope = this._get_scope();
+            query_ext = manifold.query_store.find_analyzed_query_ext(this.options.query_uuid);
+            resources = query_ext.records.values();
+
+            $.each(resources, function(i, resource) {
+                if ($.inArray(resource.facility_name, scope.facility_names) == -1)
+                    scope.facility_names.push(resource.facility_name);
+                if (scope.testbed_names[resource.facility_name] === undefined)
+                    scope.testbed_names[resource.facility_name] = Array();
+                if ($.inArray(resource.testbed_name, scope.testbed_names[resource.facility_name]) == -1)
+                    scope.testbed_names[resource.facility_name].push(resource.testbed_name);
+            });
 
-        /* INTERNAL FUNCTIONS */
+            scope.$apply();
+        },
 
-        set_networks: function()
+        /*
+        on_networks_query_done: function()
         {
             var scope = this._get_scope();
             var query_ext = manifold.query_store.find_analyzed_query_ext(this.options.query_networks_uuid);
             $.each(scope.testbeds, function(i, testbed) { testbed.active = true });
             scope.$apply();
         },
+*/
+
+        /* INTERNAL FUNCTIONS */
 
         _get_scope : function()
         {
index 22f7f0a..296e5ef 100644 (file)
@@ -1,24 +1,34 @@
 <div id={{ domid }} ng-controller="TestbedsCtrl">
 
-<div class="list-group-item sl-platform"><span class="list-group-item-heading">Testbeds</span></div>
+<div class="list-group-item sl-platform"><span class="list-group-item-heading">Facilities</span></div>
 
-<div ng-repeat="testbed in testbeds"
-     ng-click="select(testbed)">
+<div ng-repeat="facility_name in facility_names">
        <a href="#" 
           class="list-group-item sl-platform"
-       ng-class="{active: testbed.active}"
-          id="testbeds-filter_{[{ testbed.network_hrn }]}"
-          data-platform="{[{ testbed.network_hrn }]}">
-       <span class="list-group-item-heading">{[{ testbed.platform }]}</span>
-       <!--<p class="list-group-item-heading">{[{ testbed.network_hrn }]}</p>-->
+          ng-class="{active: is_facility_active(facility_name)}"
+          id="facility-filter_{[{ facility_name }]}"
+          ng-click="select_facility(facility_name)"
+          data-platform="{[{ facility_name }]}">
+       <span class="list-group-item-heading">{[{ facility_name }]}</span>
        </a>
+       <div ng-repeat="testbed_name in testbed_names[facility_name]">
+               <a href="#" 
+                  class="list-group-item sl-platform"
+                  ng-class="{active: is_testbed_active(facility_name, testbed_name)}"
+                  id="testbeds-filter_{[{ testbed_name }]}"
+                ng-click="select_testbed(facility_name, testbed_name)"
+                  data-platform="{[{ testbed_name }]}">
+               <span class="list-group-item-heading">&nbsp;&nbsp;{[{testbed_name}]}</span>
+               </a>
+       </div>
 </div>
 
+<!--
 <style>
 a.sl-platform  {
     text-transform: uppercase;
 }
 
 </style>
-
+-->
 </div>
index e887ff7..7f3c2d6 100644 (file)
@@ -52,15 +52,27 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         # Example: select slice_hrn, resource.urn, lease.resource, lease.start_time, lease.end_time from slice where slice_hrn == "ple.upmc.myslicedemo"
         main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
         main_query.select(
-                'slice_urn', # XXX We need the key otherwise the storage of records bugs !
+                # SLICE
                 'slice_hrn',
+                # - The record key is needed otherwise the storage of records
+                #   bugs !
+                'slice_urn',
+                # RESOURCES
                 'resource.urn',
                 'resource.hostname', 'resource.type',
                 'resource.network_hrn',
+                # - The facility_name and testbed_name are required for the
+                #   testbeds plugin to properly work.
+                'resource.facility_name', 
+                'resource.testbed_name',
+                # LEASES
                 'lease.resource',
                 'lease.start_time',
                 'lease.end_time',
-                'lease.lease_id', # Important for NITOS identify already existing leases
+                # - The lease_id is important for NITOS identify already existing
+                #   leases
+                'lease.lease_id', 
+
                 #'user.user_hrn',
                 #'application.measurement_point.counter'
         )
@@ -206,22 +218,22 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         network_md = metadata.details_by_object('network')
         network_fields = [column['name'] for column in network_md['column']]
 
-        query_networks = Query.get('network').select(network_fields)
-        page.enqueue_query(query_networks)
+        #query_networks = Query.get('network').select(network_fields)
+        #page.enqueue_query(query_networks)
 
         filter_testbeds = TestbedsPlugin(
             page            = page,
             domid           = 'testbeds-filter',
             title           = 'Filter by testbeds',
             query           = sq_resource,
-            query_networks  = query_networks,
-            init_key        = "network_hrn",
-            checkboxes      = True,
-            datatables_options = {
-                'iDisplayLength': 25,
-                'bLengthChange' : True,
-                'bAutoWidth'    : True,
-                },
+            #query_networks  = query_networks,
+            #init_key        = "network_hrn",
+            #checkboxes      = True,
+            #datatables_options = {
+            #    'iDisplayLength': 25,
+            #    'bLengthChange' : True,
+            #    'bAutoWidth'    : True,
+            #    },
         )
 
         filter_status = FilterStatusPlugin(