MOBILE-3109 choice: Add return types to choice

main
Dani Palou 2019-09-06 17:31:47 +02:00
parent 49deffa9dd
commit 3647b7bfa0
3 changed files with 139 additions and 26 deletions

View File

@ -19,13 +19,13 @@
<!-- Activity availability messages -->
<ion-card class="core-info-card" icon-start *ngIf="choiceNotOpenYet">
<ion-icon name="information-circle"></ion-icon>
<p *ngIf="options && options.length">{{ 'addon.mod_choice.previewonly' | translate:{$a: choice.openTimeReadable} }}</p>
<p *ngIf="!options || !options.length">{{ 'addon.mod_choice.notopenyet' | translate:{$a: choice.openTimeReadable} }}</p>
<p *ngIf="options && options.length">{{ 'addon.mod_choice.previewonly' | translate:{$a: openTimeReadable} }}</p>
<p *ngIf="!options || !options.length">{{ 'addon.mod_choice.notopenyet' | translate:{$a: openTimeReadable} }}</p>
</ion-card>
<ion-card class="core-info-card" icon-start *ngIf="choiceClosed">
<ion-icon name="information-circle"></ion-icon>
<p *ngIf="options && options.length">{{ 'addon.mod_choice.yourselection' | translate }} <core-format-text [text]="options[0].text"></core-format-text></p>
<p>{{ 'addon.mod_choice.expired' | translate:{$a: choice.closeTimeReadable} }}</p>
<p>{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}</p>
</ion-card>
<!-- Choice done in offline but not synchronized -->
@ -80,7 +80,7 @@
<ion-item-group *ngFor="let result of results">
<ion-item-divider text-wrap>
<h2><core-format-text [text]="result.text"></core-format-text></h2>
<p>{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }} ({{ 'core.percentagenumber' | translate: {$a: result.percentageamount} }})</p>
<p>{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }} ({{ 'core.percentagenumber' | translate: {$a: result.percentageamountfixed} }})</p>
</ion-item-divider>
<a ion-item *ngFor="let user of result.userresponses" core-user-link [courseId]="courseid" [userId]="user.userid" [title]="user.fullname" text-wrap>
<ion-avatar core-user-avatar [user]="user" item-start [courseId]="courseid"></ion-avatar>

View File

