Добавлен загрузчик данных для календаря в дашбордах
This commit is contained in:
parent
d7b37470ef
commit
50f7aeaf2f
4 changed files with 84 additions and 7 deletions
|
|
@ -42,16 +42,21 @@ export type IssueAndEvent = {
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CalendarService {
|
export class CalendarService {
|
||||||
private interval = 30 * 24 * 60 * 60 * 1000; // 30 days
|
private defaultInterval = 30 * 24 * 60 * 60 * 1000; // 30 days
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public calendarEventsKey: string,
|
public calendarEventsKey: string,
|
||||||
private issuesService: IssuesService,
|
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 issues = await this.issuesService.find(filter);
|
||||||
const actualEvents = this.getActualEvents(issues);
|
const actualEvents = this.getActualEvents(issues, interval);
|
||||||
const formattedEvents = actualEvents
|
const formattedEvents = actualEvents
|
||||||
.map((event) => {
|
.map((event) => {
|
||||||
return this.generateICalendarEvent(event.issue, event.event);
|
return this.generateICalendarEvent(event.issue, event.event);
|
||||||
|
|
@ -63,9 +68,21 @@ export class CalendarService {
|
||||||
return res;
|
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(
|
private getActualEvents(
|
||||||
issues: RedmineTypes.ExtendedIssue[],
|
issues: RedmineTypes.ExtendedIssue[],
|
||||||
|
interval?: number,
|
||||||
): IssueAndEvent[] {
|
): IssueAndEvent[] {
|
||||||
|
if (typeof interval !== 'number') interval = this.defaultInterval;
|
||||||
const res: IssueAndEvent[] = [];
|
const res: IssueAndEvent[] = [];
|
||||||
for (let i = 0; i < issues.length; i++) {
|
for (let i = 0; i < issues.length; i++) {
|
||||||
const issue = issues[i];
|
const issue = issues[i];
|
||||||
|
|
@ -78,16 +95,18 @@ export class CalendarService {
|
||||||
const events = issue[this.calendarEventsKey];
|
const events = issue[this.calendarEventsKey];
|
||||||
for (let j = 0; j < events.length; j++) {
|
for (let j = 0; j < events.length; j++) {
|
||||||
const event = events[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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private actualEvent(event: CalendarEvent): boolean {
|
private actualEvent(event: CalendarEvent, interval: number): boolean {
|
||||||
const now = Luxon.DateTime.now().toMillis();
|
const now = Luxon.DateTime.now().toMillis();
|
||||||
const from = now - this.interval;
|
const from = now - interval;
|
||||||
const to = now + this.interval;
|
const to = now + interval;
|
||||||
return Boolean(
|
return Boolean(
|
||||||
(from <= event.fromTimestamp && event.fromTimestamp <= to) ||
|
(from <= event.fromTimestamp && event.fromTimestamp <= to) ||
|
||||||
(from <= event.toTimestamp && event.toTimestamp <= to),
|
(from <= event.toTimestamp && event.toTimestamp <= to),
|
||||||
|
|
|
||||||
|
|
@ -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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import { ListIssuesByUsersLikeJiraWidgetDataLoaderService } from './widget-data-
|
||||||
import { RootIssueSubTreesWidgetDataLoaderService } from './widget-data-loader/root-issue-subtrees.widget-data-loader.service';
|
import { RootIssueSubTreesWidgetDataLoaderService } from './widget-data-loader/root-issue-subtrees.widget-data-loader.service';
|
||||||
import { createInteractiveWidget } from './interactive-widget-factory';
|
import { createInteractiveWidget } from './interactive-widget-factory';
|
||||||
import { Result, success } from '@app/event-emitter/utils/result';
|
import { Result, success } from '@app/event-emitter/utils/result';
|
||||||
|
import { CalendarWidgetDataLoaderService } from './widget-data-loader/calendar.widget-data-loader.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WidgetsCollectionService {
|
export class WidgetsCollectionService {
|
||||||
|
|
@ -14,6 +15,7 @@ export class WidgetsCollectionService {
|
||||||
private listIssuesByFieldsWidgetDataLoaderService: ListIssuesByFieldsWidgetDataLoaderService,
|
private listIssuesByFieldsWidgetDataLoaderService: ListIssuesByFieldsWidgetDataLoaderService,
|
||||||
private listIssuesByUsersLikeJiraWidgetDataLoaderService: ListIssuesByUsersLikeJiraWidgetDataLoaderService,
|
private listIssuesByUsersLikeJiraWidgetDataLoaderService: ListIssuesByUsersLikeJiraWidgetDataLoaderService,
|
||||||
private rootIssueSubTreesWidgetDataLoaderService: RootIssueSubTreesWidgetDataLoaderService,
|
private rootIssueSubTreesWidgetDataLoaderService: RootIssueSubTreesWidgetDataLoaderService,
|
||||||
|
private calendarWidgetDataLoaderService: CalendarWidgetDataLoaderService,
|
||||||
) {
|
) {
|
||||||
const collection = [
|
const collection = [
|
||||||
createInteractiveWidget(
|
createInteractiveWidget(
|
||||||
|
|
@ -40,6 +42,10 @@ export class WidgetsCollectionService {
|
||||||
this.rootIssueSubTreesWidgetDataLoaderService,
|
this.rootIssueSubTreesWidgetDataLoaderService,
|
||||||
'issues_list_by_tree',
|
'issues_list_by_tree',
|
||||||
),
|
),
|
||||||
|
createInteractiveWidget(
|
||||||
|
this.calendarWidgetDataLoaderService,
|
||||||
|
'calendar_next_events',
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
collection.forEach((w) => this.appendWidget(w));
|
collection.forEach((w) => this.appendWidget(w));
|
||||||
|
|
|
||||||
|
|
@ -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 { ListIssuesByFieldsWidgetDataLoaderService } from './dashboards/widget-data-loader/list-issues-by-fields.widget-data-loader.service';
|
||||||
import { WidgetsCollectionService } from './dashboards/widgets-collection.service';
|
import { WidgetsCollectionService } from './dashboards/widgets-collection.service';
|
||||||
import { DashboardsController } from './dashboards/dashboards.controller';
|
import { DashboardsController } from './dashboards/dashboards.controller';
|
||||||
|
import { CalendarWidgetDataLoaderService } from './dashboards/widget-data-loader/calendar.widget-data-loader.service';
|
||||||
|
|
||||||
@Module({})
|
@Module({})
|
||||||
export class EventEmitterModule implements OnModuleInit {
|
export class EventEmitterModule implements OnModuleInit {
|
||||||
|
|
@ -104,6 +105,7 @@ export class EventEmitterModule implements OnModuleInit {
|
||||||
DashboardsService,
|
DashboardsService,
|
||||||
DashboardsDataService,
|
DashboardsDataService,
|
||||||
WidgetsCollectionService,
|
WidgetsCollectionService,
|
||||||
|
CalendarWidgetDataLoaderService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
EventEmitterService,
|
EventEmitterService,
|
||||||
|
|
@ -142,6 +144,7 @@ export class EventEmitterModule implements OnModuleInit {
|
||||||
DashboardsService,
|
DashboardsService,
|
||||||
DashboardsDataService,
|
DashboardsDataService,
|
||||||
WidgetsCollectionService,
|
WidgetsCollectionService,
|
||||||
|
CalendarWidgetDataLoaderService,
|
||||||
],
|
],
|
||||||
controllers: [
|
controllers: [
|
||||||
MainController,
|
MainController,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue