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

adding job last run status

This commit is contained in:
Patrik J. Braun 2019-12-27 00:24:44 +01:00
parent 509a398639
commit 8b6a367257
25 changed files with 206 additions and 79 deletions

View File

@ -102,4 +102,15 @@ export class AdminMWs {
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err)); return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err));
} }
} }
public static getJobLastRuns(req: Request, res: Response, next: NextFunction) {
try {
req.resultPipe = ObjectManagers.getInstance().JobManager.getJobLastRuns();
return next();
} catch (err) {
if (err instanceof Error) {
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + err.toString(), err));
}
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err));
}
}
} }

View File

@ -1,5 +1,6 @@
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO} from '../../../../common/entities/job/JobProgressDTO';
import {JobDTO} from '../../../../common/entities/job/JobDTO'; import {JobDTO} from '../../../../common/entities/job/JobDTO';
import {JobLastRunDTO} from '../../../../common/entities/job/JobLastRunDTO';
export interface IJobManager { export interface IJobManager {
@ -15,4 +16,6 @@ export interface IJobManager {
stopSchedules(): void; stopSchedules(): void;
runSchedules(): void; runSchedules(): void;
getJobLastRuns(): { [key: string]: { [key: string]: JobLastRunDTO } };
} }

View File

@ -1,11 +1,12 @@
import {IJobManager} from '../database/interfaces/IJobManager'; import {IJobManager} from '../database/interfaces/IJobManager';
import {JobProgressDTO} from '../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO} from '../../../common/entities/job/JobProgressDTO';
import {IJob} from './jobs/IJob'; import {IJob} from './jobs/IJob';
import {JobRepository} from './JobRepository'; import {JobRepository} from './JobRepository';
import {Config} from '../../../common/config/private/Config'; import {Config} from '../../../common/config/private/Config';
import {AfterJobTrigger, JobScheduleDTO, JobTriggerType} from '../../../common/entities/job/JobScheduleDTO'; import {AfterJobTrigger, JobScheduleDTO, JobTriggerType} from '../../../common/entities/job/JobScheduleDTO';
import {Logger} from '../../Logger'; import {Logger} from '../../Logger';
import {NotificationManager} from '../NotifocationManager'; import {NotificationManager} from '../NotifocationManager';
import {JobLastRunDTO} from '../../../common/entities/job/JobLastRunDTO';
declare var global: NodeJS.Global; declare var global: NodeJS.Global;
@ -30,6 +31,14 @@ export class JobManager implements IJobManager {
return m; return m;
} }
getJobLastRuns(): { [key: string]: { [key: string]: JobLastRunDTO } } {
const m: { [id: string]: { [id: string]: JobLastRunDTO } } = {};
JobRepository.Instance.getAvailableJobs().forEach(t => {
m[t.Name] = t.LastRuns;
});
return m;
}
async run<T>(jobName: string, config: T): Promise<void> { async run<T>(jobName: string, config: T): Promise<void> {
const t = this.findJob(jobName); const t = this.findJob(jobName);
if (t) { if (t) {
@ -54,9 +63,10 @@ export class JobManager implements IJobManager {
} }
async onJobFinished(job: IJob<any>): Promise<void> { async onJobFinished(job: IJob<any>): Promise<void> {
console.log('onFinished' + job.Name);
const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name); const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name);
if (sch) { if (sch) {
console.log('found parent'); console.log('parent found' + sch.jobName);
const children = Config.Server.Jobs.scheduled.filter(s => s.trigger.type === JobTriggerType.after && const children = Config.Server.Jobs.scheduled.filter(s => s.trigger.type === JobTriggerType.after &&
(<AfterJobTrigger>s.trigger).afterScheduleName === sch.name); (<AfterJobTrigger>s.trigger).afterScheduleName === sch.name);
for (let i = 0; i < children.length; ++i) { for (let i = 0; i < children.length; ++i) {

View File

@ -1,4 +1,4 @@
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO} from '../../../../common/entities/job/JobProgressDTO';
import {ObjectManagers} from '../../ObjectManagers'; import {ObjectManagers} from '../../ObjectManagers';
import {Config} from '../../../../common/config/private/Config'; import {Config} from '../../../../common/config/private/Config';
import {ConfigTemplateEntry, DefaultsJobs} from '../../../../common/entities/job/JobDTO'; import {ConfigTemplateEntry, DefaultsJobs} from '../../../../common/entities/job/JobDTO';

View File

@ -1,4 +1,4 @@
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO} from '../../../../common/entities/job/JobProgressDTO';
import {ConfigTemplateEntry} from '../../../../common/entities/job/JobDTO'; import {ConfigTemplateEntry} from '../../../../common/entities/job/JobDTO';
import {Job} from './Job'; import {Job} from './Job';
import * as path from 'path'; import * as path from 'path';

View File

@ -1,12 +1,14 @@
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO} from '../../../../common/entities/job/JobProgressDTO';
import {JobDTO} from '../../../../common/entities/job/JobDTO'; import {JobDTO} from '../../../../common/entities/job/JobDTO';
import {JobLastRunDTO} from '../../../../common/entities/job/JobLastRunDTO';
export interface IJob<T> extends JobDTO { export interface IJob<T> extends JobDTO {
Name: string; Name: string;
Supported: boolean; Supported: boolean;
Progress: JobProgressDTO; Progress: JobProgressDTO;
LastRuns: { [key: string]: JobLastRunDTO };
start(config: T, onFinishCB?: () => void): Promise<void>; start(config: T, OnFinishCB: () => void): Promise<void>;
stop(): void; stop(): void;

View File

@ -1,4 +1,4 @@
import {JobProgressDTO, JobState} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO, JobState} from '../../../../common/entities/job/JobProgressDTO';
import {ObjectManagers} from '../../ObjectManagers'; import {ObjectManagers} from '../../ObjectManagers';
import * as path from 'path'; import * as path from 'path';
import {Config} from '../../../../common/config/private/Config'; import {Config} from '../../../../common/config/private/Config';

View File

@ -1,9 +1,11 @@
import {JobProgressDTO, JobState} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO, JobState} from '../../../../common/entities/job/JobProgressDTO';
import {Logger} from '../../../Logger'; import {Logger} from '../../../Logger';
import {IJob} from './IJob'; import {IJob} from './IJob';
import {ConfigTemplateEntry, JobDTO} from '../../../../common/entities/job/JobDTO'; import {ConfigTemplateEntry, JobDTO} from '../../../../common/entities/job/JobDTO';
import {JobLastRunDTO, JobLastRunState} from '../../../../common/entities/job/JobLastRunDTO';
declare const process: any; declare const process: any;
declare const global: any;
const LOG_TAG = '[JOB]'; const LOG_TAG = '[JOB]';
@ -14,6 +16,7 @@ export abstract class Job<T = void> implements IJob<T> {
protected config: T; protected config: T;
protected prResolve: () => void; protected prResolve: () => void;
protected IsInstant = false; protected IsInstant = false;
protected lastRuns: { [key: string]: JobLastRunDTO } = {};
public abstract get Supported(): boolean; public abstract get Supported(): boolean;
@ -21,12 +24,15 @@ export abstract class Job<T = void> implements IJob<T> {
public abstract get ConfigTemplate(): ConfigTemplateEntry[]; public abstract get ConfigTemplate(): ConfigTemplateEntry[];
public get LastRuns(): { [key: string]: JobLastRunDTO } {
return this.lastRuns;
}
public get Progress(): JobProgressDTO { public get Progress(): JobProgressDTO {
return this.progress; return this.progress;
} }
public start(config: T, onFinishCB = () => { public start(config: T, onFinishCB: () => void): Promise<void> {
}): Promise<void> {
this.OnFinishCB = onFinishCB; this.OnFinishCB = onFinishCB;
if (this.state === JobState.idle && this.Supported) { if (this.state === JobState.idle && this.Supported) {
Logger.info(LOG_TAG, 'Running job: ' + this.Name); Logger.info(LOG_TAG, 'Running job: ' + this.Name);
@ -78,6 +84,17 @@ export abstract class Job<T = void> implements IJob<T> {
protected abstract async init(): Promise<void>; protected abstract async init(): Promise<void>;
private onFinish(): void { private onFinish(): void {
this.lastRuns[JSON.stringify(this.config)] = {
all: this.progress.left + this.progress.progress,
done: this.progress.progress,
comment: '',
config: this.config,
state: this.progress.state === JobState.stopping ? JobLastRunState.canceled : JobLastRunState.finished,
time: {
start: this.progress.time.start,
end: Date.now()
}
};
this.progress = null; this.progress = null;
if (global.gc) { if (global.gc) {
global.gc(); global.gc();
@ -95,16 +112,16 @@ export abstract class Job<T = void> implements IJob<T> {
if (this.state === JobState.idle) { if (this.state === JobState.idle) {
return; return;
} }
let prg = null;
if (this.state === JobState.running) { if (this.state === JobState.running) {
this.progress = await this.step(); prg = await this.step();
} else {
this.progress = null;
} }
if (this.progress == null) { // finished if (prg == null) { // finished
this.state = JobState.idle; this.state = JobState.idle;
this.onFinish(); this.onFinish();
return; return;
} }
this.progress = prg;
this.run(); this.run();
} catch (e) { } catch (e) {
Logger.error(LOG_TAG, e); Logger.error(LOG_TAG, e);

View File

@ -3,7 +3,7 @@ import * as path from 'path';
import * as util from 'util'; import * as util from 'util';
import {promises as fsp} from 'fs'; import {promises as fsp} from 'fs';
import {Job} from './Job'; import {Job} from './Job';
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO} from '../../../../common/entities/job/JobProgressDTO';
import {ProjectPath} from '../../../ProjectPath'; import {ProjectPath} from '../../../ProjectPath';
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing'; import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
import {VideoProcessing} from '../../fileprocessing/VideoProcessing'; import {VideoProcessing} from '../../fileprocessing/VideoProcessing';

View File

@ -28,14 +28,14 @@ export class ThumbnailGenerationJob extends FileJob<MediaDTO, { sizes: number[]
return true; return true;
} }
start(config: { sizes: number[] }): Promise<void> { start(config: { sizes: number[] }, OnFinishCB: () => void): Promise<void> {
for (let i = 0; i < config.sizes.length; ++i) { for (let i = 0; i < config.sizes.length; ++i) {
if (Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(config.sizes[i]) === -1) { if (Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(config.sizes[i]) === -1) {
throw new Error('unknown thumbnails size: ' + config.sizes[i] + '. Add it to the possible thumbnail sizes.'); throw new Error('unknown thumbnails size: ' + config.sizes[i] + '. Add it to the possible thumbnail sizes.');
} }
} }
return super.start(config); return super.start(config, OnFinishCB);
} }
protected async processDirectory(directory: DirectoryDTO): Promise<MediaDTO[]> { protected async processDirectory(directory: DirectoryDTO): Promise<MediaDTO[]> {

View File

@ -43,6 +43,12 @@ export class AdminRouter {
AdminMWs.getJobProgresses, AdminMWs.getJobProgresses,
RenderingMWs.renderResult RenderingMWs.renderResult
); );
app.get('/api/admin/jobs/scheduled/lastRun',
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
AdminMWs.getJobLastRuns,
RenderingMWs.renderResult
);
app.post('/api/admin/jobs/scheduled/:id/start', app.post('/api/admin/jobs/scheduled/:id/start',
AuthenticationMWs.authenticate, AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin), AuthenticationMWs.authorise(UserRoles.Admin),

View File

@ -75,17 +75,50 @@ export class PrivateConfigDefaultsClass extends PublicConfigClass implements IPr
listingLimit: 1000 listingLimit: 1000
}, },
Jobs: { Jobs: {
scheduled: [{ scheduled: [
name: DefaultsJobs[DefaultsJobs['Database Reset']], {
jobName: DefaultsJobs[DefaultsJobs['Database Reset']], name: DefaultsJobs[DefaultsJobs.Indexing],
config: {}, jobName: DefaultsJobs[DefaultsJobs.Indexing],
trigger: {type: JobTriggerType.never} config: {},
}, { trigger: {type: JobTriggerType.never}
name: DefaultsJobs[DefaultsJobs.Indexing], },
jobName: DefaultsJobs[DefaultsJobs.Indexing], {
config: {}, name: DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
trigger: {type: JobTriggerType.never} jobName: DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
}] config: {sizes: [160]},
trigger: {
type: JobTriggerType.after,
afterScheduleName: DefaultsJobs[DefaultsJobs.Indexing]
}
},
{
name: DefaultsJobs[DefaultsJobs['Photo Converting']],
jobName: DefaultsJobs[DefaultsJobs['Photo Converting']],
config: {},
trigger: {
type: JobTriggerType.after,
afterScheduleName: DefaultsJobs[DefaultsJobs['Thumbnail Generation']]
}
},
{
name: DefaultsJobs[DefaultsJobs['Video Converting']],
jobName: DefaultsJobs[DefaultsJobs['Video Converting']],
config: {},
trigger: {
type: JobTriggerType.after,
afterScheduleName: DefaultsJobs[DefaultsJobs['Photo Converting']]
}
},
{
name: DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']],
jobName: DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']],
config: {},
trigger: {
type: JobTriggerType.after,
afterScheduleName: DefaultsJobs[DefaultsJobs['Video Converting']]
}
}
]
} }
}; };
} }

View File

@ -115,7 +115,7 @@ export module ClientConfig {
/** /**
* These configuration will be available at frontend and backend too * These configuration will be available at frontend and backend too
*/ */
export class PublicConfigClass { export class PublicConfigClass {
public Client: ClientConfig.Config = { public Client: ClientConfig.Config = {
applicationTitle: 'PiGallery 2', applicationTitle: 'PiGallery 2',

View File

@ -0,0 +1,15 @@
export enum JobLastRunState {
finished = 1, canceled = 2
}
export interface JobLastRunDTO {
config: any;
done: number;
all: number;
state: JobLastRunState;
comment: string;
time: {
start: number,
end: number
};
}

View File

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

View File

@ -10,7 +10,7 @@ import {I18n} from '@ngx-translate/i18n-polyfill';
import {ScheduledJobsService} from '../scheduled-jobs.service'; import {ScheduledJobsService} from '../scheduled-jobs.service';
import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO';
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
import {JobState} from '../../../../../common/entities/settings/JobProgressDTO'; import {JobState} from '../../../../../common/entities/job/JobProgressDTO';
@Component({ @Component({
selector: 'app-settings-indexing', selector: 'app-settings-indexing',

View File

@ -189,9 +189,11 @@
</div> </div>
</ng-container> </ng-container>
</div> </div>
<app-settings-job-progress <app-settings-job-progress
class="card-footer bg-transparent" class="card-footer bg-transparent"
[progress]="jobsService.progress.value[schedule.jobName]"> [progress]="jobsService.progress.value[schedule.jobName]"
[lastRun]="(jobsService.lastRuns.value[schedule.jobName] || {})[getConfigHash(schedule)]">
</app-settings-job-progress> </app-settings-job-progress>
</div> </div>

View File

@ -18,7 +18,7 @@ import {
import {Utils} from '../../../../../common/Utils'; import {Utils} from '../../../../../common/Utils';
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO'; import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
import {JobState} from '../../../../../common/entities/settings/JobProgressDTO'; import {JobState} from '../../../../../common/entities/job/JobProgressDTO';
import {Job} from '../../../../../backend/model/jobs/jobs/Job'; import {Job} from '../../../../../backend/model/jobs/jobs/Job';
import {ModalDirective} from 'ngx-bootstrap/modal'; import {ModalDirective} from 'ngx-bootstrap/modal';
@ -211,6 +211,10 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
this.settings.scheduled.push(this.newSchedule); this.settings.scheduled.push(this.newSchedule);
} }
getConfigHash(schedule: JobScheduleDTO): string {
return JSON.stringify(schedule.config);
}
private getNextRunningDate(sch: JobScheduleDTO, list: JobScheduleDTO[], depth: number = 0): number { private getNextRunningDate(sch: JobScheduleDTO, list: JobScheduleDTO[], depth: number = 0): number {
if (depth > list.length) { if (depth > list.length) {
return 0; return 0;

View File

@ -1,3 +1,20 @@
<div class="row" *ngIf="!progress && lastRun">
<div class="col-md-2 col-12" i18n>
Last run:
</div>
<div class="col-md-4 col-12">
<span class="oi oi-clock" title="Run between" i18n-title aria-hidden="true"></span>
{{lastRun.time.start | date:'medium'}} - {{lastRun.time.end | date:'mediumTime'}}
</div>
<div class="col-md-3 col-6">
<span class="oi oi-check" title="done/all" i18n-title aria-hidden="true"></span>
{{lastRun.all}}/{{lastRun.done}}
</div>
<div class="col-md-3 col-6">
<span class="oi oi-pulse" title="Status" i18n-title aria-hidden="true"></span>
{{JobLastRunState[lastRun.state]}}
</div>
</div>
<div *ngIf="progress"> <div *ngIf="progress">
<div class="form-group row"> <div class="form-group row">

View File

@ -1,6 +1,7 @@
import {Component, Input, OnChanges, OnDestroy} from '@angular/core'; import {Component, Input, OnChanges, OnDestroy} from '@angular/core';
import {JobProgressDTO, JobState} from '../../../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO, JobState} from '../../../../../../common/entities/job/JobProgressDTO';
import {Subscription, timer} from 'rxjs'; import {Subscription, timer} from 'rxjs';
import {JobLastRunDTO, JobLastRunState} from '../../../../../../common/entities/job/JobLastRunDTO';
@Component({ @Component({
selector: 'app-settings-job-progress', selector: 'app-settings-job-progress',
@ -10,8 +11,10 @@ import {Subscription, timer} from 'rxjs';
export class JobProgressComponent implements OnDestroy, OnChanges { export class JobProgressComponent implements OnDestroy, OnChanges {
@Input() progress: JobProgressDTO; @Input() progress: JobProgressDTO;
@Input() lastRun: JobLastRunDTO;
JobState = JobState; JobState = JobState;
timeCurrentCopy: number; timeCurrentCopy: number;
JobLastRunState = JobLastRunState;
private timerSub: Subscription; private timerSub: Subscription;
constructor() { constructor() {

View File

@ -11,7 +11,7 @@ import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'
import {Utils} from '../../../../../common/Utils'; import {Utils} from '../../../../../common/Utils';
import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO';
import {ErrorDTO} from '../../../../../common/entities/Error'; import {ErrorDTO} from '../../../../../common/entities/Error';
import {JobState} from '../../../../../common/entities/settings/JobProgressDTO'; import {JobState} from '../../../../../common/entities/job/JobProgressDTO';
@Component({ @Component({

View File

@ -1,19 +1,22 @@
import {EventEmitter, Injectable} from '@angular/core'; import {EventEmitter, Injectable} from '@angular/core';
import {BehaviorSubject} from 'rxjs'; import {BehaviorSubject} from 'rxjs';
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO'; import {JobProgressDTO} from '../../../../common/entities/job/JobProgressDTO';
import {NetworkService} from '../../model/network/network.service'; import {NetworkService} from '../../model/network/network.service';
import {JobLastRunDTO} from '../../../../common/entities/job/JobLastRunDTO';
@Injectable() @Injectable()
export class ScheduledJobsService { export class ScheduledJobsService {
public progress: BehaviorSubject<{ [key: string]: JobProgressDTO }>; public progress: BehaviorSubject<{ [key: string]: JobProgressDTO }>;
public lastRuns: BehaviorSubject<{ [key: string]: { [key: string]: JobLastRunDTO } }>;
public onJobFinish: EventEmitter<string> = new EventEmitter<string>(); public onJobFinish: EventEmitter<string> = new EventEmitter<string>();
timer: number = null; timer: number = null;
private subscribers = 0; private subscribers = 0;
constructor(private _networkService: NetworkService) { constructor(private _networkService: NetworkService) {
this.progress = new BehaviorSubject({}); this.progress = new BehaviorSubject({});
this.lastRuns = new BehaviorSubject({});
} }
public calcTimeElapsed(progress: JobProgressDTO) { public calcTimeElapsed(progress: JobProgressDTO) {
@ -53,6 +56,7 @@ export class ScheduledJobsService {
protected async getProgress(): Promise<void> { protected async getProgress(): Promise<void> {
const prevPrg = this.progress.value; const prevPrg = this.progress.value;
this.progress.next(await this._networkService.getJson<{ [key: string]: JobProgressDTO }>('/admin/jobs/scheduled/progress')); this.progress.next(await this._networkService.getJson<{ [key: string]: JobProgressDTO }>('/admin/jobs/scheduled/progress'));
this.lastRuns.next(await this._networkService.getJson<{ [key: string]: { [key: string]: JobLastRunDTO } }>('/admin/jobs/scheduled/lastRun'));
for (const prg in prevPrg) { for (const prg in prevPrg) {
if (!this.progress.value.hasOwnProperty(prg)) { if (!this.progress.value.hasOwnProperty(prg)) {
this.onJobFinish.emit(prg); this.onJobFinish.emit(prg);

View File

@ -10,7 +10,7 @@ import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'
import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO';
import {ErrorDTO} from '../../../../../common/entities/Error'; import {ErrorDTO} from '../../../../../common/entities/Error';
import {ScheduledJobsService} from '../scheduled-jobs.service'; import {ScheduledJobsService} from '../scheduled-jobs.service';
import {JobState} from '../../../../../common/entities/settings/JobProgressDTO'; import {JobState} from '../../../../../common/entities/job/JobProgressDTO';
@Component({ @Component({
selector: 'app-settings-thumbnail', selector: 'app-settings-thumbnail',

View File

@ -10,7 +10,7 @@ import {ScheduledJobsService} from '../scheduled-jobs.service';
import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO';
import {ErrorDTO} from '../../../../../common/entities/Error'; import {ErrorDTO} from '../../../../../common/entities/Error';
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
import { JobState } from '../../../../../common/entities/settings/JobProgressDTO'; import { JobState } from '../../../../../common/entities/job/JobProgressDTO';
@Component({ @Component({