diff --git a/src/addon/mod/feedback/lang/en.json b/src/addon/mod/feedback/lang/en.json index 113036f44..f9a3b44a2 100644 --- a/src/addon/mod/feedback/lang/en.json +++ b/src/addon/mod/feedback/lang/en.json @@ -1,18 +1,26 @@ { "analysis": "Analysis", "anonymous": "Anonymous", + "anonymous_entries": "Anonymous entries ({{$a}})", "average": "Average", "completed_feedbacks": "Submitted answers", "complete_the_form": "Answer the questions...", "continue_the_form": "Continue the form", "feedbackclose": "Allow answers to", "feedbackopen": "Allow answers from", + "feedback_is_not_open": "The feedback is not open", "mode": "Mode", "non_anonymous": "User's name will be logged and shown with answers", + "non_anonymous_entries": "Non anonymous entries ({{$a}})", + "non_respondents_students": "Non respondents students ({{$a}})", + "not_started": "Not started", "overview": "Overview", "page_after_submit": "Completion message", "preview": "Preview", "questions": "Questions", + "responses": "Responses", + "response_nr": "Response number", "show_nonrespondents": "Show non-respondents", + "started": "Started", "this_feedback_is_already_submitted": "You've already completed this activity." } \ No newline at end of file diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html new file mode 100644 index 000000000..906dfbd6b --- /dev/null +++ b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html @@ -0,0 +1,45 @@ + + + {{ 'addon.mod_feedback.responses' |translate }} + + + + + + + + + + {{ 'core.groupsseparate' | translate }} + {{ 'core.groupsvisible' | translate }} + + {{groupOpt.name}} + + + + {{ 'addon.mod_feedback.non_respondents_students' | translate : {$a: total } }} + + + + + + +

+

+ + {{ 'addon.mod_feedback.started' | translate}} + + + {{ 'addon.mod_feedback.not_started' | translate}} + +

