MOBILE-3109 survey: Add return types to survey

main
Dani Palou 2019-09-10 09:39:15 +02:00
parent a2fbe1c808
commit a779e69b49
4 changed files with 111 additions and 33 deletions

View File

@ -36,14 +36,14 @@
<ng-container *ngFor="let question of questions; let index=index; let isEven=even;">
<!-- Parent question (Category header) -->
<div *ngIf="question.multi && question.multi.length" [attr.padding-top]="index == 1">
<div *ngIf="question.multiArray && question.multiArray.length" [attr.padding-top]="index == 1">
<h3 padding-horizontal>{{ question.text }}</h3>
<ion-grid no-padding>
<ion-row no-padding align-items-center class="hidden-phone">
<ion-col col-7>
<div padding>{{ 'addon.mod_survey.responses' | translate }}</div>
</ion-col>
<ion-col text-center *ngFor="let option of question.options">
<ion-col text-center *ngFor="let option of question.optionsArray">
<div padding>{{ option }}</div>
</ion-col>
</ion-row>
@ -61,20 +61,20 @@
</ion-col>
<!-- Tablet view: radio buttons -->
<ion-col class="hidden-phone" text-center *ngFor="let option of question.options; let value=index;">
<ion-col class="hidden-phone" text-center *ngFor="let option of question.optionsArray; let value=index;">
<ion-radio [value]="value + 1" [attr.aria-labelledby]="'addon-mod_survey-'+question.name"></ion-radio>
</ion-col>
<ion-col class="hidden-tablet">
<ion-select padding [(ngModel)]="answers[question.name]" [attr.aria-labelledby]="'addon-mod_survey-'+question.name" interface="action-sheet" [required]="question.required">
<ion-option value="-1" selected disabled>{{ 'core.choose' | translate }}</ion-option>
<ion-option *ngFor="let option of question.options; let value=index;" [value]="value +1">{{option}}</ion-option>
<ion-option *ngFor="let option of question.optionsArray; let value=index;" [value]="value +1">{{option}}</ion-option>
</ion-select>
</ion-col>
</ion-row>
</ion-grid>
<!-- Single question (don't belong to a category) -->
<ng-container *ngIf="(!question.multi || question.multi.length == 0) && question.parent === 0">
<ng-container *ngIf="(!question.multiArray || question.multiArray.length == 0) && question.parent === 0">
<ion-grid no-padding text-wrap *ngIf="question.type > 0" [class.even]="isEven">
<ion-row no-padding align-items-center>
<ion-col col-7>
@ -82,7 +82,7 @@
</ion-col>
<ion-col col-5>
<ion-select padding [(ngModel)]="answers[question.name]" [attr.aria-labelledby]="'addon-mod_survey-'+question.name" interface="action-sheet" [required]="question.required">
<ion-option *ngFor="let option of question.options; let value=index;" [value]="value">{{option}}</ion-option>
<ion-option *ngFor="let option of question.optionsArray; let value=index;" [value]="value">{{option}}</ion-option>
</ion-select>
</ion-col>
</ion-row>

View File

@ -15,8 +15,8 @@
import { Component, Optional, Injector } from '@angular/core';
import { Content } from 'ionic-angular';
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
import { AddonModSurveyProvider } from '../../providers/survey';
import { AddonModSurveyHelperProvider } from '../../providers/helper';
import { AddonModSurveyProvider, AddonModSurveySurvey } from '../../providers/survey';
import { AddonModSurveyHelperProvider, AddonModSurveyQuestionFormatted } from '../../providers/helper';
import { AddonModSurveyOfflineProvider } from '../../providers/offline';
import { AddonModSurveySyncProvider } from '../../providers/sync';
@ -31,8 +31,8 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo
component = AddonModSurveyProvider.COMPONENT;
moduleName = 'survey';
survey: any;
questions: any;
survey: AddonModSurveySurvey;
questions: AddonModSurveyQuestionFormatted[];
answers = {};
protected userId: number;
@ -103,7 +103,7 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo
return this.surveyProvider.getSurvey(this.courseId, this.module.id).then((survey) => {
this.survey = survey;
this.description = survey.intro || survey.description;
this.description = survey.intro;
this.dataRetrieved.emit(survey);
if (sync) {
@ -144,13 +144,13 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo
// Init answers object.
this.questions.forEach((q) => {
if (q.name) {
const isTextArea = q.multi && q.multi.length === 0 && q.type === 0;
const isTextArea = q.multiArray && q.multiArray.length === 0 && q.type === 0;
this.answers[q.name] = q.required ? -1 : (isTextArea ? '' : '0');
}
if (q.multi && !q.multi.length && q.parent === 0 && q.type > 0) {
if (q.multiArray && !q.multiArray.length && q.parent === 0 && q.type > 0) {
// Options shown in a select. Remove all HTML.
q.options = q.options.map((option) => {
q.optionsArray = q.optionsArray.map((option) => {
return this.textUtils.cleanTags(option);
});
}

View File

@ -14,6 +14,7 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { AddonModSurveyQuestion } from './survey';
/**
* Service that provides helper functions for surveys.
@ -29,7 +30,7 @@ export class AddonModSurveyHelperProvider {
* @param value Value to convert.
* @return Array.
*/
protected commaStringToArray(value: any): string[] {
protected commaStringToArray(value: string | string[]): string[] {
if (typeof value == 'string') {
if (value.length > 0) {
return value.split(',');
@ -47,7 +48,7 @@ export class AddonModSurveyHelperProvider {
* @param questions Questions.
* @return Object with parent questions.
*/
protected getParentQuestions(questions: any[]): any {
protected getParentQuestions(questions: AddonModSurveyQuestion[]): {[id: number]: AddonModSurveyQuestion} {
const parents = {};
questions.forEach((question) => {
@ -66,24 +67,24 @@ export class AddonModSurveyHelperProvider {
* @param questions Questions.
* @return Promise resolved with the formatted questions.
*/
formatQuestions(questions: any[]): any[] {
formatQuestions(questions: AddonModSurveyQuestion[]): AddonModSurveyQuestionFormatted[] {
const strIPreferThat = this.translate.instant('addon.mod_survey.ipreferthat'),
strIFoundThat = this.translate.instant('addon.mod_survey.ifoundthat'),
strChoose = this.translate.instant('core.choose'),
formatted = [],
formatted: AddonModSurveyQuestionFormatted[] = [],
parents = this.getParentQuestions(questions);
let num = 1;
questions.forEach((question) => {
// Copy the object to prevent modifying the original.
const q1 = Object.assign({}, question),
const q1: AddonModSurveyQuestionFormatted = Object.assign({}, question),
parent = parents[q1.parent];
// Turn multi and options into arrays.
q1.multi = this.commaStringToArray(q1.multi);
q1.options = this.commaStringToArray(q1.options);
q1.multiArray = this.commaStringToArray(q1.multi);
q1.optionsArray = this.commaStringToArray(q1.options);
if (parent) {
// It's a sub-question.
@ -114,7 +115,7 @@ export class AddonModSurveyHelperProvider {
q1.name = 'q' + q1.id;
q1.num = num++;
if (q1.type > 0) { // Add "choose" option since this question is not required.
q1.options.unshift(strChoose);
q1.optionsArray.unshift(strChoose);
}
}
@ -123,5 +124,15 @@ export class AddonModSurveyHelperProvider {
return formatted;
}
}
/**
* Survey question with some calculated data.
*/
export type AddonModSurveyQuestionFormatted = AddonModSurveyQuestion & {
required?: boolean; // Calculated in the app. Whether the question is required.
name?: string; // Calculated in the app. The name of the question.
num?: number; // Calculated in the app. Number of the question.
multiArray?: string[]; // Subquestions ids, converted to an array.
optionsArray?: string[]; // Question options, converted to an array.
};

View File

@ -21,6 +21,7 @@ import { CoreFilepoolProvider } from '@providers/filepool';
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
import { AddonModSurveyOfflineProvider } from './offline';
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
/**
* Service that provides some features for surveys.
@ -46,7 +47,7 @@ export class AddonModSurveyProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the questions are retrieved.
*/
getQuestions(surveyId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
getQuestions(surveyId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModSurveyQuestion[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
surveyid: surveyId
@ -61,7 +62,9 @@ export class AddonModSurveyProvider {
preSets.emergencyCache = false;
}
return site.read('mod_survey_get_questions', params, preSets).then((response) => {
return site.read('mod_survey_get_questions', params, preSets)
.then((response: AddonModSurveyGetQuestionsResult): any => {
if (response.questions) {
return response.questions;
}
@ -101,7 +104,9 @@ export class AddonModSurveyProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the survey is retrieved.
*/
protected getSurveyDataByKey(courseId: number, key: string, value: any, ignoreCache?: boolean, siteId?: string): Promise<any> {
protected getSurveyDataByKey(courseId: number, key: string, value: any, ignoreCache?: boolean, siteId?: string)
: Promise<AddonModSurveySurvey> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
courseids: [courseId]
@ -116,7 +121,9 @@ export class AddonModSurveyProvider {
preSets.emergencyCache = false;
}
return site.read('mod_survey_get_surveys_by_courses', params, preSets).then((response) => {
return site.read('mod_survey_get_surveys_by_courses', params, preSets)
.then((response: AddonModSurveyGetSurveysByCoursesResult): any => {
if (response && response.surveys) {
const currentSurvey = response.surveys.find((survey) => {
return survey[key] == value;
@ -140,7 +147,7 @@ export class AddonModSurveyProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the survey is retrieved.
*/
getSurvey(courseId: number, cmId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
getSurvey(courseId: number, cmId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModSurveySurvey> {
return this.getSurveyDataByKey(courseId, 'coursemodule', cmId, ignoreCache, siteId);
}
@ -153,7 +160,7 @@ export class AddonModSurveyProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the survey is retrieved.
*/
getSurveyById(courseId: number, id: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
getSurveyById(courseId: number, id: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModSurveySurvey> {
return this.getSurveyDataByKey(courseId, 'id', id, ignoreCache, siteId);
}
@ -277,17 +284,16 @@ export class AddonModSurveyProvider {
* @param surveyId Survey ID.
* @param answers Answers.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when answers are successfully submitted. Rejected with object containing
* the error message (if any) and a boolean indicating if the error was returned by WS.
* @return Promise resolved when answers are successfully submitted.
*/
submitAnswersOnline(surveyId: number, answers: any[], siteId?: string): Promise<any> {
submitAnswersOnline(surveyId: number, answers: any[], siteId?: string): Promise<void> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
surveyid: surveyId,
answers: answers
};
return site.write('mod_survey_submit_answers', params).then((response) => {
return site.write('mod_survey_submit_answers', params).then((response: AddonModSurveySubmitAnswersResult) => {
if (!response.status) {
return Promise.reject(this.utils.createFakeWSError(''));
}
@ -295,3 +301,64 @@ export class AddonModSurveyProvider {
});
}
}
/**
* Survey returned by WS mod_survey_get_surveys_by_courses.
*/
export type AddonModSurveySurvey = {
id: number; // Survey id.
coursemodule: number; // Course module id.
course: number; // Course id.
name: string; // Survey name.
intro?: string; // The Survey intro.
introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
introfiles?: CoreWSExternalFile[]; // @since 3.2.
template?: number; // Survey type.
days?: number; // Days.
questions?: string; // Question ids.
surveydone?: number; // Did I finish the survey?.
timecreated?: number; // Time of creation.
timemodified?: number; // Time of last modification.
section?: number; // Course section id.
visible?: number; // Visible.
groupmode?: number; // Group mode.
groupingid?: number; // Group id.
};
/**
* Survey question.
*/
export type AddonModSurveyQuestion = {
id: number; // Question id.
text: string; // Question text.
shorttext: string; // Question short text.
multi: string; // Subquestions ids.
intro: string; // The question intro.
type: number; // Question type.
options: string; // Question options.
parent: number; // Parent question (for subquestions).
};
/**
* Result of WS mod_survey_get_questions.
*/
export type AddonModSurveyGetQuestionsResult = {
questions: AddonModSurveyQuestion[];
warnings?: CoreWSExternalWarning[];
};
/**
* Result of WS mod_survey_get_surveys_by_courses.
*/
export type AddonModSurveyGetSurveysByCoursesResult = {
surveys: AddonModSurveySurvey[];
warnings?: CoreWSExternalWarning[];
};
/**
* Result of WS mod_survey_submit_answers.
*/
export type AddonModSurveySubmitAnswersResult = {
status: boolean; // Status: true if success.
warnings?: CoreWSExternalWarning[];
};