MOBILE-2850 quiz: Prefetch data after syncing quiz
parent
0cec1cc01e
commit
c6b51873f1
|
@ -50,6 +50,21 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the module.
|
||||
*
|
||||
* @param {any} module The module object returned by WS.
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [dirPath] Path of the directory where to store all the content files.
|
||||
* @param {boolean} [single] True if we're downloading a single module, false if we're downloading a whole section.
|
||||
* @param {boolean} [canStart=true] If true, start a new attempt if needed.
|
||||
* @return {Promise<any>} Promise resolved when all content is downloaded.
|
||||
*/
|
||||
download(module: any, courseId: number, dirPath?: string, single?: boolean, canStart: boolean = true): Promise<any> {
|
||||
// Same implementation for download and prefetch.
|
||||
return this.prefetch(module, courseId, single, dirPath, canStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of files. If not defined, we'll assume they're in module.contents.
|
||||
*
|
||||
|
@ -190,7 +205,7 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
|
||||
const siteId = this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
return this.quizProvider.getQuiz(courseId, module.id, false, siteId).then((quiz) => {
|
||||
return this.quizProvider.getQuiz(courseId, module.id, false, false, siteId).then((quiz) => {
|
||||
if (quiz.allowofflineattempts !== 1 || quiz.hasquestions === 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -220,10 +235,11 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
* @param {number} courseId Course ID the module belongs to.
|
||||
* @param {boolean} [single] True if we're downloading a single module, false if we're downloading a whole section.
|
||||
* @param {string} [dirPath] Path of the directory where to store all the content files.
|
||||
* @param {boolean} [canStart=true] If true, start a new attempt if needed.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise<any> {
|
||||
return this.prefetchPackage(module, courseId, single, this.prefetchQuiz.bind(this));
|
||||
prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string, canStart: boolean = true): Promise<any> {
|
||||
return this.prefetchPackage(module, courseId, single, this.prefetchQuiz.bind(this), undefined, canStart);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -233,9 +249,10 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
* @param {number} courseId Course ID the module belongs to.
|
||||
* @param {boolean} single True if we're downloading a single module, false if we're downloading a whole section.
|
||||
* @param {String} siteId Site ID.
|
||||
* @param {boolean} canStart If true, start a new attempt if needed.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected prefetchQuiz(module: any, courseId: number, single: boolean, siteId: string): Promise<any> {
|
||||
protected prefetchQuiz(module: any, courseId: number, single: boolean, siteId: string, canStart: boolean): Promise<any> {
|
||||
let attempts: any[],
|
||||
startAttempt = false,
|
||||
quiz,
|
||||
|
@ -244,7 +261,7 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
preflightData;
|
||||
|
||||
// Get quiz.
|
||||
return this.quizProvider.getQuiz(courseId, module.id, false, siteId).then((quizData) => {
|
||||
return this.quizProvider.getQuiz(courseId, module.id, false, true, siteId).then((quizData) => {
|
||||
quiz = quizData;
|
||||
|
||||
const promises = [],
|
||||
|
@ -272,7 +289,13 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
}).then(() => {
|
||||
// Check if we need to start a new attempt.
|
||||
let attempt = attempts[attempts.length - 1];
|
||||
if (!attempt || this.quizProvider.isAttemptFinished(attempt.state)) {
|
||||
|
||||
if (!canStart && !attempt) {
|
||||
// No attempts and we won't start a new one, so we don't need preflight data.
|
||||
return;
|
||||
}
|
||||
|
||||
if (canStart && (!attempt || this.quizProvider.isAttemptFinished(attempt.state))) {
|
||||
// Check if the user can attempt the quiz.
|
||||
if (attemptAccessInfo.preventnewattemptreasons.length) {
|
||||
return Promise.reject(this.textUtils.buildMessage(attemptAccessInfo.preventnewattemptreasons));
|
||||
|
@ -331,6 +354,11 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
|
||||
return Promise.all(promises);
|
||||
}).then(() => {
|
||||
if (!canStart) {
|
||||
// Nothing else to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's nothing to send, mark the quiz as synchronized.
|
||||
// We don't return the promises because it should be fast and we don't want to block the user for this.
|
||||
if (!this.syncProvider) {
|
||||
|
@ -477,14 +505,49 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
|||
return this.prefetchAttempt(quiz, lastAttempt, preflightData, siteId);
|
||||
}
|
||||
}).then(() => {
|
||||
// Prefetch finished, get current status to determine if we need to change it.
|
||||
// Prefetch finished, set the right status.
|
||||
return this.setStatusAfterPrefetch(quiz, attempts, true, false, siteId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the right status to a quiz after prefetching.
|
||||
* If the last attempt is finished or there isn't one, set it as not downloaded to show download icon.
|
||||
*
|
||||
* @param {any} quiz Quiz.
|
||||
* @param {any[]} [attempts] List of attempts. If not provided, they will be calculated.
|
||||
* @param {boolean} [forceCache] Whether it should always return cached data. Only if attempts is undefined.
|
||||
* @param {boolean} [ignoreCache] Whether it should ignore cached data (it will always fail in offline or server down). Only if
|
||||
* attempts is undefined.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
setStatusAfterPrefetch(quiz: any, attempts?: any[], forceCache?: boolean, ignoreCache?: boolean, siteId?: string)
|
||||
: Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const promises = [];
|
||||
let status;
|
||||
|
||||
if (!attempts) {
|
||||
// Get the attempts.
|
||||
promises.push(this.quizProvider.getUserAttempts(quiz.id, 'all', true, forceCache, ignoreCache, siteId).then((atts) => {
|
||||
attempts = atts;
|
||||
}));
|
||||
}
|
||||
|
||||
// Check the current status of the quiz.
|
||||
promises.push(this.filepoolProvider.getPackageStatus(siteId, this.component, quiz.coursemodule).then((stat) => {
|
||||
status = stat;
|
||||
}));
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
|
||||
return this.filepoolProvider.getPackageStatus(siteId, this.component, quiz.coursemodule);
|
||||
}).then((status) => {
|
||||
if (status !== CoreConstants.NOT_DOWNLOADED) {
|
||||
// Quiz was downloaded, set the new status.
|
||||
// If no attempts or last is finished we'll mark it as not downloaded to show download icon.
|
||||
const isLastFinished = !lastAttempt || this.quizProvider.isAttemptFinished(lastAttempt.state),
|
||||
const lastAttempt = attempts[attempts.length - 1],
|
||||
isLastFinished = !lastAttempt || this.quizProvider.isAttemptFinished(lastAttempt.state),
|
||||
newStatus = isLastFinished ? CoreConstants.NOT_DOWNLOADED : CoreConstants.DOWNLOADED;
|
||||
|
||||
return this.filepoolProvider.storePackageStatus(siteId, newStatus, this.component, quiz.coursemodule);
|
||||
|
|
|
@ -23,9 +23,10 @@ import { CoreTextUtilsProvider } from '@providers/utils/text';
|
|||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||
import { CoreQuestionProvider } from '@core/question/providers/question';
|
||||
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||
import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync';
|
||||
import { AddonModQuizProvider } from './quiz';
|
||||
import { AddonModQuizOfflineProvider } from './quiz-offline';
|
||||
import { AddonModQuizPrefetchHandler } from './prefetch-handler';
|
||||
|
@ -51,7 +52,7 @@ export interface AddonModQuizSyncResult {
|
|||
* Service to sync quizzes.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonModQuizSyncProvider extends CoreSyncBaseProvider {
|
||||
export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider {
|
||||
|
||||
static AUTO_SYNCED = 'addon_mod_quiz_autom_synced';
|
||||
|
||||
|
@ -59,13 +60,14 @@ export class AddonModQuizSyncProvider extends CoreSyncBaseProvider {
|
|||
|
||||
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider,
|
||||
syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService,
|
||||
courseProvider: CoreCourseProvider, private eventsProvider: CoreEventsProvider, timeUtils: CoreTimeUtilsProvider,
|
||||
private eventsProvider: CoreEventsProvider, timeUtils: CoreTimeUtilsProvider,
|
||||
private quizProvider: AddonModQuizProvider, private quizOfflineProvider: AddonModQuizOfflineProvider,
|
||||
private prefetchHandler: AddonModQuizPrefetchHandler, private questionProvider: CoreQuestionProvider,
|
||||
private questionDelegate: CoreQuestionDelegate, private logHelper: CoreCourseLogHelperProvider) {
|
||||
protected prefetchHandler: AddonModQuizPrefetchHandler, private questionProvider: CoreQuestionProvider,
|
||||
private questionDelegate: CoreQuestionDelegate, private logHelper: CoreCourseLogHelperProvider,
|
||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, private courseProvider: CoreCourseProvider) {
|
||||
|
||||
super('AddonModQuizSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate,
|
||||
timeUtils);
|
||||
timeUtils, prefetchDelegate, prefetchHandler);
|
||||
|
||||
this.componentTranslate = courseProvider.translateModuleName('quiz');
|
||||
}
|
||||
|
@ -97,7 +99,11 @@ export class AddonModQuizSyncProvider extends CoreSyncBaseProvider {
|
|||
}).then(() => {
|
||||
if (updated) {
|
||||
// Data has been sent. Update prefetched data.
|
||||
return this.prefetchHandler.prefetchQuizAndLastAttempt(quiz, false, siteId);
|
||||
return this.courseProvider.getModuleBasicInfoByInstance(quiz.id, 'quiz', siteId).then((module) => {
|
||||
return this.prefetchAfterUpdateQuiz(module, quiz, courseId, undefined, siteId);
|
||||
}).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}
|
||||
}).then(() => {
|
||||
return this.setSyncTime(quiz.id, siteId).catch(() => {
|
||||
|
@ -145,6 +151,41 @@ export class AddonModQuizSyncProvider extends CoreSyncBaseProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Conveniece function to prefetch data after an update.
|
||||
*
|
||||
* @param {any} module Module.
|
||||
* @param {any} quiz Quiz.
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {RegExp} [regex] If regex matches, don't download the data. Defaults to check files.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
prefetchAfterUpdateQuiz(module: any, quiz: any, courseId: number, regex?: RegExp, siteId?: string): Promise<any> {
|
||||
regex = regex || /^.*files$/;
|
||||
|
||||
let shouldDownload;
|
||||
|
||||
// Get the module updates to check if the data was updated or not.
|
||||
return this.prefetchDelegate.getModuleUpdates(module, courseId, true, siteId).then((result) => {
|
||||
|
||||
if (result && result.updates && result.updates.length > 0) {
|
||||
// Only prefetch if files haven't changed.
|
||||
shouldDownload = !result.updates.find((entry) => {
|
||||
return entry.name.match(regex);
|
||||
});
|
||||
|
||||
if (shouldDownload) {
|
||||
return this.prefetchHandler.download(module, courseId, undefined, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
}).then(() => {
|
||||
// Prefetch finished or not needed, set the right status.
|
||||
return this.prefetchHandler.setStatusAfterPrefetch(quiz, undefined, shouldDownload, false, siteId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to synchronize all the quizzes in a certain site or in all sites.
|
||||
*
|
||||
|
@ -185,7 +226,7 @@ export class AddonModQuizSyncProvider extends CoreSyncBaseProvider {
|
|||
if (!this.syncProvider.isBlocked(AddonModQuizProvider.COMPONENT, quiz.id, siteId)) {
|
||||
|
||||
// Quiz not blocked, try to synchronize it.
|
||||
promises.push(this.quizProvider.getQuizById(quiz.courseid, quiz.id, false, siteId).then((quiz) => {
|
||||
promises.push(this.quizProvider.getQuizById(quiz.courseid, quiz.id, false, false, siteId).then((quiz) => {
|
||||
return this.syncQuizIfNeeded(quiz, false, siteId).then((data) => {
|
||||
if (data && data.warnings && data.warnings.length) {
|
||||
// Store the warnings to show them when the user opens the quiz.
|
||||
|
|
|
@ -672,10 +672,13 @@ export class AddonModQuizProvider {
|
|||
* @param {string} key Name of the property to check.
|
||||
* @param {any} value Value to search.
|
||||
* @param {boolean} [forceCache] Whether it should always return cached data.
|
||||
* @param {boolean} [ignoreCache] Whether it should ignore cached data (it will always fail in offline or server down).
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the Quiz is retrieved.
|
||||
*/
|
||||
protected getQuizByField(courseId: number, key: string, value: any, forceCache?: boolean, siteId?: string): Promise<any> {
|
||||
protected getQuizByField(courseId: number, key: string, value: any, forceCache?: boolean, ignoreCache?: boolean,
|
||||
siteId?: string): Promise<any> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
courseids: [courseId]
|
||||
|
@ -686,6 +689,9 @@ export class AddonModQuizProvider {
|
|||
|
||||
if (forceCache) {
|
||||
preSets.omitExpires = true;
|
||||
} else if (ignoreCache) {
|
||||
preSets.getFromCache = false;
|
||||
preSets.emergencyCache = false;
|
||||
}
|
||||
|
||||
return site.read('mod_quiz_get_quizzes_by_courses', params, preSets).then((response) => {
|
||||
|
@ -710,11 +716,12 @@ export class AddonModQuizProvider {
|
|||
* @param {number} courseId Course ID.
|
||||
* @param {number} cmId Course module ID.
|
||||
* @param {boolean} [forceCache] Whether it should always return cached data.
|
||||
* @param {boolean} [ignoreCache] Whether it should ignore cached data (it will always fail in offline or server down).
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the quiz is retrieved.
|
||||
*/
|
||||
getQuiz(courseId: number, cmId: number, forceCache?: boolean, siteId?: string): Promise<any> {
|
||||
return this.getQuizByField(courseId, 'coursemodule', cmId, forceCache, siteId);
|
||||
getQuiz(courseId: number, cmId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
||||
return this.getQuizByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -723,11 +730,12 @@ export class AddonModQuizProvider {
|
|||
* @param {number} courseId Course ID.
|
||||
* @param {number} id Quiz ID.
|
||||
* @param {boolean} [forceCache] Whether it should always return cached data.
|
||||
* @param {boolean} [ignoreCache] Whether it should ignore cached data (it will always fail in offline or server down).
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the quiz is retrieved.
|
||||
*/
|
||||
getQuizById(courseId: number, id: number, forceCache?: boolean, siteId?: string): Promise<any> {
|
||||
return this.getQuizByField(courseId, 'id', id, forceCache, siteId);
|
||||
getQuizById(courseId: number, id: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
||||
return this.getQuizByField(courseId, 'id', id, forceCache, ignoreCache, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1223,7 +1231,7 @@ export class AddonModQuizProvider {
|
|||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Get required data to call the invalidate functions.
|
||||
return this.getQuiz(courseId, moduleId, false, siteId).then((quiz) => {
|
||||
return this.getQuiz(courseId, moduleId, true, false, siteId).then((quiz) => {
|
||||
return this.getUserAttempts(quiz.id, 'all', true, false, false, siteId).then((attempts) => {
|
||||
// Now invalidate it.
|
||||
const lastAttemptId = attempts.length ? attempts[attempts.length - 1].id : undefined;
|
||||
|
|
|
@ -38,7 +38,7 @@ export class CoreCourseActivitySyncBaseProvider extends CoreSyncBaseProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Conveniece function to refetch data after an update.
|
||||
* Conveniece function to prefetch data after an update.
|
||||
*
|
||||
* @param {any} module Module.
|
||||
* @param {number} courseId Course ID.
|
||||
|
|
Loading…
Reference in New Issue