Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Cs 2517 madison enhancement #34

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions app/assets/javascripts/angular/controllers/madison_ui_ctrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

var app = angular.module('madison');

var SLIDER_STEP = 5;
app.controller('MainUICtrl', ['$scope', '$routeParams', 'Requirement', function($scope, $routeParams, Requirement) {

$scope.requirements = Requirement.query({challenge_id: $routeParams.challenge_id});
Expand All @@ -15,6 +16,203 @@ app.controller('MainUICtrl', ['$scope', '$routeParams', 'Requirement', function(
{name: "Generic", value: "All"}
];

// determine which section will be shown on Weight tab.
$scope.showWeightSection = function(section) {
// shows sections if use_advanced_features is checked.
if($scope.use_advanced_features && section !== "all")
return true;

// shows section named 'all', which contains all questions, if use_advanced_features is not checked.
if(!$scope.use_advanced_features && section === "all")
return true;

return false;
};

// In order to use Weight tab pannel view,
// prepare reqsBySection(requirements grouped By Section) model,
// section is the one used when 'use_advanced_features' is on.
// section named 'all' is the special one, which contains all questions, and is used
// when 'use_advanced_features' is off.
$scope.prepareForWeight = function() {
var reqsBySection = {};

reqsBySection["all"] = [];
angular.forEach($scope.requirements, function(req) {
reqsBySection[req.section] = reqsBySection[req.section] || [];

reqsBySection[req.section].push({
id: req.id,
description: req.description,
weight: req.weight * 100,
isLocked: false
});

reqsBySection["all"].push({
id: req.id,
description: req.description,
weight: req.weight * 100,
isLocked: false
})
});

$scope.reqsBySection = reqsBySection;

for(var section in reqsBySection) {
// adjust weights just to make sure the total weights of the group is 100
adjustWeight(section, reqsBySection[section][0]);
}
};

// adjusts weights of questions in the group so that make sure the total of weights are 100.
// 1. calculate overflow/underflow numbers of weights.
// 2. And also gather target questions to decrease/increase weight.
// 3. In case of overflow, decrease the weights of target.
// 4. In case of underflow, increase the weights of target.
// 5. So as a result, the total number of weight goes 100.
//
// Increasing/Decreasing Rule is based on round robin.
//
function adjustWeight(section, reqId) {
var reqs = $scope.reqsBySection[section];
var initiator = null;
var sum = 0;
var max = 100;
var targets = [];

reqs.forEach(function(req) {
sum += req.weight;

if(req.isLocked) {
max -= req.weight;
}

if(req.id === reqId) {
initiator = req;
}

if(req.id !== reqId && req.isLocked == false) {
targets.push(req);
}

});

if(targets.length === 0) {
initiator.weight = max;
return;
}

if(sum > 100) {
// slider value increased
decreaseTarget(sum-100)
} else {
// slider value decreased
increaseTarget(100-sum)
}

function decreaseTarget(value) {
while(value > 0) {
if(targets.length === 0) {
// when no other targets exits, decrease initiator.
initiator.weight -= value;
value = 0;
continue;
}

var step = SLIDER_STEP;
targets.forEach(function(req) {

if(req.weight < step) {
// this requirement does not have any room to decrease.
targets.splice(targets.indexOf(req), 1);
return;
}

if(value > 0) {
req.weight -= step;
value -= step;
}
});
}
}

function increaseTarget(value) {
while(value > 0) {
if(targets.length === 0) {
// when no other targets exits, increase initiator.
initiator.weight += value;
value = 0;
continue;
}

var step = SLIDER_STEP;
targets.forEach(function(req) {
if(value > 0) {
req.weight += step;
value -= step;
}
});
}
}

}

// save requirements of section.
function saveWeight(section) {
var reqs = $scope.reqsBySection[section];

reqs.forEach(function(req) {
$scope.requirements.forEach(function(orgReq) {
if(req.id === orgReq.id) {
orgReq.weight = req.weight/100;
orgReq.$update();
}
})
});
}

// slider options
$scope.sliderOptions = {
min: 0,
max: 100,
step: SLIDER_STEP,
stop: function(evt, ui) {
// triggered after sliding event finished.
var section = $(evt.target).data("section");
var reqId = $(evt.target).data("req-id");

adjustWeight(section, reqId);

// needs digest to updated adjusted weights.
$scope.$digest();

saveWeight(section);
}
}

// sorting options
$scope.sortableOptions = {
axis: 'y',
cursor: "move",
opacity: 0.7,
revert: 200,
placeholder: "well", // class name of drop target
stop: function(e, ui) {
// save sorting result.
var requirements = $scope.requirements;
for(var i=0; i< requirements.length; i++) {
var r = requirements[i];
// console.log("No.", i+1, r.description, "order = ", r.order_by);

if(r.order_by === i+1) { continue; }

// request will be sent only for the changed requirements.
r.order_by = i+1;
r.$update();
}
},
};

$scope.addFromLibrary = function() {
Requirement.query({library: $scope.library.value}, function(requirements) {
angular.forEach(requirements, function(req){
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/angular/madison.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Our module will be called 'app'.
*/
angular.module('madison', ['ngResource'])
angular.module('madison', ['ngResource', 'ui.sortable', 'ui.slider'])
.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
.when(
Expand Down
109 changes: 68 additions & 41 deletions app/assets/javascripts/angular/templates/madison_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,16 @@ <h1>Madison Requirements Builder</h1>
<ul class="nav nav-tabs">
<li class="active"><a href="#requirements" data-toggle="tab">Requirements</a></li>
<li><a href="#scorecard" data-toggle="tab">Scorecard</a></li>
<li><a href="#weight" data-toggle="tab" ng-click="prepareForWeight()">Weight</a></li>
</ul>

<div class="tab-content">
<div class="tab-pane active" id="requirements">

<div ui-sortable="sortableOptions" ng-model="requirements">
<div ng-repeat="req in requirements" style="padding-bottom:25px">
<div style="margin-bottom:15px">{{req.description}}
<div style="cursor:move; margin-bottom:15px" title="You can drag and drop to order">
<i class="icon-move"></i>
{{req.description}}
<div ng-show="req.section == 'Functional'">
<i style="font-size:11px">This requirements will display on the challenge description as a bullet point in addition to displaying on the scorecard.</i></div>
</div>
Expand All @@ -160,57 +163,81 @@ <h1>Madison Requirements Builder</h1>
<button class="btn btn-mini btn btn-danger" type="button" ng-click="delete(req)">Delete</button>

</div>

</div>
</div>
<div class="tab-pane" id="scorecard">

<p><strong>Your requirements will display as scorecard questions for the judges as follows:</strong></p>

<div ng-repeat="req in requirements" style="padding-bottom:25px">
{{req.description}}<br/>
<div ui-sortable="sortableOptions" ng-model="requirements">
<div ng-repeat="req in requirements" style="cursor:move; padding-bottom:25px">
<i class="icon-move"></i>
{{req.description}}<br/>

<div ng-show="req.scoring_type == 'Yes/No'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">Yes</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">No</label>
</div>

<div ng-show="req.scoring_type == 'Yes/No'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">Yes</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">No</label>
</div>
<div ng-show="req.scoring_type == '1-4'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">25%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">50%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">75%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">100%</label>
</div>

<div ng-show="req.scoring_type == '1-5'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">20%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">40%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">60%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">80%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">100%</label>
</div>

<div ng-show="req.scoring_type == '1-10'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">10%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">20%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">30%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">40%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">50%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">60%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">70%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">80%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">90%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">100%</label>
</div>

<div ng-show="req.scoring_type == 'Comments'" style="padding-left:5px; padding-bottom:10px">
<textarea rows="3" style="width:450px"></textarea>
</div>

<div ng-show="req.scoring_type == '1-4'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">25%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">50%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">75%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">100%</label>
</div>
</div>
</div>

<div ng-show="req.scoring_type == '1-5'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">20%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">40%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">60%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">80%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">100%</label>
</div>

<div ng-show="req.scoring_type == '1-10'" style="padding-left:5px">
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">10%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">20%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">30%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">40%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">50%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">60%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">70%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">80%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">90%</label>
<label class="radio inline"><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1">100%</label>
</div>

<div ng-show="req.scoring_type == 'Comments'" style="padding-left:5px; padding-bottom:10px">
<textarea rows="3" style="width:450px"></textarea>
</div>
<div class="tab-pane" id="weight">
<p class="help"><strong>Adjust weights for the sections using sliders.</strong></p>

<div ng-repeat="(section, requirements) in reqsBySection | filter:section!='all'">
<div class="section well" ng-show="showWeightSection(section)" style="margin-bottom: 5px; padding: 5px 10px;">
<h6>{{section}}</h6>
<div ng-repeat="req in requirements">
<div>{{req.description}}</div>
<div ui-slider="sliderOptions" ng-model="req.weight" data-section="{{section}}", data-req-id="{{req.id}}"></div>
<div class="clearfix">
<span class="pull-right">{{(req.weight)}}%</span>
<label class="checkbox pull-right">
<input type="checkbox" ng-model="req.isLocked"> Lock
&nbsp; &nbsp;
</label>
</div>

</div>
</div>
</div>


</div>
</div> <!-- end of tab pane -->
</div>

</div>
</div>

Expand Down
Loading