Created
April 11, 2017 07:17
-
-
Save oops-wrong/7999c3c19e9b7e83a3f33d89ac4990c1 to your computer and use it in GitHub Desktop.
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
import { | |
QnAnswer, | |
QnAnswerContainer, | |
QnEntities, | |
QnHelper, | |
QnLocaleStorage, | |
QnOption, | |
QnQuestion, | |
QnStorageModel, | |
QnTask, | |
QnTaskName, | |
QnTaskAvailableName, | |
QnTest, | |
QuestionHelper, | |
QuestionnaireView, | |
QuestionnaireAvailableName, | |
QuestionnaireName, | |
QuestionnaireService, | |
RadioChoice, | |
UserQuestionnaire, | |
UserQuestionnaireHelper | |
} from '../profile.module'; | |
export class QnShowController implements ng.IController { | |
public static $inject = [ | |
'$q', | |
'$state', | |
'questionnaireService' | |
]; | |
public allLoaded = false; | |
public audioUrl: string; | |
public finishButtonLoading = false; | |
public introTemplate: string; | |
public isNeedCallback = false; | |
public qnOptions: QnOption[]; | |
public qnQuestions: QnQuestion[]; | |
public qnAnswers: QnAnswerContainer = new QnAnswerContainer(); | |
public qnTasks: QnTask[]; | |
public QnTaskName = QnTaskName; | |
public qnTest: QnTest; | |
public QuestionHelper = QuestionHelper; | |
public questionnaire: QuestionnaireView; | |
public questionnaireName: QuestionnaireAvailableName; | |
public questionnaireTemplate: string; | |
public radioOptionsGroupedByQuestions: RadioChoice[]; | |
public startButtonLoading = false; | |
public stateView = new StateView(); | |
public storageParams: QnStorageModel = new QnStorageModel; | |
public text: string; | |
private initState: StateAvailable = State.start; | |
private loadedQnAnswers: QnAnswer[]; | |
private state: StateAvailable; | |
private userQuestionnaire: UserQuestionnaire; | |
constructor( | |
private $q: ng.IQService, | |
private $state: ng.ui.IStateService, | |
private questionnaireService: QuestionnaireService, | |
) {} | |
public $onInit() { | |
this.setQuestionnaireName(); | |
this.getQuestionMetadata(); | |
if (!QuestionnaireName.isValidName(this.questionnaireName)) { | |
this.setState(State.notFound); | |
this.afterLoading(); | |
return; | |
} | |
this.setQuestionnaire(); | |
this.setQuestionSource(); | |
this.load(); | |
} | |
public addOptionToAnswers(question: QnQuestion, questionIndex: number, option: QnOption): void { | |
if (!this.hasOptionInAnswers(question, questionIndex, option)) { | |
let answers = this.qnAnswers[question.task]; | |
let answer: QnAnswer = answers[questionIndex]; | |
answer.answers.push(option.text); | |
this.qnAnswerUpdate(answer); | |
} | |
} | |
public createQnAnswer(question: QnQuestion, index: number): void { | |
let answers = this.qnAnswers[question.task] || []; | |
this.qnAnswers[question.task] = answers; | |
let loadedQnAnswer = QnHelper.getAnswerByQuestion(this.loadedQnAnswers, question.id); | |
answers[index] = _.assignIn(new QnAnswer(), loadedQnAnswer) || new QnAnswer(); | |
answers[index].callback = this.getQnAnswerCallback(); | |
answers[index].question = question.id; | |
} | |
public finishQuestionnaire(): void { | |
if (UserQuestionnaireHelper.hasFinishedAt(this.userQuestionnaire)) { | |
return; | |
} | |
this.finishButtonLoading = true; | |
this.questionnaireService.finishQuestionnaire(this.userQuestionnaire) | |
.then((userQuestionnaire: UserQuestionnaire) => { | |
this.userQuestionnaire = userQuestionnaire; | |
this.setState(State.finish); | |
}) | |
.catch(() => { | |
this.setState(State.error); | |
}) | |
.finally(() => { | |
this.finishButtonLoading = false; | |
}); | |
} | |
public getOptionsByQuestion(question: QnQuestion): QnOption[] { | |
return QnHelper.getOptionsByQuestion(this.qnOptions, question); | |
} | |
public getQuestionsByTask(taskName: QnTaskAvailableName): QnQuestion[] { | |
let task: QnTask = QnHelper.getTaskByName(this.qnTasks, taskName); | |
return QnHelper.getQuestionsByTask(this.qnQuestions, task.id); | |
} | |
public getRadioOptionsByQuestion(question: QnQuestion): RadioChoice { | |
let options: QnOption[] = this.getOptionsByQuestion(question); | |
return QnHelper.convertOptionsToRadioOptions(options); | |
} | |
public hasOptionInAnswers(question: QnQuestion, questionIndex: number, option: QnOption): boolean { | |
let answers = this.qnAnswers[question.task]; | |
let hasAnswerObject = !!answers; | |
let hasAnswers = () => !!answers[questionIndex]; | |
let hasOptionInAnswers = () => !!~answers[questionIndex].answers.indexOf(option.text); | |
return hasAnswerObject && hasAnswers() && hasOptionInAnswers(); | |
} | |
public isQnEntitiesLoaded(): boolean { | |
return !!(this.qnOptions && this.qnQuestions && this.qnTasks); | |
} | |
public listeningChoices(): void { | |
this.storageParams.listening = true; | |
QnLocaleStorage.setListeningFlag(this.storageParams.listening); | |
} | |
public qnAnswerUpdate(qnAnswer: QnAnswer): void { | |
this.questionnaireService.createOrUpdateQnAnswer(qnAnswer) | |
.then((newQnAnswer: QnAnswer) => { | |
_.assignIn(qnAnswer, newQnAnswer); | |
qnAnswer.callback = this.getQnAnswerCallback(); | |
}) | |
.catch(() => { | |
// Update state on time ending. | |
window.location.reload(); | |
}); | |
} | |
public readingChoices(): void { | |
this.storageParams.reading = true; | |
QnLocaleStorage.setReadingFlag(this.storageParams.reading); | |
} | |
public setOptionToAnswers(question: QnQuestion, questionIndex: number, option: QnOption): void { | |
if (!this.hasOptionInAnswers(question, questionIndex, option)) { | |
let answers = this.qnAnswers[question.task]; | |
let answer: QnAnswer = answers[questionIndex]; | |
answer.answers = [option.text]; | |
this.qnAnswerUpdate(answer); | |
} | |
} | |
public setRadioOptions(): void { | |
this.radioOptionsGroupedByQuestions = []; | |
_.each(this.qnQuestions, qnQuestion => { | |
this.radioOptionsGroupedByQuestions[qnQuestion.id] = this.getRadioOptionsByQuestion(qnQuestion); | |
}); | |
} | |
public startQuestionnaire(): void { | |
this.startButtonLoading = true; | |
this.questionnaireService.startQuestionnaire(this.qnTest) | |
.then((userQuestionnaire: UserQuestionnaire) => { | |
this.resetQuestionMetadata(); | |
this.userQuestionnaire = userQuestionnaire; | |
this.setState(State.process); | |
}) | |
.catch(() => { | |
this.setState(State.error); | |
}) | |
.finally(() => { | |
this.startButtonLoading = false; | |
}); | |
} | |
public timeEnded(): void { | |
if (this.qnTest.is_limited) { | |
this.setState(State.finish); | |
} | |
} | |
private afterLoading(): void { | |
this.allLoaded = true; | |
if (_.isEmpty(this.state)) { | |
this.setState(this.initState); | |
} | |
} | |
private afterSuccessfulLoading(): void { | |
this.setStateOnUserQnExisting(); | |
this.setRemainingTime(); | |
QnLocaleStorage.setStartTime(this.qnTest.time_available); | |
} | |
private errorLoading(): void { | |
this.setState(State.error); | |
} | |
private getQnAnswerCallback(): (...rest) => any { | |
if (this.isNeedCallback) { | |
return this.qnAnswerUpdate.bind(this); | |
} | |
return void 0; | |
} | |
private getQuestionMetadata(): void { | |
this.storageParams.listening = QnLocaleStorage.getListeningFlag(); | |
this.storageParams.reading = QnLocaleStorage.getReadingFlag(); | |
} | |
private load(): void { | |
let qnTestLoader = this.setQnTest(); | |
let userQuestionnaireLoader = this.setUserQuestionnaire(); | |
this.$q.all([qnTestLoader, userQuestionnaireLoader]) | |
.then(() => this.afterSuccessfulLoading()) | |
.catch(() => this.errorLoading()) | |
.finally(() => this.afterLoading()); | |
} | |
private loadQnEntities(): ng.IPromise<void> { | |
let qnsLoader = this.questionnaireService.loadQnEntities(this.questionnaireName); | |
return qnsLoader | |
.then(({qnAnswers, qnOptions, qnQuestions, qnTasks}: QnEntities) => { | |
this.setQnAnswers(qnAnswers); | |
this.qnOptions = qnOptions; | |
this.qnQuestions = qnQuestions; | |
this.qnTasks = qnTasks; | |
}) | |
.catch(() => { | |
this.setState(State.error); | |
}); | |
} | |
private resetQuestionMetadata(): void { | |
this.storageParams.timer = this.qnTest.time_available; | |
this.storageParams.listening = false; | |
this.storageParams.reading = false; | |
QnLocaleStorage.resetQnParams(this.storageParams); | |
} | |
private setQnAnswers(qnAnswers: QnAnswer[]): void { | |
this.loadedQnAnswers = _.map(qnAnswers, qnAnswer => { | |
qnAnswer.callback = this.getQnAnswerCallback(); | |
return qnAnswer; | |
}); | |
} | |
private setQnTest(): ng.IPromise<void> { | |
let loader = this.questionnaireService.loadQnTests(); | |
return loader.then((qnTests: QnTest[]) => { | |
this.qnTest = QnHelper.getTestByName(qnTests, this.questionnaireName); | |
if (_.isEmpty(this.qnTest)) { | |
this.setState(State.notFound); | |
} | |
}); | |
} | |
private setQuestionnaire(): void { | |
this.questionnaire = this.questionnaireService.getQuestionnaire(this.questionnaireName); | |
} | |
private setQuestionnaireName(): void { | |
this.questionnaireName = this.$state.params['test_name']; | |
} | |
private setQuestionSource(): void { | |
if (QuestionnaireName.isForeignName(this.questionnaireName)) { | |
this.audioUrl = `/assets/source/${this.questionnaireName}.mp3`; | |
this.questionnaireService.getQustionText(this.questionnaireName) | |
.then(response => { | |
this.text = response.data.text; | |
}); | |
} | |
} | |
private setRemainingTime(): void { | |
let remaining = _.get(this.userQuestionnaire, 'remaining'); | |
this.storageParams.timer = _.toNumber(remaining); | |
} | |
private setState(state: StateAvailable): void { | |
switch (state) { | |
case State.start: | |
this.stateView.setState(State.start); | |
break; | |
case State.process: | |
this.stateView.setState(State.process); | |
this.loadQnEntities(); | |
break; | |
case State.finish: | |
this.stateView.setState(State.finish); | |
this.finishQuestionnaire(); | |
break; | |
case State.error: | |
this.stateView.setState(State.error); | |
break; | |
case State.notFound: | |
break; | |
default: | |
throw new TypeError(`Unknown state '${state}'`); | |
} | |
this.state = state; | |
} | |
private setStateOnUserQnExisting(): void { | |
if (UserQuestionnaireHelper.isProcessedUserQuestionnaire(this.userQuestionnaire)) { | |
this.initState = State.process; | |
} | |
if (UserQuestionnaireHelper.isFinishedUserQuestionnaire(this.userQuestionnaire)) { | |
this.initState = State.finish; | |
} | |
} | |
private setUserQuestionnaire(): ng.IPromise<void> { | |
let qnLoader = this.questionnaireService.loadUserQuestionnaires(); | |
return qnLoader.then((userQuestionnaires: UserQuestionnaire[]) => { | |
this.userQuestionnaire = UserQuestionnaireHelper.getTestByName(userQuestionnaires, this.questionnaireName); | |
}); | |
} | |
} | |
class State { | |
static readonly start: StateAvailable = 'start'; | |
static readonly process: StateAvailable = 'process'; | |
static readonly finish: StateAvailable = 'finish'; | |
static readonly error: StateAvailable = 'error'; | |
static readonly notFound: StateAvailable = 'notFound'; | |
} | |
type StateAvailable = 'start' | 'process' | 'finish' | 'error' | 'notFound'; | |
class StateView { | |
public buttons = false; | |
public error = false; | |
public finish = false; | |
public intro = false; | |
public questionnaire = false; | |
public timer = false; | |
private state: StateAvailable; | |
public setState(state: StateAvailable): void { | |
this.resetViewState(); | |
switch (state) { | |
case State.start: | |
this.buttons = true; | |
this.intro = true; | |
break; | |
case State.process: | |
this.questionnaire = true; | |
this.timer = true; | |
break; | |
case State.finish: | |
this.finish = true; | |
break; | |
case State.error: | |
this.error = true; | |
break; | |
} | |
this.state = state; | |
} | |
private resetViewState(): void { | |
this.buttons = false; | |
this.error = false; | |
this.finish = false; | |
this.intro = false; | |
this.questionnaire = false; | |
this.timer = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment