Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented question bank functionality #65

Merged
merged 21 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f486bd6
Implemented questionBank dialog with styling in creator
dmols Jan 11, 2024
4fcc596
Support for questionBank functionality in both creator and player
dmols Jan 11, 2024
4d3be9d
Added final styling to dialog button
dmols Jan 12, 2024
4bf32e8
Added safeguards and testing for new code
dmols Jan 12, 2024
d846eee
Added test coverage for question bank code in creator and player
dmols Jan 12, 2024
845e0a6
QB button indicates when it's on + questionBankVal now updates to que…
dmols Jan 16, 2024
ed57bb9
Fixed issue where qset didn't save the QB options
dmols Jan 16, 2024
c6e9a08
Styled qb dialog more like intro dialog
dmols Jan 23, 2024
83ecb5f
Small change from HEX value to pre-defined variable
dmols Jan 23, 2024
e48191b
changed variable names for better readability
dmols Jan 23, 2024
f1c21d1
Fixed visual changes to other modal components
dmols Jan 23, 2024
2395c9f
Styled and aligned QB button like the randomize button
dmols Jan 23, 2024
ed4a829
Changed text within QB dialog to be label instead
dmols Jan 23, 2024
feabc38
Removed overlay, placed qb modal with the others, and added variable …
dmols Jan 23, 2024
a418667
Added test coverage and cleaned up code
dmols Jan 24, 2024
a7d1c0e
Made additional selectors follow snake-case syntax
dmols Jan 26, 2024
a0918a3
Added styling to dialog & disabled qb on tutorial load
dmols Feb 9, 2024
c502e54
Simplified question bank dialog code and fixed modal error
dmols Feb 12, 2024
8efe893
Minor css changes
dmols Feb 13, 2024
3e377ae
Added clarification that QB will randomize questions
dmols Feb 28, 2024
6137fb2
Added tooltip and css changes to give header flexbox properties
dmols Mar 1, 2024
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
26 changes: 23 additions & 3 deletions src/controllers/ctl-creator.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export const ControllerThisOrThatCreator = function($scope, $timeout, $sanitize,
$scope.questions = []
$scope.currIndex = -1
$scope.dialog = {}
$scope.questionBankModal = false
$scope.enableQuestionBank = false
$scope.questionBankValTemp = 1
$scope.questionBankVal = 1
$scope.tutorial = {
checked: false,
step: 1,
Expand Down Expand Up @@ -49,6 +53,12 @@ export const ControllerThisOrThatCreator = function($scope, $timeout, $sanitize,
materiaCallbacks.initExistingWidget = function(title, widget, qset, version, baseUrl) {
$scope.title = title
$scope.tutorial.step = null

if(qset.options) {
$scope.enableQuestionBank = qset.options.enableQuestionBank ? qset.options.enableQuestionBank : false;
$scope.questionBankVal = qset.options.questionBankVal ? qset.options.questionBankVal : 1;
$scope.questionBankValTemp = qset.options.questionBankVal ? qset.options.questionBankVal : 1;
}
materiaCallbacks.onQuestionImportComplete(qset.items)
}

Expand All @@ -63,7 +73,9 @@ export const ControllerThisOrThatCreator = function($scope, $timeout, $sanitize,
const qset = CreatorService.buildQset(
$sanitize($scope.title),
$scope.questions,
$scope.randomizeOrder
$scope.randomizeOrder,
$scope.enableQuestionBank,
$scope.questionBankVal
clpetersonucf marked this conversation as resolved.
Show resolved Hide resolved
)
if (qset) {
return Materia.CreatorCore.save($sanitize($scope.title), qset, 2)
Expand Down Expand Up @@ -292,6 +304,12 @@ export const ControllerThisOrThatCreator = function($scope, $timeout, $sanitize,
}
}

$scope.validateQuestionBankVal = function() {
if ($scope.questionBankValTemp >= 1 && $scope.questionBankValTemp <= $scope.questions.length) {
$scope.questionBankVal = $scope.questionBankValTemp
}
}

$scope.updateAnswerType = function(type, currIndex, side) {
let sideIndex = 0
if (side == $scope.CORRECT) {
Expand Down Expand Up @@ -567,8 +585,10 @@ export const ControllerThisOrThatCreator = function($scope, $timeout, $sanitize,
}
}

$scope.hideModal = () =>
($scope.dialog.invalid = $scope.dialog.edit = $scope.dialog.intro = $scope.dialog.rearrange = false)
$scope.hideModal = () => {
$scope.dialog.invalid = $scope.dialog.edit = $scope.dialog.intro = $scope.dialog.rearrange = $scope.questionBankModal = false
$scope.questionBankValTemp = $scope.questionBankVal
}

return Materia.CreatorCore.start(materiaCallbacks)
}
17 changes: 17 additions & 0 deletions src/controllers/ctl-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,23 @@ export const onMateriaStart = ($scope, $sce, instance, qset, version) => {
shuffleArray(_qset.items)
}

// if question bank is enabled, slice the qset to the length specified in the qset options
if(_qset.options && _qset.options.enableQuestionBank === true) {

// don't shuffle if the qset's been shuffled already
if(_qset.options.randomizeOrder === true) {
let qbItemsLength = qset.options.questionBankVal
let rndStart = Math.floor(Math.random() * (_qset.items.length - qbItemsLength + 1))
_qset.items = _qset.items.slice(rndStart, rndStart + qbItemsLength)
}
else {
shuffleArray(_qset.items)
let qbItemsLength = qset.options.questionBankVal
let rndStart = Math.floor(Math.random() * (_qset.items.length - qbItemsLength + 1))
_qset.items = _qset.items.slice(rndStart, rndStart + qbItemsLength)
}
}

$scope.choices = getAllAnswerChoices($sce, _qset)
$scope.questionCount = _qset.items.length

Expand Down
37 changes: 34 additions & 3 deletions src/creator.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,17 @@
</head>
<body ng-app="ThisOrThatCreator" ng-controller="ThisOrThatCreatorCtrl">
<div id="modal"></div>

<div id="header">
<div class="logo"></div>
<h1 id="title" ng-bind="title" ng-click="dialog.edit = true"></h1>
<div class="link" ng-click="dialog.edit = true">Edit...</div>
<button
class="right"
ng-click="questionBankModal = !questionBankModal"
ng-class="{'btn':true, 'green':enableQuestionBank, 'dark':!enableQuestionBank }"
ng-disabled="tutorial.step && tutorial.step < 6">
Question Bank: {{enableQuestionBank === true ? "On" : "Off"}}
</button>
<button
aria-label="Randomize Question Order"
class="right"
Expand All @@ -38,7 +44,6 @@ <h1 id="title" ng-bind="title" ng-click="dialog.edit = true"></h1>
Randomize Question Order: {{randomizeOrder === true ? "On" : "Off"}}
</button>
</div>

<main class="container" role="main">
<div class="question-container">
<div class="question-slider" ng-class="{ 'activated': questions[0] }">
Expand Down Expand Up @@ -425,7 +430,7 @@ <h2 class="desc">The incorrect choice</h2>
</main>

<div class="modal-bg"
ng-class="{ 'show': dialog.intro || dialog.edit || dialog.invalid || dialog.rearrange }"
ng-class="{ 'show': dialog.intro || dialog.edit || dialog.invalid || dialog.rearrange || questionBankModal }"
ng-click="hideModal()">
<div class="modal-container">
<!-- Invalid modal -->
Expand Down Expand Up @@ -517,6 +522,32 @@ <h4>Rearrange Questions</h4>
<button class="btn green done-btn" ng-click="hideModal()">Done</button>
</div>
<!-- /Rearrange questions modal -->
<!-- Question Bank Modal -->
<div class="modal question-bank-dialog"
ng-show= "questionBankModal">
<div class="enable-qb-question">
<label style="font-weight: bold;">Enable question bank? </label>
<div class="enable-qb-options"
ng-click="$event.stopPropagation()">
<span>
<input type="radio" ng-model="enableQuestionBank" ng-value=false> No</input>
<input type="radio" ng-model="enableQuestionBank" ng-change="questionBankVal = questions.length; questionBankValTemp = questions.length" ng-value=true> Yes</input>
</span>
</div>
</div>
<div>
<span ng-show="enableQuestionBank" >
<label style="font-weight: bold;">How many questions to ask? </label>
<div class="num-input-wrapper"
ng-click="$event.stopPropagation()">
<input class="num-input" type="number" ng-model="questionBankValTemp" ng-change="validateQuestionBankVal()" step="1">
<span> out of {{questions.length}}</span>
</div>
</span>
</div>
<button class="dialog-close-button btn green" ng-disabled="(questionBankValTemp < 1 || questionBankValTemp > questions.length) && enableQuestionBank" ng-click="questionBankModal = false">CONFIRM</button>
</div>
<!-- /Question Bank Modal -->
</div>
</div>
</body>
Expand Down
65 changes: 58 additions & 7 deletions src/creator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -405,18 +405,17 @@ input[type='text'] {
}

.modal {
margin: 0 auto;
padding: 15px;
width: 635px;

font-weight: 300;
font-size: 17px;
text-align: left;

background: #fff;
border-radius: 8px;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
color: #333;
font-weight: 300;
font-size: 17px;
margin: 0 auto;
padding: 15px;
width: 635px;
text-align: left;

img {
float: right;
Expand Down Expand Up @@ -485,6 +484,58 @@ input[type='text'] {
color: fade-out($red, 0.4);
}
}

}

.question-bank-dialog {
position: fixed;
height: 200px;
width: 300px;
padding: 5px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 15px;

.num-input-wrapper {
display: flex;
justify-content: center;
width: 100%;
align-items: center;

.num-input {
font-size: 17px;
width: 35px;
margin: 2px 5px 0 0;
}
}

.enable-qb-question {
margin-top: 15px;
margin-bottom: 15px;

.enable-qb-options {
display: flex;
justify-content: center;
}
}

.dialog-close-button {
margin-top: 10px;
box-shadow: none;
text-shadow: none;
font-size: 15px;

&:disabled {
background-color: $gray;
color: $darker;
}
}
}

.question-container {
Expand Down
57 changes: 57 additions & 0 deletions src/creator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -836,4 +836,61 @@ describe('Creator Controller', function() {
expect($scope.title).toEqual(widgetInfo.name)
expect($scope.questions.length).toEqual(5)
})

test('should test questionBank variable data if qset.options exists', () => {
qset.data.options = { enableQuestionBank: true, questionBankVal: 3 }
publicMethods.initExistingWidget(widgetInfo.name, widgetInfo, qset.data)

if(qset.data.options.enableQuestionBank === true) {
$scope.enableQuestionBank = qset.data.options.enableQuestionBank
}
else {
$scope.enableQuestionBank = false
}

if(qset.data.options.questionBankVal) {
$scope.questionBankVal = qset.data.options.questionBankVal
}


expect($scope.enableQuestionBank).toBe(true)
expect($scope.questionBankVal).toBe(3)

})

test('should test questionBank variable data if qset.options exists but does not have questionBank data', () => {
qset.data.options = {}
publicMethods.initExistingWidget(widgetInfo.name, widgetInfo, qset.data)

if(qset.data.options.enableQuestionBank === undefined) {
$scope.enableQuestionBank = false
}

if(qset.data.options.questionBankVal === undefined) {
$scope.questionBankVal = 1
}


expect($scope.enableQuestionBank).toBe(false)
expect($scope.questionBankVal).toBe(1)

})

test('should test changing questionBankVal based on if questionBankValTemp is valid or not', () => {
qset.data.options = {questionBankVal: 3}

publicMethods.initExistingWidget(widgetInfo.name, widgetInfo, qset.data)

// expect qbVal to update when qbValTemp is valid
$scope.questionBankValTemp = 5
$scope.validateQuestionBankVal()
expect($scope.questionBankVal).toBe(5)

// expect qbVal to stay the same if qbValTemp is invalid
$scope.questionBankValTemp = -2
$scope.validateQuestionBankVal()
expect($scope.questionBankVal).toBe(5)

})

})
58 changes: 58 additions & 0 deletions src/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
// them quite a bit more complicated, and 'higher'
// up in the unit -> functional testing ladder
// in general we should continue to push toward more

const ctlPlayer = require('./controllers/ctl-player');

// direct unit tests as this project continues to evolve
describe('Player Controller', function() {
require('angular/angular.js')
Expand Down Expand Up @@ -443,6 +446,60 @@ describe('Player Controller', function() {
}
})

test('should shuffle questions when randomize is false but enableQuestionBank is true with qb value at 3', () => {

let widgetInfoCopy = JSON.parse(JSON.stringify(widgetInfo));
let qsetCopy = JSON.parse(JSON.stringify(qset));
qsetCopy.data.options = { randomizeOrder: false, enableQuestionBank: true, questionBankVal: 3}
publicMethods.start(widgetInfoCopy, qsetCopy.data)

const originalArray = [...qset.data.items];

// qset items haven't been randomized so we must shuffle the items
if(qsetCopy.data.options.randomizeOrder === true) {
let qbItemsLength = qsetCopy.data.options.questionBankVal
let rndStart = Math.floor(Math.random() * (qsetCopy.data.items.length - qbItemsLength + 1))
qsetCopy.data.items = qsetCopy.data.items.slice(rndStart, rndStart + qbItemsLength)
}
else {
ctlPlayer.shuffleArray(qsetCopy.data.items)
let qbItemsLength = qsetCopy.data.options.questionBankVal
let rndStart = Math.floor(Math.random() * (qsetCopy.data.items.length - qbItemsLength + 1))
qsetCopy.data.items = qsetCopy.data.items.slice(rndStart, rndStart + qbItemsLength)
}

// array should be smaller since it got sliced
expect(originalArray.length).not.toEqual(qsetCopy.data.items.length);

})

test('should shuffle questions when randomize is true and so is enableQuestionBank', () => {

let widgetInfoCopy = JSON.parse(JSON.stringify(widgetInfo));
let qsetCopy = JSON.parse(JSON.stringify(qset));
qsetCopy.data.options = { randomizeOrder: true, enableQuestionBank: true, questionBankVal: 3}
publicMethods.start(widgetInfoCopy, qsetCopy.data)

const originalArray = [...qset.data.items];

// qsetCopy items was randomized earlier in the player code so no need to reshuffle
if(qsetCopy.data.options.randomizeOrder === true) {
let qbItemsLength = qsetCopy.data.options.questionBankVal
let rndStart = Math.floor(Math.random() * (qsetCopy.data.items.length - qbItemsLength + 1))
qsetCopy.data.items = qsetCopy.data.items.slice(rndStart, rndStart + qbItemsLength)
}
else {
ctlPlayer.shuffleArray(qsetCopy.data.items)
let qbItemsLength = qsetCopy.data.options.questionBankVal
let rndStart = Math.floor(Math.random() * (qsetCopy.data.items.length - qbItemsLength + 1))
qsetCopy.data.items = qsetCopy.data.items.slice(rndStart, rndStart + qbItemsLength)
}

// array should be smaller since it got sliced
expect(originalArray.length).not.toEqual(qsetCopy.data.items.length);

})

test('should not shuffle questions if there are none', () => {
global.Math.random = jest.fn(() => 0.1)
qset.data.items = []
Expand Down Expand Up @@ -488,4 +545,5 @@ describe('Player Controller', function() {
expect($scope.instructionsOpen).toBe(false)

})

})
5 changes: 4 additions & 1 deletion src/services/srv-creator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const buildQset = ($sanitize, title, items, isRandom) => {
export const buildQset = ($sanitize, title, items, isRandom, enableQuestionBank, questionBankVal) => {
const qsetItems = []
const qset = {}
qset.options = {}
Expand All @@ -18,6 +18,9 @@ export const buildQset = ($sanitize, title, items, isRandom) => {

qset.items = qsetItems
qset.options.randomizeOrder = isRandom
qset.options.enableQuestionBank = enableQuestionBank
qset.options.questionBankVal = questionBankVal

return qset
}

Expand Down
Loading