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:
parent
e2864117b2
commit
509a398639
@ -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'));
|
||||
}
|
||||
|
@ -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}));
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -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}
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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',
|
||||
|
@ -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">×</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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user