@ -16,7 +16,7 @@ import { Component, Optional, Injector } from '@angular/core';
import { Content } from 'ionic-angular';
import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
import { AddonModChoiceProvider } from '../../providers/choice';
import { AddonModChoiceProvider, AddonModChoiceChoice, AddonModChoiceOption, AddonModChoiceResult } from '../../providers/choice';
import { AddonModChoiceOfflineProvider } from '../../providers/offline';
import { AddonModChoiceSyncProvider } from '../../providers/sync';
@ -31,9 +31,9 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
component = AddonModChoiceProvider.COMPONENT;
moduleName = 'choice';
choice: any;
options = [];
selectedOption: any;
choice: AddonModChoiceChoice;
options: AddonModChoiceOption[] = [];
selectedOption: {id: number};
choiceNotOpenYet = false;
choiceClosed = false;
canEdit = false;
@ -43,6 +43,8 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
labels = [];
results = [];
publishInfo: string; // Message explaining the user what will happen with his choices.
openTimeReadable: string;
closeTimeReadable: string;
protected userId: number;
protected syncEventName = AddonModChoiceSyncProvider.AUTO_SYNCED;
@ -122,12 +124,12 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
return this.choiceProvider.getChoice(this.courseId, this.module.id).then((choice) => {
this.choice = choice;
this.choice.timeopen = parseInt(choice.timeopen) * 1000;
this.choice.openTimeReadable = this.timeUtils.userDate(choice.timeopen);
this.choice.timeclose = parseInt(choice.timeclose) * 1000;
this.choice.closeTimeReadable = this.timeUtils.userDate(choice.timeclose);
this.choice.timeopen = choice.timeopen * 1000;
this.choice.timeclose = choice.timeclose * 1000;
this.openTimeReadable = this.timeUtils.userDate(choice.timeopen);
this.closeTimeReadable = this.timeUtils.userDate(choice.timeclose);
this.description = choice.intro || choice.description;
this.description = choice.intro;
this.choiceNotOpenYet = choice.timeopen && choice.timeopen > this.now;
this.choiceClosed = choice.timeclose && choice.timeclose <= this.now;
@ -175,7 +177,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
if (hasOffline) {
promise = this.choiceOffline.getResponse(this.choice.id).then((response) => {
const optionsKeys = {};
const optionsKeys: {[id: number]: AddonModChoiceOption} = {};
options.forEach((option) => {
optionsKeys[option.id] = option;
});
@ -223,7 +225,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
promise = Promise.resolve(options);
}
promise.then((options) => {
promise.then((options: AddonModChoiceOption[]) => {
const isOpen = this.isChoiceOpen();
let hasAnswered = false;
@ -291,11 +293,11 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
let hasVotes = false;
this.data = [];
this.labels = [];
results.forEach((result) => {
results.forEach((result: AddonModChoiceResultFormatted) => {
if (result.numberofuser > 0) {
hasVotes = true;
}
result.percentageamount = parseFloat(result.percentageamount).toFixed(1);
result.percentageamountfixed = result.percentageamount.toFixed(1);
this.data.push(result.numberofuser);
this.labels.push(result.text);
});
@ -429,3 +431,10 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
return result.updated;
}
}
/**
* Choice result with some calculated data.
*/
export type AddonModChoiceResultFormatted = AddonModChoiceResult & {
percentageamountfixed: string; // Percentage of users answers with fixed decimals.
};

View File

@ -20,6 +20,7 @@ import { CoreFilepoolProvider } from '@providers/filepool';
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
import { AddonModChoiceOfflineProvider } from './offline';
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
/**
* Service that provides some features for choices.
@ -118,7 +119,9 @@ export class AddonModChoiceProvider {
responses: responses
};
return site.write('mod_choice_delete_choice_responses', params).then((response) => {
return site.write('mod_choice_delete_choice_responses', params)
.then((response: AddonModChoiceDeleteChoiceResponsesResult) => {
// Other errors ocurring.
if (!response || response.status === false) {
return Promise.reject(this.utils.createFakeWSError(''));
@ -179,7 +182,7 @@ export class AddonModChoiceProvider {
* @return Promise resolved when the choice is retrieved.
*/
protected getChoiceByDataKey(siteId: string, courseId: number, key: string, value: any, forceCache?: boolean,
ignoreCache?: boolean): Promise<any> {
ignoreCache?: boolean): Promise<AddonModChoiceChoice> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
@ -198,7 +201,9 @@ export class AddonModChoiceProvider {
preSets.emergencyCache = false;
}
return site.read('mod_choice_get_choices_by_courses', params, preSets).then((response) => {
return site.read('mod_choice_get_choices_by_courses', params, preSets)
.then((response: AddonModChoiceGetChoicesByCoursesResult): any => {
if (response && response.choices) {
const currentChoice = response.choices.find((choice) => choice[key] == value);
if (currentChoice) {
@ -221,7 +226,8 @@ export class AddonModChoiceProvider {
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
* @return Promise resolved when the choice is retrieved.
*/
getChoice(courseId: number, cmId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean): Promise<any> {
getChoice(courseId: number, cmId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean)
: Promise<AddonModChoiceChoice> {
return this.getChoiceByDataKey(siteId, courseId, 'coursemodule', cmId, forceCache, ignoreCache);
}
@ -235,7 +241,8 @@ export class AddonModChoiceProvider {
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
* @return Promise resolved when the choice is retrieved.
*/
getChoiceById(courseId: number, choiceId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean): Promise<any> {
getChoiceById(courseId: number, choiceId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean)
: Promise<AddonModChoiceChoice> {
return this.getChoiceByDataKey(siteId, courseId, 'id', choiceId, forceCache, ignoreCache);
}
@ -247,7 +254,7 @@ export class AddonModChoiceProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with choice options.
*/
getOptions(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
getOptions(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModChoiceOption[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
choiceid: choiceId
@ -262,7 +269,9 @@ export class AddonModChoiceProvider {
preSets.emergencyCache = false;
}
return site.read('mod_choice_get_choice_options', params, preSets).then((response) => {
return site.read('mod_choice_get_choice_options', params, preSets)
.then((response: AddonModChoiceGetChoiceOptionsResult): any => {
if (response.options) {
return response.options;
}
@ -280,7 +289,7 @@ export class AddonModChoiceProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with choice results.
*/
getResults(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
getResults(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModChoiceResult[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
choiceid: choiceId
@ -294,7 +303,9 @@ export class AddonModChoiceProvider {
preSets.emergencyCache = false;
}
return site.read('mod_choice_get_choice_results', params, preSets).then((response) => {
return site.read('mod_choice_get_choice_results', params, preSets)
.then((response: AddonModChoiceGetChoiceResults): any => {
if (response.options) {
return response.options;
}
@ -456,3 +467,96 @@ export class AddonModChoiceProvider {
});
}
}
/**
* Choice returned by mod_choice_get_choices_by_courses.
*/
export type AddonModChoiceChoice = {
id: number; // Choice instance id.
coursemodule: number; // Course module id.
course: number; // Course id.
name: string; // Choice name.
intro: string; // The choice intro.
introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
introfiles?: CoreWSExternalFile[]; // @since 3.2.
publish?: boolean; // If choice is published.
showresults?: number; // 0 never, 1 after answer, 2 after close, 3 always.
display?: number; // Display mode (vertical, horizontal).
allowupdate?: boolean; // Allow update.
allowmultiple?: boolean; // Allow multiple choices.
showunanswered?: boolean; // Show users who not answered yet.
includeinactive?: boolean; // Include inactive users.
limitanswers?: boolean; // Limit unswers.
timeopen?: number; // Date of opening validity.
timeclose?: number; // Date of closing validity.
showpreview?: boolean; // Show preview before timeopen.
timemodified?: number; // Time of last modification.
completionsubmit?: boolean; // Completion on user submission.
section?: number; // Course section id.
visible?: boolean; // Visible.
groupmode?: number; // Group mode.
groupingid?: number; // Group id.
};
/**
* Option returned by mod_choice_get_choice_options.
*/
export type AddonModChoiceOption = {
id: number; // Option id.
text: string; // Text of the choice.
maxanswers: number; // Maximum number of answers.
displaylayout: boolean; // True for orizontal, otherwise vertical.
countanswers: number; // Number of answers.
checked: boolean; // We already answered.
disabled: boolean; // Option disabled.
};
/**
* Result returned by mod_choice_get_choice_results.
*/
export type AddonModChoiceResult = {
id: number; // Choice instance id.
text: string; // Text of the choice.
maxanswer: number; // Maximum number of answers.
userresponses: {
userid: number; // User id.
fullname: string; // User full name.
profileimageurl: string; // Profile user image url.
answerid?: number; // Answer id.
timemodified?: number; // Time of modification.
}[];
numberofuser: number; // Number of users answers.
percentageamount: number; // Percentage of users answers.
};
/**
* Result of WS mod_choice_get_choices_by_courses.
*/
export type AddonModChoiceGetChoicesByCoursesResult = {
choices: AddonModChoiceChoice[];
warnings?: CoreWSExternalWarning[];
};
/**
* Result of WS mod_choice_get_choice_options.
*/
export type AddonModChoiceGetChoiceOptionsResult = {
options: AddonModChoiceOption[]; // Options.
warnings?: CoreWSExternalWarning[];
};
/**
* Result of WS mod_choice_get_choice_results.
*/
export type AddonModChoiceGetChoiceResults = {
options: AddonModChoiceResult[];
warnings?: CoreWSExternalWarning[];
};
/**
* Result of WS mod_choice_delete_choice_responses.
*/
export type AddonModChoiceDeleteChoiceResponsesResult = {
status: boolean; // Status, true if everything went right.
warnings?: CoreWSExternalWarning[];
};