1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

implementing after trigger

This commit is contained in:
Patrik J. Braun 2019-12-26 23:30:46 +01:00
parent e2864117b2
commit 509a398639
12 changed files with 258 additions and 134 deletions

View File

@ -436,7 +436,7 @@ export class SettingsMWs {
}
}
public static async updateTasksSettings(req: Request, res: Response, next: NextFunction) {
public static async updateJobSettings(req: Request, res: Response, next: NextFunction) {
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
}

View File

@ -3,8 +3,9 @@ import {JobProgressDTO} from '../../../common/entities/settings/JobProgressDTO';
import {IJob} from './jobs/IJob';
import {JobRepository} from './JobRepository';
import {Config} from '../../../common/config/private/Config';
import {JobScheduleDTO, JobTriggerType} from '../../../common/entities/job/JobScheduleDTO';
import {AfterJobTrigger, JobScheduleDTO, JobTriggerType} from '../../../common/entities/job/JobScheduleDTO';
import {Logger} from '../../Logger';
import {NotificationManager} from '../NotifocationManager';
declare var global: NodeJS.Global;
@ -32,7 +33,9 @@ export class JobManager implements IJobManager {
async run<T>(jobName: string, config: T): Promise<void> {
const t = this.findJob(jobName);
if (t) {
await t.start(config);
await t.start(config, () => {
this.onJobFinished(t);
});
} else {
Logger.warn(LOG_TAG, 'cannot find job to start:' + jobName);
}
@ -50,6 +53,22 @@ export class JobManager implements IJobManager {
}
}
async onJobFinished(job: IJob<any>): Promise<void> {
const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name);
if (sch) {
console.log('found parent');
const children = Config.Server.Jobs.scheduled.filter(s => s.trigger.type === JobTriggerType.after &&
(<AfterJobTrigger>s.trigger).afterScheduleName === sch.name);
for (let i = 0; i < children.length; ++i) {
try {
await this.run(children[i].jobName, children[i].config);
} catch (e) {
NotificationManager.warning('Job running error:' + children[i].name, e.toString());
}
}
}
}
getAvailableJobs(): IJob<any>[] {
return JobRepository.Instance.getAvailableJobs();
}
@ -65,58 +84,12 @@ export class JobManager implements IJobManager {
Config.Server.Jobs.scheduled.forEach(s => this.runSchedule(s));
}
protected getNextDayOfTheWeek(refDate: Date, dayOfWeek: number) {
const date = new Date(refDate);
date.setDate(refDate.getDate() + (dayOfWeek + 1 + 7 - refDate.getDay()) % 7);
if (date.getDay() === refDate.getDay()) {
return new Date(refDate);
}
date.setHours(0, 0, 0, 0);
return date;
}
protected nextValidDate(date: Date, h: number, m: number, dayDiff: number): Date {
date.setSeconds(0);
if (date.getHours() < h || (date.getHours() === h && date.getMinutes() < m)) {
date.setHours(h);
date.setMinutes(m);
} else {
date.setTime(date.getTime() + dayDiff);
date.setHours(h);
date.setMinutes(m);
}
return date;
}
protected getDateFromSchedule(refDate: Date, schedule: JobScheduleDTO): Date {
switch (schedule.trigger.type) {
case JobTriggerType.scheduled:
return new Date(schedule.trigger.time);
case JobTriggerType.periodic:
const hour = Math.floor(schedule.trigger.atTime / 1000 / (60 * 60));
const minute = (schedule.trigger.atTime / 1000 / 60) % 60;
if (schedule.trigger.periodicity <= 6) { // Between Monday and Sunday
const nextRunDate = this.getNextDayOfTheWeek(refDate, schedule.trigger.periodicity);
return this.nextValidDate(nextRunDate, hour, minute, 7 * 24 * 60 * 60 * 1000);
}
// every day
return this.nextValidDate(new Date(refDate), hour, minute, 24 * 60 * 60 * 1000);
}
return null;
}
protected findJob<T = any>(jobName: string): IJob<T> {
return this.getAvailableJobs().find(t => t.Name === jobName);
}
private runSchedule(schedule: JobScheduleDTO) {
const nextDate = this.getDateFromSchedule(new Date(), schedule);
const nextDate = JobScheduleDTO.getNextRunningDate(new Date(), schedule);
if (nextDate && nextDate.getTime() > Date.now()) {
Logger.debug(LOG_TAG, 'running schedule: ' + schedule.jobName +
' at ' + nextDate.toLocaleString(undefined, {hour12: false}));

View File

@ -10,7 +10,7 @@ import {Logger} from '../../../Logger';
declare var global: NodeJS.Global;
const LOG_TAG = '[FileTask]';
const LOG_TAG = '[FileJob]';
export abstract class FileJob<T, S = void> extends Job<S> {
@ -56,7 +56,7 @@ export abstract class FileJob<T, S = void> extends Job<S> {
await this.processFile(file);
} catch (e) {
console.error(e);
Logger.error(LOG_TAG, 'Error during processing file: ' + e.toString());
Logger.error(LOG_TAG, 'Error during processing file.' + ', ' + e.toString());
}
}
return this.progress;

View File

@ -6,7 +6,7 @@ export interface IJob<T> extends JobDTO {
Supported: boolean;
Progress: JobProgressDTO;
start(config: T): Promise<void>;
start(config: T, onFinishCB?: () => void): Promise<void>;
stop(): void;

View File

@ -2,7 +2,6 @@ import {JobProgressDTO, JobState} from '../../../../common/entities/settings/Job
import {Logger} from '../../../Logger';
import {IJob} from './IJob';
import {ConfigTemplateEntry, JobDTO} from '../../../../common/entities/job/JobDTO';
import * as rimraf from 'rimraf';
declare const process: any;
@ -16,20 +15,19 @@ export abstract class Job<T = void> implements IJob<T> {
protected prResolve: () => void;
protected IsInstant = false;
public abstract get Supported(): boolean;
public abstract get Name(): string;
public abstract get ConfigTemplate(): ConfigTemplateEntry[];
public get Progress(): JobProgressDTO {
return this.progress;
}
public start(config: T): Promise<void> {
public start(config: T, onFinishCB = () => {
}): Promise<void> {
this.OnFinishCB = onFinishCB;
if (this.state === JobState.idle && this.Supported) {
Logger.info(LOG_TAG, 'Running job: ' + this.Name);
this.config = config;
@ -72,6 +70,9 @@ export abstract class Job<T = void> implements IJob<T> {
};
}
protected OnFinishCB = () => {
};
protected abstract async step(): Promise<JobProgressDTO>;
protected abstract async init(): Promise<void>;
@ -85,6 +86,7 @@ export abstract class Job<T = void> implements IJob<T> {
if (this.IsInstant) {
this.prResolve();
}
this.OnFinishCB();
}
private run() {

View File

@ -104,10 +104,10 @@ export class SettingsRouter {
SettingsMWs.updateIndexingSettings,
RenderingMWs.renderOK
);
app.put('/api/settings/tasks',
app.put('/api/settings/jobs',
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
SettingsMWs.updateTasksSettings,
SettingsMWs.updateJobSettings,
RenderingMWs.renderOK
);
}

View File

@ -76,17 +76,15 @@ export class PrivateConfigDefaultsClass extends PublicConfigClass implements IPr
},
Jobs: {
scheduled: [{
name: DefaultsJobs[DefaultsJobs['Database Reset']],
jobName: DefaultsJobs[DefaultsJobs['Database Reset']],
config: {},
trigger: {type: JobTriggerType.never}
}, {
name: DefaultsJobs[DefaultsJobs.Indexing],
jobName: DefaultsJobs[DefaultsJobs.Indexing],
config: {},
trigger: {type: JobTriggerType.never}
}, {
jobName: DefaultsJobs[DefaultsJobs['Video Converting']],
config: {},
trigger: {type: JobTriggerType.never}
}]
}
};

View File

@ -1,5 +1,5 @@
export enum JobTriggerType {
never = 1, scheduled = 2, periodic = 3
never = 1, scheduled = 2, periodic = 3, after = 4
}
export interface JobTrigger {
@ -21,8 +21,64 @@ export interface PeriodicJobTrigger extends JobTrigger {
atTime: number; // day time
}
export interface AfterJobTrigger extends JobTrigger {
type: JobTriggerType.after;
afterScheduleName: string; // runs after schedule
}
export interface JobScheduleDTO {
name: string;
jobName: string;
config: any;
trigger: NeverJobTrigger | ScheduledJobTrigger | PeriodicJobTrigger;
trigger: NeverJobTrigger | ScheduledJobTrigger | PeriodicJobTrigger | AfterJobTrigger;
}
export module JobScheduleDTO {
const getNextDayOfTheWeek = (refDate: Date, dayOfWeek: number) => {
const date = new Date(refDate);
date.setDate(refDate.getDate() + (dayOfWeek + 1 + 7 - refDate.getDay()) % 7);
if (date.getDay() === refDate.getDay()) {
return new Date(refDate);
}
date.setHours(0, 0, 0, 0);
return date;
};
const nextValidDate = (date: Date, h: number, m: number, dayDiff: number): Date => {
date.setSeconds(0);
if (date.getHours() < h || (date.getHours() === h && date.getMinutes() < m)) {
date.setHours(h);
date.setMinutes(m);
} else {
date.setTime(date.getTime() + dayDiff);
date.setHours(h);
date.setMinutes(m);
}
return date;
};
export const getNextRunningDate = (refDate: Date, schedule: JobScheduleDTO): Date => {
switch (schedule.trigger.type) {
case JobTriggerType.scheduled:
return new Date(schedule.trigger.time);
case JobTriggerType.periodic:
const hour = Math.floor(schedule.trigger.atTime / 1000 / (60 * 60));
const minute = (schedule.trigger.atTime / 1000 / 60) % 60;
if (schedule.trigger.periodicity <= 6) { // Between Monday and Sunday
const nextRunDate = getNextDayOfTheWeek(refDate, schedule.trigger.periodicity);
return nextValidDate(nextRunDate, hour, minute, 7 * 24 * 60 * 60 * 1000);
}
// every day
return nextValidDate(new Date(refDate), hour, minute, 24 * 60 * 60 * 1000);
}
return null;
};
}

View File

@ -64,47 +64,47 @@
</nav>
</div>
<div class="col-md-10">
<app-settings-basic #setting #basic
[simplifiedMode]="simplifiedMode"
[hidden]="!basic.HasAvailableSettings"></app-settings-basic>
<app-settings-usermanager #setting #userManager
[hidden]="!userManager.HasAvailableSettings"></app-settings-usermanager>
<app-settings-database #setting #database
[simplifiedMode]="simplifiedMode"
[hidden]="!database.HasAvailableSettings"></app-settings-database>
<app-settings-photo #setting #photo
[hidden]="!photo.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-photo>
<app-settings-video #setting #video
[hidden]="!video.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-video>
<app-settings-thumbnail #setting #thumbnail
[hidden]="!thumbnail.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
<app-settings-search #setting #search
[hidden]="!search.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-search>
<app-settings-share #setting #share
[hidden]="!share.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-share>
<app-settings-map #setting #map
[hidden]="!map.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-map>
<app-settings-meta-file #setting #metaFile
[hidden]="!metaFile.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-meta-file>
<app-settings-other #setting #other
[hidden]="!other.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-other>
<app-settings-random-photo #setting #random
[hidden]="!random.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-random-photo>
<app-settings-faces #setting #faces
[hidden]="!faces.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-faces>
<app-settings-indexing #setting #indexing
[hidden]="!indexing.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-indexing>
<!-- <app-settings-basic #setting #basic-->
<!-- [simplifiedMode]="simplifiedMode"-->
<!-- [hidden]="!basic.HasAvailableSettings"></app-settings-basic>-->
<!-- <app-settings-usermanager #setting #userManager-->
<!-- [hidden]="!userManager.HasAvailableSettings"></app-settings-usermanager>-->
<!-- <app-settings-database #setting #database-->
<!-- [simplifiedMode]="simplifiedMode"-->
<!-- [hidden]="!database.HasAvailableSettings"></app-settings-database>-->
<!-- <app-settings-photo #setting #photo-->
<!-- [hidden]="!photo.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-photo>-->
<!-- <app-settings-video #setting #video-->
<!-- [hidden]="!video.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-video>-->
<!-- <app-settings-thumbnail #setting #thumbnail-->
<!-- [hidden]="!thumbnail.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-thumbnail>-->
<!-- <app-settings-search #setting #search-->
<!-- [hidden]="!search.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-search>-->
<!-- <app-settings-share #setting #share-->
<!-- [hidden]="!share.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-share>-->
<!-- <app-settings-map #setting #map-->
<!-- [hidden]="!map.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-map>-->
<!-- <app-settings-meta-file #setting #metaFile-->
<!-- [hidden]="!metaFile.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-meta-file>-->
<!-- <app-settings-other #setting #other-->
<!-- [hidden]="!other.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-other>-->
<!-- <app-settings-random-photo #setting #random-->
<!-- [hidden]="!random.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-random-photo>-->
<!-- <app-settings-faces #setting #faces-->
<!-- [hidden]="!faces.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-faces>-->
<!-- <app-settings-indexing #setting #indexing-->
<!-- [hidden]="!indexing.HasAvailableSettings"-->
<!-- [simplifiedMode]="simplifiedMode"></app-settings-indexing>-->
<app-settings-jobs #setting #jobs
[hidden]="!jobs.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-jobs>

View File

@ -27,8 +27,8 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
public inProgress = false;
public error: string = null;
public changed = false;
public settings: T = <any>{};
public original: T = <any>{};
public settings: T = <T>{};
public original: T = <T>{};
text = {
Enabled: 'Enabled',
Disabled: 'Disabled',

View File

@ -1,16 +1,17 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
{{Name}}<ng-container *ngIf="changed">*</ng-container>
{{Name}}
<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
<div *ngFor="let schedule of settings.scheduled; let i= index">
<div class="card bg-light">
<div class="card-header clickable" (click)="showDetails[i]=!showDetails[i]">
<div *ngFor="let schedule of sortedSchedules() as schedules; let i= index">
<div class="card bg-light {{shouldIdent(schedule,schedules[i-1])? 'ml-4' : ''}}">
<div class="card-header clickable" (click)="showDetails[schedule.name]=!showDetails[schedule.name]">
<div class="d-flex justify-content-between">
{{schedule.jobName}} @<!--
{{schedule.name}} @<!--
-->
<ng-container [ngSwitch]="schedule.trigger.type">
<ng-container *ngSwitchCase="JobTriggerType.periodic">
@ -20,28 +21,24 @@
<ng-container
*ngSwitchCase="JobTriggerType.scheduled">{{schedule.trigger.time | date:"medium"}}</ng-container>
<ng-container *ngSwitchCase="JobTriggerType.never" i18n>never</ng-container>
<ng-container *ngSwitchCase="JobTriggerType.after">
<ng-container i18n>after</ng-container>
{{schedule.trigger.afterScheduleName}}
</ng-container>
</ng-container>
<button class="btn btn-danger button-delete" (click)="remove(i)"><span class="oi oi-trash"></span>
</button>
</div>
</div>
<div class="card-body" [hidden]="!showDetails[i]">
<div class="card-body" [hidden]="!showDetails[schedule.name]">
<div class="row">
<div class="col-md-9">
<div class="form-group row">
<label class="col-md-2 control-label" [for]="'jobName'+i" i18n>Job:</label>
<div class="col-md-10">
<select class="form-control" (change)="jobTypeChanged(schedule)" [(ngModel)]="schedule.jobName"
[name]="'jobName'+i" required>
<option *ngFor="let availableJob of _settingsService.availableJobs | async"
[ngValue]="availableJob.Name">{{availableJob.Name}}
</option>
</select>
<small class="form-text text-muted"
i18n>Select a job to schedule.
</small>
{{schedule.jobName}}
</div>
</div>
<div class="form-group row">
@ -60,6 +57,23 @@
</div>
</div>
<div class="form-group row" *ngIf="schedule.trigger.type == JobTriggerType.after">
<label class="col-md-2 control-label" [for]="'triggerAfter'+i" i18n>After:</label>
<div class="col-md-10">
<select class="form-control" [(ngModel)]="schedule.trigger.afterScheduleName"
[name]="'triggerAfter'+i" required>
<ng-container *ngFor="let sch of settings.scheduled">
<option *ngIf="sch.name !== schedule.name"
[ngValue]="sch.name">{{sch.name}}
</option>
</ng-container>
</select>
<small class="form-text text-muted"
i18n>The job will run after that job finishes.
</small>
</div>
</div>
<div class="form-group row" *ngIf="schedule.trigger.type == JobTriggerType.scheduled">
<label class="col-md-2 control-label" [for]="'triggerTime'+i" i18n>At:</label>
@ -191,10 +205,47 @@
(click)="reset()" i18n>Reset
</button>
<button class="btn btn-primary float-right"
(click)="addNewJob()" i18n>+ Add Job
(click)="prepareNewJob()" i18n>+ Add Job
</button>
</div>
</div>
</form>
<!-- Modal -->
<div bsModal #jobModal="bs-modal" class="modal fade" id="jobModal" tabindex="-1" role="dialog"
aria-labelledby="jobModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="jobModalLabel" i18n>Add new job</h5>
<button type="button" class="close" (click)="jobModal.hide()" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form #jobModalForm="ngForm">
<div class="modal-body">
<select class="form-control" (change)="jobTypeChanged(newSchedule)" [(ngModel)]="newSchedule.jobName"
name="newJobName" required>
<option *ngFor="let availableJob of _settingsService.availableJobs | async"
[ngValue]="availableJob.Name">{{availableJob.Name}}
</option>
</select>
<small class="form-text text-muted"
i18n>Select a job to schedule.
</small>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="jobModal.hide()" i18n>Close</button>
<button type="button" class="btn btn-primary" data-dismiss="modal"
(click)="addNewJob()"
[disabled]="!jobModalForm.form.valid" i18n>Add Job
</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
import {Component, OnChanges, OnDestroy, OnInit} from '@angular/core';
import {Component, OnChanges, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {JobsSettingsService} from './jobs.settings.service';
import {AuthenticationService} from '../../../model/network/authentication.service';
import {NavigationService} from '../../../model/navigation.service';
@ -8,6 +8,7 @@ import {I18n} from '@ngx-translate/i18n-polyfill';
import {ErrorDTO} from '../../../../../common/entities/Error';
import {ScheduledJobsService} from '../scheduled-jobs.service';
import {
AfterJobTrigger,
JobScheduleDTO,
JobTriggerType,
NeverJobTrigger,
@ -18,6 +19,8 @@ import {Utils} from '../../../../../common/Utils';
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
import {JobState} from '../../../../../common/entities/settings/JobProgressDTO';
import {Job} from '../../../../../backend/model/jobs/jobs/Job';
import {ModalDirective} from 'ngx-bootstrap/modal';
@Component({
selector: 'app-settings-jobs',
@ -29,12 +32,21 @@ import {JobState} from '../../../../../common/entities/settings/JobProgressDTO';
export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobConfig, JobsSettingsService>
implements OnInit, OnDestroy, OnChanges {
@ViewChild('jobModal', {static: false}) public jobModal: ModalDirective;
disableButtons = false;
JobTriggerTypeMap: { key: number, value: string }[];
JobTriggerType = JobTriggerType;
periods: string[] = [];
showDetails: boolean[] = [];
JobState = JobState;
newSchedule: JobScheduleDTO = {
name: '',
config: null,
jobName: '',
trigger: {
type: JobTriggerType.never
}
};
constructor(_authService: AuthenticationService,
_navigation: NavigationService,
@ -63,7 +75,6 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
this.i18n('day')]; // 7
}
getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
const job = this._settingsService.availableJobs.value.find(t => t.Name === JobName);
if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) {
@ -83,7 +94,6 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
this.jobsService.unsubscribeFromProgress();
}
public async start(schedule: JobScheduleDTO) {
this.error = '';
try {
@ -126,7 +136,6 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
this.settings.scheduled.splice(index, 1);
}
jobTypeChanged(schedule: JobScheduleDTO) {
const job = this._settingsService.availableJobs.value.find(t => t.Name === schedule.jobName);
schedule.config = schedule.config || {};
@ -135,9 +144,10 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
}
}
addNewJob() {
prepareNewJob() {
const jobName = this._settingsService.availableJobs.value[0].Name;
const newSchedule: JobScheduleDTO = {
this.newSchedule = {
name: 'new job',
jobName: jobName,
config: <any>{},
trigger: {
@ -146,11 +156,12 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
};
const job = this._settingsService.availableJobs.value.find(t => t.Name === jobName);
newSchedule.config = newSchedule.config || {};
this.newSchedule.config = this.newSchedule.config || {};
if (job.ConfigTemplate) {
job.ConfigTemplate.forEach(ct => newSchedule.config[ct.id] = ct.defaultValue);
job.ConfigTemplate.forEach(ct => this.newSchedule.config[ct.id] = ct.defaultValue);
}
this.settings.scheduled.push(newSchedule);
this.jobModal.show();
}
jobTriggerTypeChanged(triggerType: JobTriggerType, schedule: JobScheduleDTO) {
@ -183,6 +194,39 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
return configElement[id].join('; ');
}
public shouldIdent(curr: JobScheduleDTO, prev: JobScheduleDTO) {
return curr && curr.trigger.type === JobTriggerType.after && prev && prev.name === curr.trigger.afterScheduleName;
}
public sortedSchedules() {
return this.settings.scheduled.slice().sort((a, b) => {
return this.getNextRunningDate(a, this.settings.scheduled) - this.getNextRunningDate(b, this.settings.scheduled);
});
}
addNewJob() {
const jobName = this.newSchedule.jobName;
const count = this.settings.scheduled.filter(s => s.jobName === jobName).length;
this.newSchedule.name = count === 0 ? jobName : jobName + ' ' + (count + 1);
this.settings.scheduled.push(this.newSchedule);
}
private getNextRunningDate(sch: JobScheduleDTO, list: JobScheduleDTO[], depth: number = 0): number {
if (depth > list.length) {
return 0;
}
if (sch.trigger.type === JobTriggerType.never) {
return list.map(s => s.name).sort().indexOf(sch.name) * -1;
}
if (sch.trigger.type === JobTriggerType.after) {
const parent = list.find(s => s.name === (<AfterJobTrigger>sch.trigger).afterScheduleName);
if (parent) {
return this.getNextRunningDate(parent, list, depth + 1) + 0.001;
}
}
const d = JobScheduleDTO.getNextRunningDate(new Date(), sch);
return d !== null ? d.getTime() : 0;
}
}