Добавлен загрузчик данных для календаря в дашбордах

This commit is contained in:
Pavel Gnedov 2024-02-02 07:53:29 +07:00
parent d7b37470ef
commit 50f7aeaf2f
4 changed files with 84 additions and 7 deletions

View file

@ -42,16 +42,21 @@ export type IssueAndEvent = {
@Injectable()
export class CalendarService {
private interval = 30 * 24 * 60 * 60 * 1000; // 30 days
private defaultInterval = 30 * 24 * 60 * 60 * 1000; // 30 days
constructor(
public calendarEventsKey: string,
private issuesService: IssuesService,
) {}
async getICalData(filter: any): Promise<string> {
/**
* @param filter фильтр для первичной выборки данных из couchdb через nano.filter
* @param interval период в милисекундах
* @returns
*/
async getICalData(filter: any, interval?: number): Promise<string> {
const issues = await this.issuesService.find(filter);
const actualEvents = this.getActualEvents(issues);
const actualEvents = this.getActualEvents(issues, interval);
const formattedEvents = actualEvents
.map((event) => {
return this.generateICalendarEvent(event.issue, event.event);
@ -63,9 +68,21 @@ export class CalendarService {
return res;
}
/**
* @param filter фильтр для первичной выборки данных из couchdb через nano.filter
* @param interval период в милисекундах
* @returns
*/
async getRawData(filter: any, interval?: number): Promise<IssueAndEvent[]> {
const issues = await this.issuesService.find(filter);
return this.getActualEvents(issues, interval);
}
private getActualEvents(
issues: RedmineTypes.ExtendedIssue[],
interval?: number,
): IssueAndEvent[] {
if (typeof interval !== 'number') interval = this.defaultInterval;
const res: IssueAndEvent[] = [];
for (let i = 0; i < issues.length; i++) {
const issue = issues[i];
@ -78,16 +95,18 @@ export class CalendarService {
const events = issue[this.calendarEventsKey];
for (let j = 0; j < events.length; j++) {
const event = events[j];
if (this.actualEvent(event)) res.push({ event: event, issue: issue });
if (this.actualEvent(event, interval)) {
res.push({ event: event, issue: issue });
}
}
}
return res;
}
private actualEvent(event: CalendarEvent): boolean {
private actualEvent(event: CalendarEvent, interval: number): boolean {
const now = Luxon.DateTime.now().toMillis();
const from = now - this.interval;
const to = now + this.interval;
const from = now - interval;
const to = now + interval;
return Boolean(
(from <= event.fromTimestamp && event.fromTimestamp <= to) ||
(from <= event.toTimestamp && event.toTimestamp <= to),

View file

@ -0,0 +1,49 @@
import { Inject, Injectable } from '@nestjs/common';
import { WidgetDataLoaderInterface } from '../widget-data-loader-interface';
import {
Result,
AppError,
success,
fail,
createAppError,
} from '@app/event-emitter/utils/result';
import {
CalendarService,
IssueAndEvent,
} from '@app/event-emitter/calendar/calendar.service';
import nano from 'nano';
export type DataLoaderParams = {
/** Период для выборки предстоящих событий в днях */
period: number;
/** Фильтр для выборки данных из couchdb */
filter: nano.MangoQuery;
};
@Injectable()
export class CalendarWidgetDataLoaderService
implements WidgetDataLoaderInterface<DataLoaderParams, any, IssueAndEvent[]>
{
constructor(
@Inject('CALENDAR_SERVICE') private calendarService: CalendarService,
) {}
isMyConfig(): boolean {
return true;
}
async load(
dataLoaderParams: DataLoaderParams,
): Promise<Result<IssueAndEvent[], AppError>> {
let data: IssueAndEvent[];
try {
data = await this.calendarService.getRawData(
dataLoaderParams.filter,
dataLoaderParams.period * 24 * 60 * 60 * 1000,
);
return success(data);
} catch (ex) {
return fail(createAppError(ex.message ? ex.message : 'UNKNOWN_ERROR'));
}
}
}

View file

@ -5,6 +5,7 @@ import { ListIssuesByUsersLikeJiraWidgetDataLoaderService } from './widget-data-
import { RootIssueSubTreesWidgetDataLoaderService } from './widget-data-loader/root-issue-subtrees.widget-data-loader.service';
import { createInteractiveWidget } from './interactive-widget-factory';
import { Result, success } from '@app/event-emitter/utils/result';
import { CalendarWidgetDataLoaderService } from './widget-data-loader/calendar.widget-data-loader.service';
@Injectable()
export class WidgetsCollectionService {
@ -14,6 +15,7 @@ export class WidgetsCollectionService {
private listIssuesByFieldsWidgetDataLoaderService: ListIssuesByFieldsWidgetDataLoaderService,
private listIssuesByUsersLikeJiraWidgetDataLoaderService: ListIssuesByUsersLikeJiraWidgetDataLoaderService,
private rootIssueSubTreesWidgetDataLoaderService: RootIssueSubTreesWidgetDataLoaderService,
private calendarWidgetDataLoaderService: CalendarWidgetDataLoaderService,
) {
const collection = [
createInteractiveWidget(
@ -40,6 +42,10 @@ export class WidgetsCollectionService {
this.rootIssueSubTreesWidgetDataLoaderService,
'issues_list_by_tree',
),
createInteractiveWidget(
this.calendarWidgetDataLoaderService,
'calendar_next_events',
),
];
collection.forEach((w) => this.appendWidget(w));

View file

@ -36,6 +36,7 @@ import { ListIssuesByUsersLikeJiraWidgetDataLoaderService } from './dashboards/w
import { ListIssuesByFieldsWidgetDataLoaderService } from './dashboards/widget-data-loader/list-issues-by-fields.widget-data-loader.service';
import { WidgetsCollectionService } from './dashboards/widgets-collection.service';
import { DashboardsController } from './dashboards/dashboards.controller';
import { CalendarWidgetDataLoaderService } from './dashboards/widget-data-loader/calendar.widget-data-loader.service';
@Module({})
export class EventEmitterModule implements OnModuleInit {
@ -104,6 +105,7 @@ export class EventEmitterModule implements OnModuleInit {
DashboardsService,
DashboardsDataService,
WidgetsCollectionService,
CalendarWidgetDataLoaderService,
],
exports: [
EventEmitterService,
@ -142,6 +144,7 @@ export class EventEmitterModule implements OnModuleInit {
DashboardsService,
DashboardsDataService,
WidgetsCollectionService,
CalendarWidgetDataLoaderService,
],
controllers: [
MainController,