MOBILE-3659 course: Implement log helper
parent
8d752d2bf5
commit
2906210242
|
@ -17,15 +17,15 @@ import { NgModule } from '@angular/core';
|
||||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||||
|
|
||||||
import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/database/course';
|
import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/database/course';
|
||||||
|
import { SITE_SCHEMA as LOG_SITE_SCHEMA } from './services/database/log';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: CORE_SITE_SCHEMAS,
|
provide: CORE_SITE_SCHEMAS,
|
||||||
useValue: [SITE_SCHEMA, OFFLINE_SITE_SCHEMA],
|
useValue: [SITE_SCHEMA, OFFLINE_SITE_SCHEMA, LOG_SITE_SCHEMA],
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CoreCourseModule {
|
export class CoreCourseModule {}
|
||||||
}
|
|
||||||
|
|
|
@ -47,8 +47,9 @@ export interface CoreCourseOptionsHandler extends CoreDelegateHandler {
|
||||||
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||||
* @return True or promise resolved with true if enabled.
|
* @return True or promise resolved with true if enabled.
|
||||||
*/
|
*/
|
||||||
isEnabledForCourse(courseId: number,
|
isEnabledForCourse(
|
||||||
accessData: CoreCourseAccessData,
|
courseId: number,
|
||||||
|
accessData: CoreCourseAccess,
|
||||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
): boolean | Promise<boolean>;
|
): boolean | Promise<boolean>;
|
||||||
|
@ -56,7 +57,7 @@ export interface CoreCourseOptionsHandler extends CoreDelegateHandler {
|
||||||
/**
|
/**
|
||||||
* Returns the data needed to render the handler.
|
* Returns the data needed to render the handler.
|
||||||
*
|
*
|
||||||
* @param course The course. // @todo: define type in the whole file.
|
* @param course The course.
|
||||||
* @return Data or promise resolved with the data.
|
* @return Data or promise resolved with the data.
|
||||||
*/
|
*/
|
||||||
getDisplayData?(
|
getDisplayData?(
|
||||||
|
@ -226,7 +227,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
||||||
|
|
||||||
protected coursesHandlers: {
|
protected coursesHandlers: {
|
||||||
[courseId: number]: {
|
[courseId: number]: {
|
||||||
access: any;
|
access: CoreCourseAccess;
|
||||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
||||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
||||||
deferred: PromiseDefer<void>;
|
deferred: PromiseDefer<void>;
|
||||||
|
@ -320,7 +321,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
||||||
protected async getHandlersForAccess(
|
protected async getHandlersForAccess(
|
||||||
courseId: number,
|
courseId: number,
|
||||||
refresh: boolean,
|
refresh: boolean,
|
||||||
accessData: any,
|
accessData: CoreCourseAccess,
|
||||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
): Promise<CoreCourseOptionsHandler[]> {
|
): Promise<CoreCourseOptionsHandler[]> {
|
||||||
|
@ -618,7 +619,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
||||||
*/
|
*/
|
||||||
async updateHandlersForCourse(
|
async updateHandlersForCourse(
|
||||||
courseId: number,
|
courseId: number,
|
||||||
accessData: any,
|
accessData: CoreCourseAccess,
|
||||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
@ -673,5 +674,6 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
||||||
|
|
||||||
export class CoreCourseOptionsDelegate extends makeSingleton(CoreCourseOptionsDelegateService) {}
|
export class CoreCourseOptionsDelegate extends makeSingleton(CoreCourseOptionsDelegateService) {}
|
||||||
|
|
||||||
// @todo define
|
export type CoreCourseAccess = {
|
||||||
export type CoreCourseAccessData = any;
|
type: string; // Either CoreCourseProvider.ACCESS_GUEST or CoreCourseProvider.ACCESS_DEFAULT.
|
||||||
|
};
|
||||||
|
|
|
@ -856,7 +856,6 @@ export class CoreCourseProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @param name Name of the course.
|
* @param name Name of the course.
|
||||||
* @return Promise resolved when the WS call is successful.
|
* @return Promise resolved when the WS call is successful.
|
||||||
* @todo use logHelper. Remove eslint disable when done.
|
|
||||||
*/
|
*/
|
||||||
async logView(courseId: number, sectionNumber?: number, siteId?: string, name?: string): Promise<void> {
|
async logView(courseId: number, sectionNumber?: number, siteId?: string, name?: string): Promise<void> {
|
||||||
const params: CoreCourseViewCourseWSParams = {
|
const params: CoreCourseViewCourseWSParams = {
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreSiteSchema } from '@services/sites';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database variables for CoreCourse service.
|
||||||
|
*/
|
||||||
|
export const ACTIVITY_LOG_TABLE = 'course_activity_log';
|
||||||
|
export const SITE_SCHEMA: CoreSiteSchema = {
|
||||||
|
name: 'CoreCourseLogHelperProvider',
|
||||||
|
version: 1,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
name: ACTIVITY_LOG_TABLE,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'component',
|
||||||
|
type: 'TEXT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'componentid',
|
||||||
|
type: 'INTEGER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ws',
|
||||||
|
type: 'TEXT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'data',
|
||||||
|
type: 'TEXT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'time',
|
||||||
|
type: 'INTEGER',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
primaryKeys: ['component', 'componentid', 'ws', 'time'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CoreCourseActivityLogDBRecord = {
|
||||||
|
component: string;
|
||||||
|
componentid: number;
|
||||||
|
ws: string;
|
||||||
|
time: number;
|
||||||
|
data?: string;
|
||||||
|
};
|
|
@ -0,0 +1,365 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreApp } from '@services/app';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { ACTIVITY_LOG_TABLE, CoreCourseActivityLogDBRecord } from './database/log';
|
||||||
|
import { CoreStatusWithWarningsWSResponse } from '@services/ws';
|
||||||
|
import { CoreWSError } from '@classes/errors/wserror';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to manage logging to Moodle.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class CoreCourseLogHelperProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the offline saved activity logs.
|
||||||
|
*
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when deleted, rejected if failure.
|
||||||
|
*/
|
||||||
|
protected async deleteLogs(component: string, componentId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
const conditions: Partial<CoreCourseActivityLogDBRecord> = {
|
||||||
|
component,
|
||||||
|
componentid: componentId,
|
||||||
|
};
|
||||||
|
|
||||||
|
await site.getDb().deleteRecords(ACTIVITY_LOG_TABLE, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a WS based log.
|
||||||
|
*
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param ws WS name.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when deleted, rejected if failure.
|
||||||
|
*/
|
||||||
|
protected async deleteWSLogsByComponent(component: string, componentId: number, ws: string, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
const conditions: Partial<CoreCourseActivityLogDBRecord> = {
|
||||||
|
component,
|
||||||
|
componentid: componentId,
|
||||||
|
ws,
|
||||||
|
};
|
||||||
|
|
||||||
|
await site.getDb().deleteRecords(ACTIVITY_LOG_TABLE, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the offline saved activity logs using call data.
|
||||||
|
*
|
||||||
|
* @param ws WS name.
|
||||||
|
* @param data Data to send to the WS.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when deleted, rejected if failure.
|
||||||
|
*/
|
||||||
|
protected async deleteWSLogs(ws: string, data: Record<string, unknown>, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
const conditions: Partial<CoreCourseActivityLogDBRecord> = {
|
||||||
|
ws,
|
||||||
|
data: CoreUtils.instance.sortAndStringify(data),
|
||||||
|
};
|
||||||
|
|
||||||
|
await site.getDb().deleteRecords(ACTIVITY_LOG_TABLE, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the offline saved activity logs.
|
||||||
|
*
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with the list of offline logs.
|
||||||
|
*/
|
||||||
|
protected async getAllLogs(siteId?: string): Promise<CoreCourseActivityLogDBRecord[]> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
return site.getDb().getAllRecords<CoreCourseActivityLogDBRecord>(ACTIVITY_LOG_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the offline saved activity logs.
|
||||||
|
*
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with the list of offline logs.
|
||||||
|
*/
|
||||||
|
protected async getLogs(component: string, componentId: number, siteId?: string): Promise<CoreCourseActivityLogDBRecord[]> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
const conditions: Partial<CoreCourseActivityLogDBRecord> = {
|
||||||
|
component,
|
||||||
|
componentid: componentId,
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.getDb().getRecords<CoreCourseActivityLogDBRecord>(ACTIVITY_LOG_TABLE, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform log online. Data will be saved offline for syncing.
|
||||||
|
*
|
||||||
|
* @param ws WS name.
|
||||||
|
* @param data Data to send to the WS.
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
async log(ws: string, data: Record<string, unknown>, component: string, componentId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
if (!CoreApp.instance.isOnline()) {
|
||||||
|
// App is offline, store the action.
|
||||||
|
return this.storeOffline(ws, data, component, componentId, site.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.logOnline(ws, data, site.getId());
|
||||||
|
} catch (error) {
|
||||||
|
if (CoreUtils.instance.isWebServiceError(error)) {
|
||||||
|
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Couldn't connect to server, store in offline.
|
||||||
|
return this.storeOffline(ws, data, component, componentId, site.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the log online.
|
||||||
|
*
|
||||||
|
* @param ws WS name.
|
||||||
|
* @param data Data to send to the WS.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when log is successfully submitted. Rejected with object containing
|
||||||
|
* the error message (if any) and a boolean indicating if the error was returned by WS.
|
||||||
|
*/
|
||||||
|
protected async logOnline<T extends CoreStatusWithWarningsWSResponse>(
|
||||||
|
ws: string,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
// Clone to have an unmodified data object.
|
||||||
|
const wsData = Object.assign({}, data);
|
||||||
|
|
||||||
|
const response = await site.write<T>(ws, wsData);
|
||||||
|
|
||||||
|
if (!response.status) {
|
||||||
|
// Return the warning. If no warnings (shouldn't happen), create a fake one.
|
||||||
|
const warning = response.warnings?.[0] || {
|
||||||
|
warningcode: 'errorlog',
|
||||||
|
message: 'Error logging data.',
|
||||||
|
};
|
||||||
|
|
||||||
|
throw new CoreWSError(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all the logs performed.
|
||||||
|
// TODO: Remove this lines when time is accepted in logs.
|
||||||
|
await this.deleteWSLogs(ws, data, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform log online. Data will be saved offline for syncing.
|
||||||
|
* It also triggers a Firebase view_item event.
|
||||||
|
*
|
||||||
|
* @param ws WS name.
|
||||||
|
* @param data Data to send to the WS.
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param name Name of the viewed item.
|
||||||
|
* @param category Category of the viewed item.
|
||||||
|
* @param eventData Data to pass to the Firebase event.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
logSingle(
|
||||||
|
ws: string,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
component: string,
|
||||||
|
componentId: number,
|
||||||
|
name?: string,
|
||||||
|
category?: string,
|
||||||
|
eventData?: Record<string, unknown>,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
CorePushNotifications.instance.logViewEvent(componentId, name, category, ws, eventData, siteId);
|
||||||
|
|
||||||
|
return this.log(ws, data, component, componentId, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform log online. Data will be saved offline for syncing.
|
||||||
|
* It also triggers a Firebase view_item_list event.
|
||||||
|
*
|
||||||
|
* @param ws WS name.
|
||||||
|
* @param data Data to send to the WS.
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param category Category of the viewed item.
|
||||||
|
* @param eventData Data to pass to the Firebase event.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
logList(
|
||||||
|
ws: string,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
component: string,
|
||||||
|
componentId: number,
|
||||||
|
category: string,
|
||||||
|
eventData?: Record<string, unknown>,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
CorePushNotifications.instance.logViewListEvent(category, ws, eventData, siteId);
|
||||||
|
|
||||||
|
return this.log(ws, data, component, componentId, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save activity log for offline sync.
|
||||||
|
*
|
||||||
|
* @param ws WS name.
|
||||||
|
* @param data Data to send to the WS.
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
protected async storeOffline(
|
||||||
|
ws: string,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
component: string,
|
||||||
|
componentId: number,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
const log: CoreCourseActivityLogDBRecord = {
|
||||||
|
component,
|
||||||
|
componentid: componentId,
|
||||||
|
ws,
|
||||||
|
data: CoreUtils.instance.sortAndStringify(data),
|
||||||
|
time: CoreTimeUtils.instance.timestamp(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await site.getDb().insertRecord(ACTIVITY_LOG_TABLE, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync all the offline saved activity logs.
|
||||||
|
*
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
async syncSite(siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
siteId = site.getId();
|
||||||
|
|
||||||
|
const logs = await this.getAllLogs(siteId);
|
||||||
|
|
||||||
|
const unique: CoreCourseActivityLogDBRecord[] = [];
|
||||||
|
|
||||||
|
// TODO: When time is accepted on log, do not discard same logs.
|
||||||
|
logs.forEach((log) => {
|
||||||
|
// Just perform unique syncs.
|
||||||
|
const found = unique.find((doneLog) => log.component == doneLog.component && log.componentid == doneLog.componentid &&
|
||||||
|
log.ws == doneLog.ws && log.data == doneLog.data);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
unique.push(log);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.syncLogs(unique, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync the offline saved activity logs.
|
||||||
|
*
|
||||||
|
* @param component Component name.
|
||||||
|
* @param componentId Component ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
async syncActivity(component: string, componentId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
siteId = site.getId();
|
||||||
|
|
||||||
|
const logs = await this.getLogs(component, componentId, siteId);
|
||||||
|
|
||||||
|
const unique: CoreCourseActivityLogDBRecord[] = [];
|
||||||
|
|
||||||
|
// TODO: When time is accepted on log, do not discard same logs.
|
||||||
|
logs.forEach((log) => {
|
||||||
|
// Just perform unique syncs.
|
||||||
|
const found = unique.find((doneLog) => log.ws == doneLog.ws && log.data == doneLog.data);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
unique.push(log);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.syncLogs(unique, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync and delete given logs.
|
||||||
|
*
|
||||||
|
* @param logs Array of log objects.
|
||||||
|
* @param siteId Site Id.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async syncLogs(logs: CoreCourseActivityLogDBRecord[], siteId: string): Promise<void> {
|
||||||
|
await Promise.all(logs.map(async (log) => {
|
||||||
|
const data = CoreTextUtils.instance.parseJSON<Record<string, unknown>>(log.data || '{}', {});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.logOnline(log.ws, data, siteId);
|
||||||
|
} catch (error) {
|
||||||
|
if (CoreUtils.instance.isWebServiceError(error)) {
|
||||||
|
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||||
|
await CoreUtils.instance.ignoreErrors(this.deleteWSLogs(log.ws, data, siteId));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.deleteWSLogsByComponent(log.component, log.componentid, log.ws, siteId);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CoreCourseLogHelper extends makeSingleton(CoreCourseLogHelperProvider) {}
|
Loading…
Reference in New Issue