Skip to content

Instantly share code, notes, and snippets.

@oops-wrong
Created April 11, 2017 07:17
Show Gist options
  • Save oops-wrong/7999c3c19e9b7e83a3f33d89ac4990c1 to your computer and use it in GitHub Desktop.
Save oops-wrong/7999c3c19e9b7e83a3f33d89ac4990c1 to your computer and use it in GitHub Desktop.
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