From 3647b7bfa0885934c57f4d65cb0ee4f9fadf0401 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 6 Sep 2019 17:31:47 +0200 Subject: [PATCH] MOBILE-3109 choice: Add return types to choice --- .../index/addon-mod-choice-index.html | 8 +- .../mod/choice/components/index/index.ts | 35 +++-- src/addon/mod/choice/providers/choice.ts | 122 ++++++++++++++++-- 3 files changed, 139 insertions(+), 26 deletions(-) diff --git a/src/addon/mod/choice/components/index/addon-mod-choice-index.html b/src/addon/mod/choice/components/index/addon-mod-choice-index.html index a9e81b5f6..cfeaddc5f 100644 --- a/src/addon/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addon/mod/choice/components/index/addon-mod-choice-index.html @@ -19,13 +19,13 @@ -

{{ 'addon.mod_choice.previewonly' | translate:{$a: choice.openTimeReadable} }}

-

{{ 'addon.mod_choice.notopenyet' | translate:{$a: choice.openTimeReadable} }}

+

{{ 'addon.mod_choice.previewonly' | translate:{$a: openTimeReadable} }}

+

{{ 'addon.mod_choice.notopenyet' | translate:{$a: openTimeReadable} }}

{{ 'addon.mod_choice.yourselection' | translate }}

-

{{ 'addon.mod_choice.expired' | translate:{$a: choice.closeTimeReadable} }}

+

{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}

@@ -80,7 +80,7 @@

-

{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }} ({{ 'core.percentagenumber' | translate: {$a: result.percentageamount} }})

+

{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }} ({{ 'core.percentagenumber' | translate: {$a: result.percentageamountfixed} }})

diff --git a/src/addon/mod/choice/components/index/index.ts b/src/addon/mod/choice/components/index/index.ts index c5bcdc852..6e3b5799e 100644 --- a/src/addon/mod/choice/components/index/index.ts +++ b/src/addon/mod/choice/components/index/index.ts @@ -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. +}; diff --git a/src/addon/mod/choice/providers/choice.ts b/src/addon/mod/choice/providers/choice.ts index b16119516..246903592 100644 --- a/src/addon/mod/choice/providers/choice.ts +++ b/src/addon/mod/choice/providers/choice.ts @@ -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 { + ignoreCache?: boolean): Promise { 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 { + getChoice(courseId: number, cmId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean) + : Promise { 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 { + getChoiceById(courseId: number, choiceId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean) + : Promise { 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 { + getOptions(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise { 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 { + getResults(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise { 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[]; +};