github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/public/static/js/patch_new.js (about) 1 mciModule.controller('PatchController', function($scope, $filter, $window, notificationService, $http) { 2 $scope.userTz = $window.userTz; 3 $scope.canEdit = $window.canEdit; 4 $scope.enabledTasks = _.pluck($window.tasks, "Name"); 5 6 var checkedProp = _.property("checked"); 7 8 // Event handler for when the user clicks on one of the variants 9 // in the left panel. Also accounts for special toggle behavior when the user 10 // is holding shift/meta/ctrl when clicking. 11 $scope.selectVariant = function($event, index){ 12 $event.preventDefault(); 13 if ($event.ctrlKey || $event.metaKey) { 14 // Ctrl/Meta+Click: Toggle just the variant being clicked. 15 $scope.variants[index].checked = !$scope.variants[index].checked; 16 } else if ($event.shiftKey) { 17 // Shift+Click: Select everything between the first element 18 // that's already selected element and the element being clicked on. 19 var firstCheckedIndex = _.findIndex($scope.variants, checkedProp); 20 firstCheckedIndex = Math.max(firstCheckedIndex, 0); // if nothing selected yet, start at 0. 21 var indexBounds = Array(firstCheckedIndex, index).sort(function(a, b){ 22 return a-b; 23 }) 24 for(var i=indexBounds[0]; i<=indexBounds[1]; i++){ 25 $scope.variants[i].checked = true; 26 } 27 } else { 28 // Regular click: Select *only* the one being clicked, and unselect all others. 29 for(var i=0; i<$scope.variants.length;i++){ 30 $scope.variants[i].checked = (i == index); 31 } 32 } 33 } 34 35 // Returns an object containing the total number of tasks to be scheduled, 36 // and the number of variants with at least 1 task to be scheduled. 37 // Return value is in the format format {numVariants: 3, numTasks: 25} 38 $scope.selectionCount = function(){ 39 var numVariants = _.filter($scope.variants, function(x){return _.filter(x.tasks, checkedProp).length > 0}).length; 40 var numTasks = _.reduce(_.map($scope.variants, function(x){return _.filter(x.tasks, checkedProp).length}), function(x, y){return x+y}, 0); 41 return {numVariants: numVariants, numTasks: numTasks}; 42 } 43 44 // Returns the number of tasks checked only within the specified variant. 45 // Used to populate the count badge next to each variant in the left panel 46 $scope.numSetForVariant = function(variantId){ 47 var v = _.find($scope.variants, function(x){return x.id == variantId}); 48 return _.filter(_.pluck(v.tasks, "checked"), _.identity).length; 49 } 50 51 // Gets the subset of variants that are active by the user 52 $scope.selectedVariants = function(){ 53 return _.filter($scope.variants, checkedProp); 54 } 55 56 $scope.isMac = function(){ 57 return navigator.platform.toUpperCase().indexOf('MAC')>=0; 58 } 59 60 61 62 // Gets the list of tasks that are active across all the list of currently 63 // selected variants, sorted by name. Used to populate the field of 64 // checkboxes in the main panel. 65 $scope.getActiveTasks = function(){ 66 var selectedVariants = $scope.selectedVariants(); 67 68 // return the union of the set of tasks shared by all of them, sorted by name 69 var tasksInSelectedVariants = _.uniq(_.flatten(_.map(_.pluck(selectedVariants, "tasks"), _.keys))); 70 return tasksInSelectedVariants.sort(); 71 } 72 73 // Click handler for "all" and "none" links. Applies the given state to all 74 // tasks within the current set of active variants 75 $scope.changeStateAll = function(state){ 76 var selectedVariantNames = _.object(_.map(_.pluck($scope.selectedVariants(), "id"), function(id){return [id, true]})); 77 var activeTasks = $scope.getActiveTasks(); 78 for(var i=0;i<$scope.variants.length;i++){ 79 var v = $scope.variants[i]; 80 if(!(v.id in selectedVariantNames)){ 81 continue; 82 } 83 _.each(activeTasks, function(taskName){ 84 if(_.has(v.tasks, taskName)){ 85 v.tasks[taskName].checked = state; 86 } 87 }) 88 } 89 } 90 91 // Sends the current patch config to the server to save. 92 $scope.save = function(){ 93 var data = { 94 "description": $scope.patch.Description, 95 "variants_tasks": _.filter(_.map($scope.variants, function(v){ 96 return { 97 variant: v.id, 98 tasks: _.keys(_.omit(v.tasks, function(v){return !v.checked})), 99 }; 100 }), function(v){return v.tasks.length > 0}) 101 } 102 $http.post('/patch/' + $scope.patch.Id, data). 103 success(function(data, status) { 104 window.location.replace("/version/" + data.version); 105 }). 106 error(function(data, status, errorThrown) { 107 notificationService.pushNotification('Error retrieving logs: ' + JSON.stringify(data), 'errorHeader'); 108 }); 109 }; 110 111 $scope.setPatchInfo = function() { 112 $scope.patch = $window.patch; 113 $scope.patchContainer = {'Patch':$scope.patch}; 114 var patch = $scope.patch; 115 var variantsFilteredTasks = _.mapObject($window.variants, function(v, k){ 116 v.Tasks = _.filter(v.Tasks, function(x){return _.contains($scope.enabledTasks, x.Name)}); 117 return v; 118 }) 119 $scope.variants = _.sortBy(_.map(variantsFilteredTasks, function(v, variantId){ 120 return { 121 id: variantId, 122 checked:false, 123 name: v.DisplayName, 124 tasks : _.object(_.map(_.pluck(v.Tasks, "Name"), function(t){ 125 return [t, {checked:false}]; 126 })) 127 }; 128 }), "name"); 129 130 // If there's only one variant, just pre-select it. 131 if($scope.variants.length == 1 ){ 132 $scope.variants[0].checked = true; 133 } 134 135 var allUniqueTaskNames = _.uniq(_.flatten(_.map(_.pluck($scope.variants, "tasks"), _.keys))); 136 137 $scope.tasks = _.object(_.map(allUniqueTaskNames, function(taskName){ 138 // create a getter/setter for the state of the task 139 return [taskName, function(newValue){ 140 var selectedVariants = $scope.selectedVariants(); 141 if(!arguments.length){ // called with no args, act as a getter 142 var statusAcrossVariants = _.flatten(_.map(_.pluck($scope.selectedVariants(), "tasks"), function(o){return _.filter(o, function(v, k){return k==taskName})})); 143 var groupCountedStatus = _.countBy(statusAcrossVariants, function(x){return x.checked == true}); 144 if(groupCountedStatus[true] == statusAcrossVariants.length ){ 145 return true; 146 }else if(groupCountedStatus[false] == statusAcrossVariants.length ){ 147 return false; 148 } 149 return null; 150 } 151 152 var selectedVariantNames = _.object(_.map(_.pluck(selectedVariants, "id"), function(id){return [id, true]})); 153 154 // act as a setter 155 for(var i=0;i<$scope.variants.length;i++){ 156 var v = $scope.variants[i]; 157 if(!(v.id in selectedVariantNames)){ 158 continue; 159 } 160 if(_.has(v.tasks, taskName)){ 161 v.tasks[taskName].checked = newValue; 162 } 163 } 164 return newValue; 165 }]; 166 })) 167 } 168 169 // Older patches may only have the fields "Variants" and "Tasks" but newer patches 170 // have a VariantsTasks field that has all pairs grouped together by variant. 171 // This function backfills the VariantsTasks field for older patches that were created 172 // before the schema change. 173 if(!patch.VariantsTasks && (patch.Tasks || []).length > 0 && (patch.BuildVariants || []).length > 0){ 174 patch.VariantsTasks = _.map(patch.BuildVariants, function(v){ 175 // The _intersection limits the set of tasks to be included 176 // to just the ones that actually exist in the config for that variant. 177 return {Variant:v, Tasks: _.intersection(_.pluck(($window.variants[v] || {}).Tasks , "Name"), patch.Tasks)}; 178 }); 179 } 180 181 $scope.setPatchInfo(); 182 183 // Populate the checkboxes in the UI according to the variants and tasks that 184 // were specified on the command line, or that may have already been created 185 // if the patch was finalized already. 186 if((patch.VariantsTasks || []).length>0){ 187 for(var i=0;i<patch.VariantsTasks.length;i++){ 188 var vt = patch.VariantsTasks[i]; 189 var variantIndex = _.findIndex($scope.variants, function(x){return x.id == patch.VariantsTasks[i].Variant}); 190 if(variantIndex >= 0 ){ 191 _.each(vt.Tasks, function(x){ 192 $scope.variants[variantIndex].tasks[x] = {checked:true}; 193 if(!!patch.Version){ 194 // if the task was already created, we can't uncheck the box 195 $scope.variants[variantIndex].tasks[x].disabled = true; 196 } 197 }) 198 } 199 } 200 } 201 })