-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
100 start information task sang et al (#101)
* feat: added information task files * feat: updated information task * feat: completed information task
- Loading branch information
Showing
10 changed files
with
345 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
src/app/pages/tasks/task-playables/information-task/information-task.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<div class="container-fluid wrapper task-text flex-column-center"> | ||
<div *ngIf="taskStarted" class="full-box flex-column-center"> | ||
<div class="my-3"> | ||
<div style="font-size: 2rem">Click on the deck or an uncovered card</div> | ||
</div> | ||
<div class="cards-container" style="height: 400px"> | ||
<div *ngFor="let card of cardsDrawn" class="card-container"> | ||
<div | ||
(click)="handleRoundInteraction('existingCard', card)" | ||
class="card {{ largestDrawnCardValue === card ? 'largest-card' : '' }}" | ||
> | ||
{{ card }} | ||
</div> | ||
</div> | ||
</div> | ||
<div class="flex-row-space-between" style="flex-grow: 1; width: 100%"> | ||
<div style="width: 350px"> | ||
<div style="font-size: 2rem">{{ stimuli.cardValues.length - cardsDrawn.length }} cards in the deck</div> | ||
<img | ||
(click)="handleRoundInteraction('newCard', undefined)" | ||
style="width: 200px; height: 200px" | ||
class="deck" | ||
src="/assets/images/stimuli/informationtask/deckOfCards.png" | ||
alt="Deck of cards image" | ||
/> | ||
</div> | ||
<div style="flex-grow: 1"> | ||
<div style="font-size: 3rem; margin-bottom: 20px"> | ||
[ | ||
<span *ngFor="let value of valuesSelected; let i = index"> | ||
{{ value }}{{ i < valuesSelected.length - 1 ? ', ' : '' }} | ||
</span> | ||
] | ||
</div> | ||
<div style="font-size: 2rem; display: flex; justify-content: center"> | ||
<div style="margin-right: 40px">Turns Taken: {{ valuesSelected.length }}</div> | ||
<div style="margin-right: 40px"> | ||
Turns Left: {{ stimuli.cardValues.length - valuesSelected.length }} | ||
</div> | ||
<div>Total Points: {{ totalPoints }}</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> |
46 changes: 46 additions & 0 deletions
46
src/app/pages/tasks/task-playables/information-task/information-task.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
.card { | ||
border-radius: 4px; | ||
border: 4px solid black; | ||
color: green; | ||
padding: 1rem; | ||
display: flex; | ||
width: 100px; | ||
height: 150px; | ||
justify-content: center; | ||
font-size: 3rem; | ||
font-weight: bold; | ||
cursor: pointer; | ||
-webkit-user-select: none; /* Safari */ | ||
-ms-user-select: none; /* IE 10 and IE 11 */ | ||
user-select: none; /* Standard syntax */ | ||
|
||
&:hover { | ||
box-shadow: 1px 1px 10px black; | ||
} | ||
} | ||
|
||
.card-container { | ||
width: 10%; | ||
height: 150px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.cards-container { | ||
width: 100%; | ||
height: 400px; | ||
display: flex; | ||
flex-wrap: wrap; | ||
} | ||
|
||
.deck { | ||
&:hover { | ||
opacity: 0.6; | ||
cursor: pointer; | ||
} | ||
} | ||
|
||
.largest-card { | ||
color: red !important; | ||
} |
23 changes: 23 additions & 0 deletions
23
src/app/pages/tasks/task-playables/information-task/information-task.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { InformationTaskComponent } from './information-task.component'; | ||
|
||
describe('InformationTaskComponent', () => { | ||
let component: InformationTaskComponent; | ||
let fixture: ComponentFixture<InformationTaskComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
declarations: [ InformationTaskComponent ] | ||
}) | ||
.compileComponents(); | ||
|
||
fixture = TestBed.createComponent(InformationTaskComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
204 changes: 204 additions & 0 deletions
204
src/app/pages/tasks/task-playables/information-task/information-task.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
import { Component, HostListener, OnInit } from '@angular/core'; | ||
import { AbstractBaseTaskComponent } from '../base-task'; | ||
import { TimerService } from 'src/app/services/timer.service'; | ||
import { LoaderService } from 'src/app/services/loader/loader.service'; | ||
import { thisOrDefault, throwErrIfNotDefined } from 'src/app/common/commonMethods'; | ||
import { TaskPlayerState } from '../task-player/task-player.component'; | ||
import { StimuliProvidedType } from 'src/app/models/enums'; | ||
import { ComponentName } from 'src/app/services/component-factory.service'; | ||
import { DataGenerationService } from 'src/app/services/data-generation/data-generation.service'; | ||
import { InformationTaskData } from 'src/app/models/ParticipantData'; | ||
import { InformationTaskStimuliSet, InformationTaskStimulus } from 'src/app/services/data-generation/stimuli-models'; | ||
import { TranslateService } from '@ngx-translate/core'; | ||
|
||
interface InformationTaskMetadata { | ||
componentName: ComponentName; | ||
componentConfig: { | ||
numTrials: number; | ||
roundNum: number; | ||
isPractice: boolean; | ||
stimuliConfig: { | ||
type: StimuliProvidedType; | ||
stimuli: InformationTaskStimuliSet; | ||
}; | ||
}; | ||
} | ||
|
||
export enum InformationTaskCache { | ||
TOTAL_SCORE = 'information-task-total-score', | ||
OPTIMAL_SCORE = 'information-task-optimal-score', | ||
STATUS_TEXT = 'information-task-status-text', | ||
} | ||
|
||
@Component({ | ||
selector: 'app-information-task', | ||
templateUrl: './information-task.component.html', | ||
styleUrls: ['./information-task.component.scss'], | ||
}) | ||
export class InformationTaskComponent extends AbstractBaseTaskComponent { | ||
// config variables variables | ||
private numTrials: number; | ||
private roundNum: number; // determines which deck to use (1-6), translates into blockNum | ||
private isPractice: boolean; | ||
|
||
// high level variables | ||
taskData: InformationTaskData[]; | ||
stimuli: InformationTaskStimuliSet; | ||
currentStimuliIndex: number = 0; | ||
cardsDrawn: number[]; | ||
valuesSelected: number[]; | ||
taskStarted: boolean = false; | ||
roundStartTime: number; | ||
trialNum: number = 0; | ||
|
||
// local state variables | ||
|
||
get currentStimulus(): InformationTaskStimulus { | ||
return this.stimuli.cardValues[this.currentStimuliIndex]; | ||
} | ||
|
||
get currentTrial(): InformationTaskData { | ||
// will return null if taskData is not defined or if it has length of 0 | ||
return this.taskData?.length > 0 ? this.taskData[this.taskData.length - 1] : null; | ||
} | ||
|
||
get largestDrawnCardValue(): number { | ||
return this.cardsDrawn.reduce((acc, curr) => (curr > acc ? curr : acc), 0); | ||
} | ||
|
||
get totalPoints(): number { | ||
return this.valuesSelected.reduce((acc, curr) => acc + curr, 0); | ||
} | ||
|
||
// translation mapping | ||
translationMapping = { | ||
scoreStatusTextLower: { | ||
en: 'You scored lower', | ||
fr: '', | ||
}, | ||
scoreStatusTextEqual: { | ||
en: 'You were equal!', | ||
fr: '', | ||
}, | ||
scoreStatusTextHigher: { | ||
en: 'You scored higher!', | ||
fr: '', | ||
}, | ||
}; | ||
|
||
constructor( | ||
protected timerService: TimerService, | ||
protected dataGenService: DataGenerationService, | ||
protected loaderService: LoaderService, | ||
private translateService: TranslateService | ||
) { | ||
super(loaderService); | ||
} | ||
|
||
configure(metadata: InformationTaskMetadata, config?: TaskPlayerState) { | ||
try { | ||
this.userID = throwErrIfNotDefined(config.userID, 'no user ID defined'); | ||
this.studyId = throwErrIfNotDefined(config.studyID, 'no study code defined'); | ||
|
||
this.numTrials = throwErrIfNotDefined(metadata.componentConfig.numTrials, 'num trials not defined'); | ||
this.roundNum = throwErrIfNotDefined(metadata.componentConfig.roundNum, 'roundNum is not defined'); | ||
this.isPractice = thisOrDefault(metadata.componentConfig.isPractice, false); | ||
} catch (error) { | ||
throw new Error('values not defined, cannot start study'); | ||
} | ||
|
||
this.config = config; | ||
if (metadata.componentConfig.stimuliConfig.type === StimuliProvidedType.HARDCODED) | ||
this.stimuli = metadata.componentConfig.stimuliConfig.stimuli; | ||
} | ||
|
||
start(): void { | ||
console.log(this.stimuli); | ||
// configure game | ||
this.taskStarted = true; | ||
|
||
this.taskData = []; | ||
this.cardsDrawn = []; | ||
this.valuesSelected = []; | ||
this.currentStimuliIndex = 0; | ||
this.trialNum = 0; | ||
|
||
this.roundStartTime = Date.now(); | ||
super.start(); | ||
} | ||
|
||
beginRound() { | ||
this.timerService.clearTimer(); | ||
this.timerService.startTimer(); | ||
} | ||
|
||
handleRoundInteraction(cardType: 'newCard' | 'existingCard', existingCardVal?: number) { | ||
if (!this.taskStarted) return; | ||
|
||
const newCardVal = this.currentStimulus; | ||
|
||
let cardValue: number; | ||
if (cardType === 'newCard') { | ||
this.cardsDrawn.push(newCardVal.cardValue); | ||
cardValue = newCardVal.cardValue; | ||
} else { | ||
cardValue = existingCardVal; | ||
} | ||
|
||
this.valuesSelected.push(cardValue); | ||
|
||
this.taskData.push({ | ||
userID: this.userID, | ||
studyId: this.studyId, | ||
submitted: this.timerService.getCurrentTimestamp(), | ||
isPractice: this.isPractice, | ||
trial: ++this.trialNum, | ||
roundNum: this.roundNum, | ||
trialScore: cardValue, | ||
cumulativeRoundLength: Date.now() - this.roundStartTime, | ||
cumulativeRoundScore: | ||
this.taskData.length <= 0 | ||
? cardValue | ||
: this.taskData[this.taskData.length - 1].cumulativeRoundScore + cardValue, | ||
exploited: cardType === 'existingCard', | ||
expectedToExploit: newCardVal.expectedToExploit, | ||
trialResponseTime: this.timerService.stopTimerAndGetTime(), | ||
}); | ||
super.handleRoundInteraction(null); | ||
} | ||
|
||
async completeRound() { | ||
super.completeRound(); | ||
} | ||
|
||
async decideToRepeat(): Promise<void> { | ||
if (this.trialNum >= this.numTrials) { | ||
this.taskStarted = false; | ||
const totalScore = this.taskData.reduce((acc, currVal) => { | ||
return acc + currVal.trialScore; | ||
}, 0); | ||
|
||
const optimalScore = this.stimuli.optimalScore; | ||
|
||
console.log({ totalScore, optimalScore }); | ||
|
||
const statusText = | ||
totalScore < optimalScore | ||
? this.translationMapping.scoreStatusTextLower[this.translateService.currentLang] | ||
: totalScore === optimalScore | ||
? this.translationMapping.scoreStatusTextEqual[this.translateService.currentLang] | ||
: this.translationMapping.scoreStatusTextHigher[this.translateService.currentLang]; | ||
|
||
// this will replace the previous round | ||
this.config.setCacheValue(InformationTaskCache.TOTAL_SCORE, totalScore); | ||
this.config.setCacheValue(InformationTaskCache.OPTIMAL_SCORE, optimalScore); | ||
this.config.setCacheValue(InformationTaskCache.STATUS_TEXT, statusText); | ||
|
||
super.decideToRepeat(); | ||
} else { | ||
this.currentStimuliIndex++; | ||
this.beginRound(); | ||
return; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.