From 0f9507be5bc87bdef40b3f34d6514bd888ee2abe Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jordan=20Aug=C3=A9?= Date: Fri, 8 Aug 2014 11:28:11 +0200 Subject: [PATCH] Improved testbed plugin to support facility_name and testbed_name filters --- manifoldapi/static/js/manifold.js | 2 +- manifoldapi/static/js/plugin.js | 102 ++++++++++++++++++ plugins/testbeds/__init__.py | 6 +- plugins/testbeds/static/js/testbeds.js | 130 +++++++++++++++++++---- plugins/testbeds/templates/testbeds.html | 28 +++-- portal/sliceresourceview.py | 36 ++++--- 6 files changed, 260 insertions(+), 44 deletions(-) diff --git a/manifoldapi/static/js/manifold.js b/manifoldapi/static/js/manifold.js index 59cb4bb9..74fb5d9d 100644 --- a/manifoldapi/static/js/manifold.js +++ b/manifoldapi/static/js/manifold.js @@ -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"); } diff --git a/manifoldapi/static/js/plugin.js b/manifoldapi/static/js/plugin.js index 22e4be52..82a3cd03 100644 --- a/manifoldapi/static/js/plugin.js +++ b/manifoldapi/static/js/plugin.js @@ -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 diff --git a/plugins/testbeds/__init__.py b/plugins/testbeds/__init__.py index c72f6676..436dc0ee 100644 --- a/plugins/testbeds/__init__.py +++ b/plugins/testbeds/__init__.py @@ -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 diff --git a/plugins/testbeds/static/js/testbeds.js b/plugins/testbeds/static/js/testbeds.js index e21446cf..e628a679 100644 --- a/plugins/testbeds/static/js/testbeds.js +++ b/plugins/testbeds/static/js/testbeds.js @@ -20,37 +20,115 @@ /* 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); } }; @@ -80,7 +158,6 @@ this._super(options, element); /* Member variables */ - this.filters = Array(); this.testbeds = Array(); this._get_scope().instance = this; @@ -94,6 +171,7 @@ /* 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); @@ -125,17 +203,30 @@ } } }, - + */ // ... 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); @@ -143,6 +234,9 @@ $.each(scope.testbeds, function(i, testbed) { testbed.active = true }); scope.$apply(); }, +*/ + + /* INTERNAL FUNCTIONS */ _get_scope : function() { diff --git a/plugins/testbeds/templates/testbeds.html b/plugins/testbeds/templates/testbeds.html index 22f7f0ac..296e5efb 100644 --- a/plugins/testbeds/templates/testbeds.html +++ b/plugins/testbeds/templates/testbeds.html @@ -1,24 +1,34 @@
-
Testbeds
+
Facilities
-
+
- {[{ testbed.platform }]} - + ng-class="{active: is_facility_active(facility_name)}" + id="facility-filter_{[{ facility_name }]}" + ng-click="select_facility(facility_name)" + data-platform="{[{ facility_name }]}"> + {[{ facility_name }]} +
+ +   {[{testbed_name}]} + +
+
diff --git a/portal/sliceresourceview.py b/portal/sliceresourceview.py index e887ff7a..7f3c2d66 100644 --- a/portal/sliceresourceview.py +++ b/portal/sliceresourceview.py @@ -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( -- 2.43.0