Skip to content

Commit dc85f96

Browse files
committed
MOBILE-3413 h5pactivity: Prefetch attempts data
1 parent b48a27e commit dc85f96

File tree

5 files changed

+220
-84
lines changed

5 files changed

+220
-84
lines changed

src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!-- Buttons to add to the header. -->
22
<core-navbar-buttons end>
33
<core-context-menu>
4-
<core-context-menu-item *ngIf="h5pActivity && h5pActivity.enabletracking" [priority]="1000" [content]="'addon.mod_h5pactivity.review_my_attempts' | translate" (action)="viewMyAttempts()" [iconAction]="'stats'"></core-context-menu-item>
4+
<core-context-menu-item *ngIf="h5pActivity && h5pActivity.enabletracking && accessInfo && !accessInfo.canreviewattempts" [priority]="1000" [content]="'addon.mod_h5pactivity.review_my_attempts' | translate" (action)="viewMyAttempts()" [iconAction]="'stats'"></core-context-menu-item>
55
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item>
66
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></core-context-menu-item>
77
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog($event)"></core-context-menu-item>

src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit {
100100
* @return Promise resolved when done.
101101
*/
102102
protected async fetchAttempt(): Promise<void> {
103-
this.attempt = await AddonModH5PActivity.instance.getResults(this.h5pActivityId, this.attemptId);
103+
this.attempt = await AddonModH5PActivity.instance.getAttemptResults(this.h5pActivityId, this.attemptId);
104104
}
105105

106106
/**
@@ -109,7 +109,11 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit {
109109
* @return Promise resolved when done.
110110
*/
111111
protected async fetchUserProfile(): Promise<void> {
112-
this.user = await CoreUser.instance.getProfile(this.attempt.userid, this.courseId, true);
112+
try {
113+
this.user = await CoreUser.instance.getProfile(this.attempt.userid, this.courseId, true);
114+
} catch (error) {
115+
// Ignore errors.
116+
}
113117
}
114118

115119
/**

src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,11 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit {
110110
* @return Promise resolved when done.
111111
*/
112112
protected async fetchUserProfile(): Promise<void> {
113-
this.user = await CoreUser.instance.getProfile(this.userId, this.courseId, true);
113+
try {
114+
this.user = await CoreUser.instance.getProfile(this.userId, this.courseId, true);
115+
} catch (error) {
116+
// Ignore errors.
117+
}
114118
}
115119

116120
/**

src/addon/mod/h5pactivity/providers/h5pactivity.ts

Lines changed: 177 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Injectable } from '@angular/core';
1717
import { CoreSites } from '@providers/sites';
1818
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
1919
import { CoreTimeUtils } from '@providers/utils/time';
20+
import { CoreUtils } from '@providers/utils/utils';
2021
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
2122
import { CoreCourseLogHelper } from '@core/course/providers/log-helper';
2223
import { CoreH5P } from '@core/h5p/providers/h5p';
@@ -64,9 +65,9 @@ export class AddonModH5PActivityProvider {
6465
protected formatAttemptResults(attempt: AddonModH5PActivityWSAttemptResults): AddonModH5PActivityAttemptResults {
6566
const formattedAttempt: AddonModH5PActivityAttemptResults = this.formatAttempt(attempt);
6667

67-
for (const i in formattedAttempt.results) {
68-
formattedAttempt.results[i] = this.formatResult(formattedAttempt.results[i]);
69-
}
68+
formattedAttempt.results = formattedAttempt.results.map((result) => {
69+
return this.formatResult(result);
70+
});
7071

7172
return formattedAttempt;
7273
}
@@ -80,14 +81,15 @@ export class AddonModH5PActivityProvider {
8081
protected formatUserAttempts(data: AddonModH5PActivityWSUserAttempts): AddonModH5PActivityUserAttempts {
8182
const formatted: AddonModH5PActivityUserAttempts = data;
8283

83-
for (const i in formatted.attempts) {
84-
formatted.attempts[i] = this.formatAttempt(formatted.attempts[i]);
85-
}
84+
formatted.attempts = formatted.attempts.map((attempt) => {
85+
return this.formatAttempt(attempt);
86+
});
8687

8788
if (formatted.scored) {
88-
for (const i in formatted.scored.attempts) {
89-
formatted.scored.attempts[i] = this.formatAttempt(formatted.scored.attempts[i]);
90-
}
89+
90+
formatted.scored.attempts = formatted.scored.attempts.map((attempt) => {
91+
return this.formatAttempt(attempt);
92+
});
9193
}
9294

9395
return formatted;
@@ -137,6 +139,153 @@ export class AddonModH5PActivityProvider {
137139
return site.read('mod_h5pactivity_get_h5pactivity_access_information', params, preSets);
138140
}
139141

142+
/**
143+
* Get attempt results for all user attempts.
144+
*
145+
* @param id Activity ID.
146+
* @param options Other options.
147+
* @return Promise resolved with the results of the attempt.
148+
*/
149+
async getAllAttemptsResults(id: number, options?: AddonModH5PActivityGetAttemptResultsOptions)
150+
: Promise<AddonModH5PActivityAttemptsResults> {
151+
152+
const userAttempts = await AddonModH5PActivity.instance.getUserAttempts(id, options);
153+
154+
const attemptIds = userAttempts.attempts.map((attempt) => {
155+
return attempt.id;
156+
});
157+
158+
if (attemptIds.length) {
159+
// Get all the attempts with a single call.
160+
return AddonModH5PActivity.instance.getAttemptsResults(id, attemptIds, options);
161+
} else {
162+
// No attempts.
163+
return {
164+
activityid: id,
165+
attempts: [],
166+
warnings: [],
167+
};
168+
}
169+
}
170+
171+
/**
172+
* Get cache key for results WS calls.
173+
*
174+
* @param id Instance ID.
175+
* @param attemptsIds Attempts IDs.
176+
* @return Cache key.
177+
*/
178+
protected getAttemptResultsCacheKey(id: number, attemptsIds: number[]): string {
179+
return this.getAttemptResultsCommonCacheKey(id) + ':' + JSON.stringify(attemptsIds);
180+
}
181+
182+
/**
183+
* Get common cache key for results WS calls.
184+
*
185+
* @param id Instance ID.
186+
* @return Cache key.
187+
*/
188+
protected getAttemptResultsCommonCacheKey(id: number): string {
189+
return this.ROOT_CACHE_KEY + 'results:' + id;
190+
}
191+
192+
/**
193+
* Get attempt results.
194+
*
195+
* @param id Activity ID.
196+
* @param attemptId Attempt ID.
197+
* @param options Other options.
198+
* @return Promise resolved with the results of the attempt.
199+
*/
200+
async getAttemptResults(id: number, attemptId: number, options?: AddonModH5PActivityGetAttemptResultsOptions)
201+
: Promise<AddonModH5PActivityAttemptResults> {
202+
203+
options = options || {};
204+
205+
const site = await CoreSites.instance.getSite(options.siteId);
206+
207+
const params = {
208+
h5pactivityid: id,
209+
attemptids: [attemptId],
210+
};
211+
const preSets: CoreSiteWSPreSets = {
212+
cacheKey: this.getAttemptResultsCacheKey(id, params.attemptids),
213+
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
214+
};
215+
216+
if (options.forceCache) {
217+
preSets.omitExpires = true;
218+
} else if (options.ignoreCache) {
219+
preSets.getFromCache = false;
220+
preSets.emergencyCache = false;
221+
}
222+
223+
try {
224+
const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets);
225+
226+
return this.formatAttemptResults(response.attempts[0]);
227+
} catch (error) {
228+
if (CoreUtils.instance.isWebServiceError(error)) {
229+
throw error;
230+
}
231+
232+
// Check if the full list of results is cached. If so, get the results from there.
233+
options.forceCache = true;
234+
235+
const attemptsResults = await AddonModH5PActivity.instance.getAllAttemptsResults(id, options);
236+
237+
const attempt = attemptsResults.attempts.find((attempt) => {
238+
return attempt.id == attemptId;
239+
});
240+
241+
if (!attempt) {
242+
throw error;
243+
}
244+
245+
return attempt;
246+
}
247+
}
248+
249+
/**
250+
* Get attempts results.
251+
*
252+
* @param id Activity ID.
253+
* @param attemptsIds Attempts IDs.
254+
* @param options Other options.
255+
* @return Promise resolved with all the attempts.
256+
*/
257+
async getAttemptsResults(id: number, attemptsIds: number[], options?: AddonModH5PActivityGetAttemptResultsOptions)
258+
: Promise<AddonModH5PActivityAttemptsResults> {
259+
260+
options = options || {};
261+
262+
const site = await CoreSites.instance.getSite(options.siteId);
263+
264+
const params = {
265+
h5pactivityid: id,
266+
attemptids: attemptsIds,
267+
};
268+
const preSets: CoreSiteWSPreSets = {
269+
cacheKey: this.getAttemptResultsCommonCacheKey(id),
270+
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
271+
};
272+
273+
if (options.forceCache) {
274+
preSets.omitExpires = true;
275+
} else if (options.ignoreCache) {
276+
preSets.getFromCache = false;
277+
preSets.emergencyCache = false;
278+
}
279+
280+
const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets);
281+
282+
response.attempts = response.attempts.map((attempt) => {
283+
return this.formatAttemptResults(attempt);
284+
});
285+
286+
return response;
287+
}
288+
140289
/**
141290
* Get deployed file from an H5P activity instance.
142291
*
@@ -244,61 +393,6 @@ export class AddonModH5PActivityProvider {
244393
return this.getH5PActivityByField(courseId, 'id', id, forceCache, siteId);
245394
}
246395

247-
/**
248-
* Get cache key for results WS calls.
249-
*
250-
* @param id Instance ID.
251-
* @param attemptsIds Attempts IDs.
252-
* @return Cache key.
253-
*/
254-
protected getResultsCacheKey(id: number, attemptsIds: number[]): string {
255-
return this.getResultsCommonCacheKey(id) + ':' + JSON.stringify(attemptsIds);
256-
}
257-
258-
/**
259-
* Get common cache key for results WS calls.
260-
*
261-
* @param id Instance ID.
262-
* @return Cache key.
263-
*/
264-
protected getResultsCommonCacheKey(id: number): string {
265-
return this.ROOT_CACHE_KEY + 'results:' + id;
266-
}
267-
268-
/**
269-
* Get attempt results.
270-
*
271-
* @param id Activity ID.
272-
* @param attemptId Attempt ID.
273-
* @param options Other options.
274-
* @return Promise resolved with the attempts of the user.
275-
*/
276-
async getResults(id: number, attemptId: number, options?: AddonModH5PActivityGetResultsOptions)
277-
: Promise<AddonModH5PActivityAttemptResults> {
278-
279-
options = options || {};
280-
281-
const site = await CoreSites.instance.getSite(options.siteId);
282-
283-
const params = {
284-
h5pactivityid: id,
285-
attemptids: [attemptId],
286-
};
287-
const preSets: CoreSiteWSPreSets = {
288-
cacheKey: this.getResultsCacheKey(id, params.attemptids),
289-
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
290-
};
291-
292-
if (options.ignoreCache) {
293-
preSets.getFromCache = false;
294-
preSets.emergencyCache = false;
295-
}
296-
297-
const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets);
298-
299-
return this.formatAttemptResults(response.attempts[0]);
300-
}
301-
302396
/**
303397
* Get cache key for attemps WS calls.
304398
*
@@ -342,7 +436,9 @@ export class AddonModH5PActivityProvider {
342436
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
343437
};
344438

345-
if (options.ignoreCache) {
439+
if (options.forceCache) {
440+
preSets.omitExpires = true;
441+
} else if (options.ignoreCache) {
346442
preSets.getFromCache = false;
347443
preSets.emergencyCache = false;
348444
}
@@ -389,7 +485,7 @@ export class AddonModH5PActivityProvider {
389485
async invalidateAllResults(id: number, siteId?: string): Promise<void> {
390486
const site = await CoreSites.instance.getSite(siteId);
391487

392-
await site.invalidateWsCacheForKey(this.getResultsCommonCacheKey(id));
488+
await site.invalidateWsCacheForKey(this.getAttemptResultsCommonCacheKey(id));
393489
}
394490

395491
/**
@@ -403,7 +499,7 @@ export class AddonModH5PActivityProvider {
403499
async invalidateAttemptResults(id: number, attemptId: number, siteId?: string): Promise<void> {
404500
const site = await CoreSites.instance.getSite(siteId);
405501

406-
await site.invalidateWsCacheForKey(this.getResultsCacheKey(id, [attemptId]));
502+
await site.invalidateWsCacheForKey(this.getAttemptResultsCacheKey(id, [attemptId]));
407503
}
408504

409505
/**
@@ -633,6 +729,15 @@ export type AddonModH5PActivityUserAttempts = {
633729
};
634730
};
635731

732+
/**
733+
* Attempts results with some calculated data.
734+
*/
735+
export type AddonModH5PActivityAttemptsResults = {
736+
activityid: number; // Activity course module ID.
737+
attempts: AddonModH5PActivityAttemptResults[]; // The complete attempts list.
738+
warnings?: CoreWSExternalWarning[];
739+
};
740+
636741
/**
637742
* Attempt with some calculated data.
638743
*/
@@ -658,18 +763,16 @@ export type AddonModH5PActivityGetDeployedFileOptions = {
658763
};
659764

660765
/**
661-
* Options to pass to getResults function.
766+
* Options to pass to getAttemptResults function.
662767
*/
663-
export type AddonModH5PActivityGetResultsOptions = {
768+
export type AddonModH5PActivityGetAttemptResultsOptions = {
769+
forceCache?: boolean; // Whether to force cache. If not cached, it will call the WS.
664770
ignoreCache?: boolean; // Whether to ignore cache. Will fail if offline or server down.
665771
siteId?: string; // Site ID. If not defined, current site.
772+
userId?: number; // User ID. If not defined, user of the site.
666773
};
667774

668775
/**
669776
* Options to pass to getAttempts function.
670777
*/
671-
export type AddonModH5PActivityGetAttemptsOptions = {
672-
ignoreCache?: boolean; // Whether to ignore cache. Will fail if offline or server down.
673-
siteId?: string; // Site ID. If not defined, current site.
674-
userId?: number; // User ID. If not defined, user of the site.
675-
};
778+
export type AddonModH5PActivityGetAttemptsOptions = AddonModH5PActivityGetAttemptResultsOptions;

0 commit comments

Comments
 (0)