+
+
+ + + + + +
+
+
diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.module.ts b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.module.ts new file mode 100644 index 000000000..2197062a0 --- /dev/null +++ b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.module.ts @@ -0,0 +1,35 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreComponentsModule } from '@components/components.module'; +import { AddonModFeedbackComponentsModule } from '../../components/components.module'; +import { AddonModFeedbackNonRespondentsPage } from './nonrespondents'; + +@NgModule({ + declarations: [ + AddonModFeedbackNonRespondentsPage, + ], + imports: [ + CoreDirectivesModule, + CoreComponentsModule, + AddonModFeedbackComponentsModule, + IonicPageModule.forChild(AddonModFeedbackNonRespondentsPage), + TranslateModule.forChild() + ], +}) +export class AddonModFeedbackNonRespondentsPageModule {} diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts new file mode 100644 index 000000000..1ca3de974 --- /dev/null +++ b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts @@ -0,0 +1,159 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component } from '@angular/core'; +import { IonicPage, NavParams, NavController } from 'ionic-angular'; +import { AddonModFeedbackProvider } from '../../providers/feedback'; +import { AddonModFeedbackHelperProvider } from '../../providers/helper'; +import { CoreGroupInfo, CoreGroupsProvider } from '@providers/groups'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; + +/** + * Page that displays feedback non respondents. + */ +@IonicPage({ segment: 'addon-mod-feedback-nonrespondents' }) +@Component({ + selector: 'page-addon-mod-feedback-nonrespondents', + templateUrl: 'nonrespondents.html', +}) +export class AddonModFeedbackNonRespondentsPage { + + protected moduleId: number; + protected feedbackId: number; + protected courseId: number; + protected page = 0; + + selectedGroup: number; + groupInfo: CoreGroupInfo = { + groups: [], + separateGroups: false, + visibleGroups: false + }; + + users = []; + total = 0; + canLoadMore = false; + + feedbackLoaded = false; + loadingMore = false; + + constructor(navParams: NavParams, protected feedbackProvider: AddonModFeedbackProvider, + protected groupsProvider: CoreGroupsProvider, protected domUtils: CoreDomUtilsProvider, + protected feedbackHelper: AddonModFeedbackHelperProvider, protected navCtrl: NavController) { + const module = navParams.get('module'); + this.moduleId = module.id; + this.feedbackId = module.instance; + this.courseId = navParams.get('courseId'); + this.selectedGroup = navParams.get('group') || 0; + } + + /** + * View loaded. + */ + ionViewDidLoad(): void { + this.fetchData(); + } + + /** + * Fetch all the data required for the view. + * + * @param {boolean} [refresh] Empty events array first. + * @return {Promise} Promise resolved when done. + */ + fetchData(refresh: boolean = false): Promise { + this.page = 0; + this.total = 0; + this.users = []; + + return this.groupsProvider.getActivityGroupInfo(this.moduleId).then((groupInfo) => { + this.groupInfo = groupInfo; + + return this.loadGroupUsers(this.selectedGroup); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); + + if (!refresh) { + // Some call failed on first fetch, go back. + this.navCtrl.pop(); + } + + return Promise.reject(null); + }); + } + + /** + * Load Group responses. + * + * @param {number} [groupId] If defined it will change group if not, it will load more users for the same group. + * @return {Promise} Resolved with the attempts loaded. + */ + protected loadGroupUsers(groupId?: number): Promise { + if (typeof groupId == 'undefined') { + this.page++; + this.loadingMore = true; + } else { + this.selectedGroup = groupId; + this.page = 0; + this.total = 0; + this.users = []; + this.feedbackLoaded = false; + } + + return this.feedbackHelper.getNonRespondents(this.feedbackId, this.selectedGroup, this.page).then((response) => { + this.total = response.total; + + if (this.users.length < response.total) { + this.users = this.users.concat(response.users); + } + + this.canLoadMore = this.users.length < response.total; + + return response; + }).finally(() => { + this.loadingMore = false; + this.feedbackLoaded = true; + }); + } + + /** + * Change selected group or load more users. + * + * @param {number} [groupId] Group ID selected. If not defined, it will load more users. + */ + loadAttempts(groupId?: number): void { + this.loadGroupUsers(groupId).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); + }); + } + + /** + * Refresh the attempts. + * + * @param {any} refresher Refresher. + */ + refreshFeedback(refresher: any): void { + if (this.feedbackLoaded) { + const promises = []; + + promises.push(this.feedbackProvider.invalidateNonRespondentsData(this.feedbackId)); + promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.moduleId)); + + Promise.all(promises).finally(() => { + return this.fetchData(true); + }).finally(() => { + refresher.complete(); + }); + } + } +} diff --git a/src/addon/mod/feedback/pages/respondents/respondents.html b/src/addon/mod/feedback/pages/respondents/respondents.html new file mode 100644 index 000000000..f9e2c2dd5 --- /dev/null +++ b/src/addon/mod/feedback/pages/respondents/respondents.html @@ -0,0 +1,53 @@ + + + {{ 'addon.mod_feedback.responses' |translate }} + + + + + + + + + + + {{ 'core.groupsseparate' | translate }} + {{ 'core.groupsvisible' | translate }} + + {{groupOpt.name}} + + + + + {{ 'addon.mod_feedback.non_anonymous_entries' | translate : {$a: responses.total } }} + + + + + +

+

{{attempt.timemodified * 1000 | coreFormatDate: "LLL"}}

+
+ + + + + +
+ + + {{ 'addon.mod_feedback.anonymous_entries' |translate : {$a: anonResponses.total } }} + + +

{{ 'addon.mod_feedback.response_nr' |translate }}: {{attempt.number}}

+
+ + + + + +
+
+
+
+
diff --git a/src/addon/mod/feedback/pages/respondents/respondents.module.ts b/src/addon/mod/feedback/pages/respondents/respondents.module.ts new file mode 100644 index 000000000..d81210084 --- /dev/null +++ b/src/addon/mod/feedback/pages/respondents/respondents.module.ts @@ -0,0 +1,37 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreComponentsModule } from '@components/components.module'; +import { CorePipesModule } from '@pipes/pipes.module'; +import { AddonModFeedbackComponentsModule } from '../../components/components.module'; +import { AddonModFeedbackRespondentsPage } from './respondents'; + +@NgModule({ + declarations: [ + AddonModFeedbackRespondentsPage, + ], + imports: [ + CoreDirectivesModule, + CoreComponentsModule, + CorePipesModule, + AddonModFeedbackComponentsModule, + IonicPageModule.forChild(AddonModFeedbackRespondentsPage), + TranslateModule.forChild() + ], +}) +export class AddonModFeedbackRespondentsPageModule {} diff --git a/src/addon/mod/feedback/pages/respondents/respondents.ts b/src/addon/mod/feedback/pages/respondents/respondents.ts new file mode 100644 index 000000000..f281c5374 --- /dev/null +++ b/src/addon/mod/feedback/pages/respondents/respondents.ts @@ -0,0 +1,202 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, ViewChild } from '@angular/core'; +import { IonicPage, NavParams, NavController } from 'ionic-angular'; +import { AddonModFeedbackProvider } from '../../providers/feedback'; +import { AddonModFeedbackHelperProvider } from '../../providers/helper'; +import { CoreGroupInfo, CoreGroupsProvider } from '@providers/groups'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreSplitViewComponent } from '@components/split-view/split-view'; + +/** + * Page that displays feedback respondents. + */ +@IonicPage({ segment: 'addon-mod-feedback-respondents' }) +@Component({ + selector: 'page-addon-mod-feedback-respondents', + templateUrl: 'respondents.html', +}) +export class AddonModFeedbackRespondentsPage { + @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; + + protected moduleId: number; + protected feedbackId: number; + protected courseId: number; + protected page = 0; + + selectedGroup: number; + groupInfo: CoreGroupInfo = { + groups: [], + separateGroups: false, + visibleGroups: false + }; + + responses = { + attempts: [], + total: 0, + canLoadMore: false + }; + anonResponses = { + attempts: [], + total: 0, + canLoadMore: false + }; + feedbackLoaded = false; + loadingMore = false; + attemptId: number; + + constructor(navParams: NavParams, protected feedbackProvider: AddonModFeedbackProvider, + protected groupsProvider: CoreGroupsProvider, protected domUtils: CoreDomUtilsProvider, + protected feedbackHelper: AddonModFeedbackHelperProvider, protected navCtrl: NavController) { + const module = navParams.get('module'); + this.moduleId = module.id; + this.feedbackId = module.instance; + this.courseId = navParams.get('courseId'); + this.selectedGroup = navParams.get('group') || 0; + } + + /** + * View loaded. + */ + ionViewDidLoad(): void { + this.fetchData().then(() => { + if (this.splitviewCtrl.isOn()) { + if (this.responses.attempts.length > 0) { + // Take first and load it. + this.gotoAttempt(this.responses.attempts[0]); + } else if (this.anonResponses.attempts.length > 0) { + // Take first and load it. + this.gotoAttempt(this.anonResponses.attempts[0]); + } + } + }); + } + + /** + * Fetch all the data required for the view. + * + * @param {boolean} [refresh] Empty events array first. + * @return {Promise} Promise resolved when done. + */ + fetchData(refresh: boolean = false): Promise { + this.page = 0; + this.responses.total = 0; + this.responses.attempts = []; + this.anonResponses.total = 0; + this.anonResponses.attempts = []; + + return this.groupsProvider.getActivityGroupInfo(this.moduleId).then((groupInfo) => { + this.groupInfo = groupInfo; + + return this.loadGroupAttempts(this.selectedGroup); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); + + if (!refresh) { + // Some call failed on first fetch, go back. + this.navCtrl.pop(); + } + + return Promise.reject(null); + }); + } + + /** + * Load Group attempts. + * + * @param {number} [groupId] If defined it will change group if not, it will load more attempts for the same group. + * @return {Promise} Resolved with the attempts loaded. + */ + protected loadGroupAttempts(groupId?: number): Promise { + if (typeof groupId == 'undefined') { + this.page++; + this.loadingMore = true; + } else { + this.selectedGroup = groupId; + this.page = 0; + this.responses.total = 0; + this.responses.attempts = []; + this.anonResponses.total = 0; + this.anonResponses.attempts = []; + this.feedbackLoaded = false; + } + + return this.feedbackHelper.getResponsesAnalysis(this.feedbackId, this.selectedGroup, this.page).then((responses) => { + this.responses.total = responses.totalattempts; + this.anonResponses.total = responses.totalanonattempts; + + if (this.anonResponses.attempts.length < responses.totalanonattempts) { + this.anonResponses.attempts = this.anonResponses.attempts.concat(responses.anonattempts); + } + if (this.responses.attempts.length < responses.totalattempts) { + this.responses.attempts = this.responses.attempts.concat(responses.attempts); + } + + this.anonResponses.canLoadMore = this.anonResponses.attempts.length < responses.totalanonattempts; + this.responses.canLoadMore = this.responses.attempts.length < responses.totalattempts; + + return responses; + }).finally(() => { + this.loadingMore = false; + this.feedbackLoaded = true; + }); + } + + /** + * Navigate to a particular attempt. + * + * @param {any} attempt Attempt object to load. + */ + gotoAttempt(attempt: any): void { + this.attemptId = attempt.id; + this.splitviewCtrl.push('AddonModFeedbackAttemptPage', { + attemptid: attempt.id, + attempt: attempt, + feedbackId: this.feedbackId, + moduleId: this.moduleId + }); + } + + /** + * Change selected group or load more attempts. + * + * @param {number} [groupId] Group ID selected. If not defined, it will load more attempts. + */ + loadAttempts(groupId?: number): void { + this.loadGroupAttempts(groupId).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); + }); + } + + /** + * Refresh the attempts. + * + * @param {any} refresher Refresher. + */ + refreshFeedback(refresher: any): void { + if (this.feedbackLoaded) { + const promises = []; + + promises.push(this.feedbackProvider.invalidateResponsesAnalysisData(this.feedbackId)); + promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.moduleId)); + + Promise.all(promises).finally(() => { + return this.fetchData(true); + }).finally(() => { + refresher.complete(); + }); + } + } +} diff --git a/src/addon/mod/feedback/providers/feedback.ts b/src/addon/mod/feedback/providers/feedback.ts index e954024d8..7c960b4e5 100644 --- a/src/addon/mod/feedback/providers/feedback.ts +++ b/src/addon/mod/feedback/providers/feedback.ts @@ -298,6 +298,96 @@ export class AddonModFeedbackProvider { return this.getFeedbackDataByKey(courseId, 'id', id, siteId, forceCache); } + /** + * Retrieves a list of students who didn't submit the feedback. + * + * @param {number} feedbackId Feedback ID. + * @param {number} [groupId=0] Group id, 0 means that the function will determine the user group. + * @param {number} [page=0] The page of records to return. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the info is retrieved. + */ + getNonRespondents(feedbackId: number, groupId: number = 0, page: number = 0, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + const params = { + feedbackid: feedbackId, + groupid: groupId, + page: page + }, + preSets = { + cacheKey: this.getNonRespondentsDataCacheKey(feedbackId, groupId) + }; + + return site.read('mod_feedback_get_non_respondents', params, preSets); + }); + } + + /** + * Get cache key for non respondents feedback data WS calls. + * + * @param {number} feedbackId Feedback ID. + * @param {number} [groupId=0] Group id, 0 means that the function will determine the user group. + * @return {string} Cache key. + */ + protected getNonRespondentsDataCacheKey(feedbackId: number, groupId: number = 0): string { + return this.getNonRespondentsDataPrefixCacheKey(feedbackId) + groupId; + } + + /** + * Get prefix cache key for feedback non respondents data WS calls. + * + * @param {number} feedbackId Feedback ID. + * @return {string} Cache key. + */ + protected getNonRespondentsDataPrefixCacheKey(feedbackId: number): string { + return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':nonrespondents:'; + } + + /** + * Returns the feedback user responses. + * + * @param {number} feedbackId Feedback ID. + * @param {number} groupId Group id, 0 means that the function will determine the user group. + * @param {number} page The page of records to return. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the info is retrieved. + */ + getResponsesAnalysis(feedbackId: number, groupId: number, page: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + const params = { + feedbackid: feedbackId, + groupid: groupId || 0, + page: page || 0 + }, + preSets = { + cacheKey: this.getResponsesAnalysisDataCacheKey(feedbackId, groupId) + }; + + return site.read('mod_feedback_get_responses_analysis', params, preSets); + }); + } + + /** + * Get cache key for responses analysis feedback data WS calls. + * + * @param {number} feedbackId Feedback ID. + * @param {number} [groupId=0] Group id, 0 means that the function will determine the user group. + * @return {string} Cache key. + */ + protected getResponsesAnalysisDataCacheKey(feedbackId: number, groupId: number = 0): string { + return this.getResponsesAnalysisDataPrefixCacheKey(feedbackId) + groupId; + } + + /** + * Get prefix cache key for feedback responses analysis data WS calls. + * + * @param {number} feedbackId Feedback ID. + * @return {string} Cache key. + */ + protected getResponsesAnalysisDataPrefixCacheKey(feedbackId: number): string { + return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':responsesanalysis:'; + } + /** * Gets the resume page information. * @@ -449,6 +539,34 @@ export class AddonModFeedbackProvider { return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModFeedbackProvider.COMPONENT, moduleId); } + /** + * Invalidates feedback non respondents record data. + * + * @param {number} feedbackId Feedback ID. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateNonRespondentsData(feedbackId: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKeyStartingWith(this.getNonRespondentsDataPrefixCacheKey(feedbackId)); + + }); + } + + /** + * Invalidates feedback user responses record data. + * + * @param {number} feedbackId Feedback ID. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateResponsesAnalysisData(feedbackId: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKeyStartingWith(this.getResponsesAnalysisDataPrefixCacheKey(feedbackId)); + + }); + } + /** * Invalidates launch feedback data. * diff --git a/src/addon/mod/feedback/providers/helper.ts b/src/addon/mod/feedback/providers/helper.ts index 7d83e1fd1..81a04a919 100644 --- a/src/addon/mod/feedback/providers/helper.ts +++ b/src/addon/mod/feedback/providers/helper.ts @@ -14,6 +14,8 @@ import { Injectable } from '@angular/core'; import { NavController } from 'ionic-angular'; +import { AddonModFeedbackProvider } from './feedback'; +import { CoreUserProvider } from '@core/user/providers/user'; /** * Service that provides helper functions for feedbacks. @@ -21,6 +23,9 @@ import { NavController } from 'ionic-angular'; @Injectable() export class AddonModFeedbackHelperProvider { + constructor(protected feedbackProvider: AddonModFeedbackProvider, protected userProvider: CoreUserProvider) { + } + /** * Check if the page we are going to open is in the history and returns the number of pages in the stack to go back. * @@ -63,6 +68,62 @@ export class AddonModFeedbackHelperProvider { return 0; } + /** + * Retrieves a list of students who didn't submit the feedback with extra info. + * + * @param {number} feedbackId Feedback ID. + * @param {number} groupId Group id, 0 means that the function will determine the user group. + * @param {number} page The page of records to return. + * @return {Promise} Promise resolved when the info is retrieved. + */ + getNonRespondents(feedbackId: number, groupId: number, page: number): Promise { + return this.feedbackProvider.getNonRespondents(feedbackId, groupId, page).then((responses) => { + return this.addImageProfileToAttempts(responses.users).then((users) => { + responses.users = users; + + return responses; + }); + }); + } + + /** + * Returns the feedback user responses with extra info. + * + * @param {number} feedbackId Feedback ID. + * @param {number} groupId Group id, 0 means that the function will determine the user group. + * @param {number} page The page of records to return. + * @return {Promise} Promise resolved when the info is retrieved. + */ + getResponsesAnalysis(feedbackId: number, groupId: number, page: number): Promise { + return this.feedbackProvider.getResponsesAnalysis(feedbackId, groupId, page).then((responses) => { + return this.addImageProfileToAttempts(responses.attempts).then((attempts) => { + responses.attempts = attempts; + + return responses; + }); + }); + } + + /** + * Add Image profile url field on attempts + * + * @param {any} attempts Attempts array to get profile from. + * @return {Promise} Returns the same array with the profileimageurl added if found. + */ + protected addImageProfileToAttempts(attempts: any): Promise { + const promises = attempts.map((attempt) => { + return this.userProvider.getProfile(attempt.userid, attempt.courseid, true).then((user) => { + attempt.profileimageurl = user.profileimageurl; + }).catch(() => { + // Error getting profile, resolve promise without adding any extra data. + }); + }); + + return Promise.all(promises).then(() => { + return attempts; + }); + } + /** * Helper function to open a feature in the app. *