295 lines
9.8 KiB
TypeScript

// (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 { CoreSites } from '@services/sites';
import {
AddonCalendarEvents,
AddonCalendarEventsGroupedByCourse,
AddonCalendarEvent,
AddonCalendarGetActionEventsByCourseWSParams,
AddonCalendarGetActionEventsByTimesortWSParams,
AddonCalendarGetActionEventsByCoursesWSParams,
} from '@addons/calendar/services/calendar';
import moment from 'moment-timezone';
import { makeSingleton } from '@singletons';
import { CoreSiteWSPreSets } from '@classes/site';
// Cache key was maintained from block myoverview when blocks were splitted.
const ROOT_CACHE_KEY = 'myoverview:';
/**
* Service that provides some features regarding course overview.
*/
@Injectable({ providedIn: 'root' })
export class AddonBlockTimelineProvider {
static readonly EVENTS_LIMIT = 20;
static readonly EVENTS_LIMIT_PER_COURSE = 10;
/**
* Get calendar action events for the given course.
*
* @param courseId Only events in this course.
* @param afterEventId The last seen event id.
* @param searchValue The value a user wishes to search against.
* @param siteId Site ID. If not defined, use current site.
* @return Promise resolved when the info is retrieved.
*/
async getActionEventsByCourse(
courseId: number,
afterEventId?: number,
searchValue = '',
siteId?: string,
): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> {
const site = await CoreSites.getSite(siteId);
const time = this.getDayStart(-14); // Check two weeks ago.
const data: AddonCalendarGetActionEventsByCourseWSParams = {
timesortfrom: time,
courseid: courseId,
limitnum: AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE,
};
if (afterEventId) {
data.aftereventid = afterEventId;
}
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getActionEventsByCourseCacheKey(courseId),
};
if (searchValue != '') {
data.searchvalue = searchValue;
preSets.getFromCache = false;
}
const courseEvents = await site.read<AddonCalendarEvents>(
'core_calendar_get_action_events_by_course',
data,
preSets,
);
return this.treatCourseEvents(courseEvents, time);
}
/**
* Get cache key for get calendar action events for the given course value WS call.
*
* @param courseId Only events in this course.
* @return Cache key.
*/
protected getActionEventsByCourseCacheKey(courseId: number): string {
return this.getActionEventsByCoursesCacheKey() + ':' + courseId;
}
/**
* Get calendar action events for a given list of courses.
*
* @param courseIds Course IDs.
* @param siteId Site ID. If not defined, use current site.
* @param searchValue The value a user wishes to search against.
* @return Promise resolved when the info is retrieved.
*/
async getActionEventsByCourses(
courseIds: number[],
searchValue = '',
siteId?: string,
): Promise<{[courseId: string]: { events: AddonCalendarEvent[]; canLoadMore?: number } }> {
if (courseIds.length === 0) {
return {};
}
const site = await CoreSites.getSite(siteId);
const time = this.getDayStart(-14); // Check two weeks ago.
const data: AddonCalendarGetActionEventsByCoursesWSParams = {
timesortfrom: time,
courseids: courseIds,
limitnum: AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE,
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getActionEventsByCoursesCacheKey(),
};
if (searchValue != '') {
data.searchvalue = searchValue;
preSets.getFromCache = false;
}
const events = await site.read<AddonCalendarEventsGroupedByCourse>(
'core_calendar_get_action_events_by_courses',
data,
preSets,
);
const courseEvents: {[courseId: string]: { events: AddonCalendarEvent[]; canLoadMore?: number } } = {};
events.groupedbycourse.forEach((course) => {
courseEvents[course.courseid] = this.treatCourseEvents(course, time);
});
return courseEvents;
}
/**
* Get cache key for get calendar action events for a given list of courses value WS call.
*
* @return Cache key.
*/
protected getActionEventsByCoursesCacheKey(): string {
return ROOT_CACHE_KEY + 'bycourse';
}
/**
* Get calendar action events based on the timesort value.
*
* @param afterEventId The last seen event id.
* @param searchValue The value a user wishes to search against.
* @param siteId Site ID. If not defined, use current site.
* @return Promise resolved when the info is retrieved.
*/
async getActionEventsByTimesort(
afterEventId?: number,
searchValue = '',
siteId?: string,
): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> {
const site = await CoreSites.getSite(siteId);
const timesortfrom = this.getDayStart(-14); // Check two weeks ago.
const limitnum = AddonBlockTimelineProvider.EVENTS_LIMIT;
const data: AddonCalendarGetActionEventsByTimesortWSParams = {
timesortfrom,
limitnum,
};
if (afterEventId) {
data.aftereventid = afterEventId;
}
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getActionEventsByTimesortCacheKey(afterEventId, limitnum),
getCacheUsingCacheKey: true,
uniqueCacheKey: true,
};
if (searchValue != '') {
data.searchvalue = searchValue;
preSets.getFromCache = false;
preSets.cacheKey += ':' + searchValue;
}
const result = await site.read<AddonCalendarEvents>(
'core_calendar_get_action_events_by_timesort',
data,
preSets,
);
const canLoadMore = result.events.length >= limitnum ? result.lastid : undefined;
// Filter events by time in case it uses cache.
const events = result.events.filter((element) => element.timesort >= timesortfrom);
return {
events,
canLoadMore,
};
}
/**
* Get prefix cache key for calendar action events based on the timesort value WS calls.
*
* @return Cache key.
*/
protected getActionEventsByTimesortPrefixCacheKey(): string {
return ROOT_CACHE_KEY + 'bytimesort:';
}
/**
* Get cache key for get calendar action events based on the timesort value WS call.
*
* @param afterEventId The last seen event id.
* @param limit Limit num of the call.
* @return Cache key.
*/
protected getActionEventsByTimesortCacheKey(afterEventId?: number, limit?: number): string {
afterEventId = afterEventId || 0;
limit = limit || 0;
return this.getActionEventsByTimesortPrefixCacheKey() + afterEventId + ':' + limit;
}
/**
* Invalidates get calendar action events for a given list of courses WS call.
*
* @param siteId Site ID to invalidate. If not defined, use current site.
* @return Promise resolved when the data is invalidated.
*/
async invalidateActionEventsByCourses(siteId?: string): Promise<void> {
const site = await CoreSites.getSite(siteId);
await site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByCoursesCacheKey());
}
/**
* Invalidates get calendar action events based on the timesort value WS call.
*
* @param siteId Site ID to invalidate. If not defined, use current site.
* @return Promise resolved when the data is invalidated.
*/
async invalidateActionEventsByTimesort(siteId?: string): Promise<void> {
const site = await CoreSites.getSite(siteId);
await site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByTimesortPrefixCacheKey());
}
/**
* Handles course events, filtering and treating if more can be loaded.
*
* @param course Object containing response course events info.
* @param timeFrom Current time to filter events from.
* @return Object with course events and last loaded event id if more can be loaded.
*/
protected treatCourseEvents(
course: AddonCalendarEvents,
timeFrom: number,
): { events: AddonCalendarEvent[]; canLoadMore?: number } {
const canLoadMore: number | undefined =
course.events.length >= AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE ? course.lastid : undefined;
// Filter events by time in case it uses cache.
course.events = course.events.filter((element) => element.timesort >= timeFrom);
return {
events: course.events,
canLoadMore,
};
}
/**
* Returns the timestamp at the start of the day with an optional offset.
*
* @param daysOffset Offset days to add or substract.
* @return timestamp.
*/
getDayStart(daysOffset = 0): number {
return moment().startOf('day').add(daysOffset, 'days').unix();
}
}
export const AddonBlockTimeline = makeSingleton(AddonBlockTimelineProvider);