1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-23 01:27:14 +02:00

Fixing job running in settings #569

This commit is contained in:
Patrik J. Braun 2023-01-05 19:50:12 +01:00
parent 689bee2fcf
commit 36b0a216d6
10 changed files with 136 additions and 76 deletions

View File

@ -21,7 +21,7 @@ const LOG_TAG = '[FileJob]';
/** /**
* Abstract class for thumbnail creation, file deleting etc. * Abstract class for thumbnail creation, file deleting etc.
*/ */
export abstract class FileJob<S extends { indexedOnly: boolean } = { indexedOnly: boolean }> extends Job<S> { export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnly?: boolean }> extends Job<S> {
public readonly ConfigTemplate: ConfigTemplateEntry[] = []; public readonly ConfigTemplate: ConfigTemplateEntry[] = [];
directoryQueue: string[] = []; directoryQueue: string[] = [];
fileQueue: string[] = []; fileQueue: string[] = [];

View File

@ -1,20 +1,16 @@
import { Logger } from '../../../Logger'; import {Logger} from '../../../Logger';
import { IJob } from './IJob'; import {IJob} from './IJob';
import { import {ConfigTemplateEntry, JobDTO, JobDTOUtils,} from '../../../../common/entities/job/JobDTO';
ConfigTemplateEntry, import {JobProgress} from './JobProgress';
JobDTO, import {IJobListener} from './IJobListener';
JobDTOUtils, import {JobProgressStates} from '../../../../common/entities/job/JobProgressDTO';
} from '../../../../common/entities/job/JobDTO';
import { JobProgress } from './JobProgress';
import { IJobListener } from './IJobListener';
import { JobProgressStates } from '../../../../common/entities/job/JobProgressDTO';
declare const process: any; declare const process: any;
declare const global: any; declare const global: any;
const LOG_TAG = '[JOB]'; const LOG_TAG = '[JOB]';
export abstract class Job<T = void> implements IJob<T> { export abstract class Job<T extends Record<string, any> = Record<string, any>> implements IJob<T> {
public allowParallelRun: boolean = null; public allowParallelRun: boolean = null;
protected progress: JobProgress = null; protected progress: JobProgress = null;
protected config: T; protected config: T;
@ -57,7 +53,15 @@ export abstract class Job<T = void> implements IJob<T> {
); );
this.soloRun = soloRun; this.soloRun = soloRun;
this.allowParallelRun = allowParallelRun; this.allowParallelRun = allowParallelRun;
this.config = config; this.config = {} as T;
if (this.ConfigTemplate) {
this.ConfigTemplate.forEach(ct => (this.config as any)[ct.id] = ct.defaultValue);
}
if (config) {
for (const key of Object.keys(config)) {
(this.config as any)[key] = config[key];
}
}
this.progress = new JobProgress( this.progress = new JobProgress(
this.Name, this.Name,
JobDTOUtils.getHashName(this.Name, this.config) JobDTOUtils.getHashName(this.Name, this.config)

View File

@ -8,8 +8,8 @@ import {FileDTO} from '../../../../common/entities/FileDTO';
import {backendTexts} from '../../../../common/BackendTexts'; import {backendTexts} from '../../../../common/BackendTexts';
export class ThumbnailGenerationJob extends FileJob<{ export class ThumbnailGenerationJob extends FileJob<{
sizes: number[]; sizes?: number[];
indexedOnly: boolean; indexedOnly?: boolean;
}> { }> {
public readonly Name = DefaultsJobs[DefaultsJobs['Thumbnail Generation']]; public readonly Name = DefaultsJobs[DefaultsJobs['Thumbnail Generation']];
@ -29,16 +29,13 @@ export class ThumbnailGenerationJob extends FileJob<{
} }
start( start(
config: { sizes: number[]; indexedOnly: boolean }, config: { sizes?: number[]; indexedOnly?: boolean },
soloRun = false, soloRun = false,
allowParallelRun = false allowParallelRun = false
): Promise<void> { ): Promise<void> {
if (!config.sizes || !Array.isArray(config.sizes) || config.sizes.length === 0) { if (!config || !config.sizes || !Array.isArray(config.sizes) || config.sizes.length === 0) {
throw new Error( config = config || {};
'unknown thumbnails sizes: ' + config.sizes = this.ConfigTemplate.find(ct => ct.id == 'sizes').defaultValue;
config.sizes +
'. It should be an array from:' + Config.Media.Thumbnail.thumbnailSizes
);
} }
for (const item of config.sizes) { for (const item of config.sizes) {
if (Config.Media.Thumbnail.thumbnailSizes.indexOf(item) === -1) { if (Config.Media.Thumbnail.thumbnailSizes.indexOf(item) === -1) {

View File

@ -3,7 +3,7 @@ import {BehaviorSubject} from 'rxjs';
import {JobProgressDTO, JobProgressStates,} from '../../../../common/entities/job/JobProgressDTO'; import {JobProgressDTO, JobProgressStates,} from '../../../../common/entities/job/JobProgressDTO';
import {NetworkService} from '../../model/network/network.service'; import {NetworkService} from '../../model/network/network.service';
import {JobScheduleDTO} from '../../../../common/entities/job/JobScheduleDTO'; import {JobScheduleDTO} from '../../../../common/entities/job/JobScheduleDTO';
import {JobDTOUtils} from '../../../../common/entities/job/JobDTO'; import {ConfigTemplateEntry, JobDTO, JobDTOUtils} from '../../../../common/entities/job/JobDTO';
import {BackendtextService} from '../../model/backendtext.service'; import {BackendtextService} from '../../model/backendtext.service';
import {NotificationService} from '../../model/notification.service'; import {NotificationService} from '../../model/notification.service';
@ -12,6 +12,7 @@ export class ScheduledJobsService {
public progress: BehaviorSubject<Record<string, JobProgressDTO>>; public progress: BehaviorSubject<Record<string, JobProgressDTO>>;
public onJobFinish: EventEmitter<string> = new EventEmitter<string>(); public onJobFinish: EventEmitter<string> = new EventEmitter<string>();
timer: number = null; timer: number = null;
public availableJobs: BehaviorSubject<JobDTO[]>;
public jobStartingStopping: { [key: string]: boolean } = {}; public jobStartingStopping: { [key: string]: boolean } = {};
private subscribers = 0; private subscribers = 0;
@ -21,6 +22,35 @@ export class ScheduledJobsService {
private backendTextService: BackendtextService private backendTextService: BackendtextService
) { ) {
this.progress = new BehaviorSubject({}); this.progress = new BehaviorSubject({});
this.availableJobs = new BehaviorSubject([]);
}
public async getAvailableJobs(): Promise<void> {
this.availableJobs.next(
await this.networkService.getJson<JobDTO[]>('/admin/jobs/available')
);
}
public getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
const job = this.availableJobs.value.find(
(t) => t.Name === JobName
);
if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) {
return job.ConfigTemplate;
}
return null;
}
public getDefaultConfig(jobName: string): Record<string, unknown> {
const ct = this.getConfigTemplate(jobName);
if (!ct) {
return null;
}
const config = {} as Record<string, unknown>;
ct.forEach(c => config[c.id] = c.defaultValue);
return config;
} }
getProgress(schedule: JobScheduleDTO): JobProgressDTO { getProgress(schedule: JobScheduleDTO): JobProgressDTO {

View File

@ -7,7 +7,7 @@ import {WebConfigClassBuilder} from 'typeconfig/src/decorators/builders/WebConfi
import {ConfigPriority, TAGS} from '../../../../common/config/public/ClientConfig'; import {ConfigPriority, TAGS} from '../../../../common/config/public/ClientConfig';
import {CookieNames} from '../../../../common/CookieNames'; import {CookieNames} from '../../../../common/CookieNames';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import {DefaultsJobs, JobDTO} from '../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
import {StatisticDTO} from '../../../../common/entities/settings/StatisticDTO'; import {StatisticDTO} from '../../../../common/entities/settings/StatisticDTO';
import {ScheduledJobsService} from './scheduled-jobs.service'; import {ScheduledJobsService} from './scheduled-jobs.service';
import {IWebConfigClassPrivate} from '../../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass'; import {IWebConfigClassPrivate} from '../../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass';
@ -17,14 +17,12 @@ export class SettingsService {
public configPriority = ConfigPriority.basic; public configPriority = ConfigPriority.basic;
public settings: BehaviorSubject<IWebConfigClassPrivate<TAGS> & WebConfig>; public settings: BehaviorSubject<IWebConfigClassPrivate<TAGS> & WebConfig>;
private fetchingSettings = false; private fetchingSettings = false;
public availableJobs: BehaviorSubject<JobDTO[]>;
public statistic: BehaviorSubject<StatisticDTO>; public statistic: BehaviorSubject<StatisticDTO>;
constructor(private networkService: NetworkService, constructor(private networkService: NetworkService,
private jobsService: ScheduledJobsService, private jobsService: ScheduledJobsService,
private cookieService: CookieService) { private cookieService: CookieService) {
this.statistic = new BehaviorSubject(null); this.statistic = new BehaviorSubject(null);
this.availableJobs = new BehaviorSubject([]);
this.settings = new BehaviorSubject<IWebConfigClassPrivate<TAGS> & WebConfig>(WebConfigClassBuilder.attachPrivateInterface(new WebConfig())); this.settings = new BehaviorSubject<IWebConfigClassPrivate<TAGS> & WebConfig>(WebConfigClassBuilder.attachPrivateInterface(new WebConfig()));
this.getSettings().catch(console.error); this.getSettings().catch(console.error);
@ -48,12 +46,6 @@ export class SettingsService {
} }
public async getAvailableJobs(): Promise<void> {
this.availableJobs.next(
await this.networkService.getJson<JobDTO[]>('/admin/jobs/available')
);
}
public async getSettings(): Promise<void> { public async getSettings(): Promise<void> {
if (this.fetchingSettings === true) { if (this.fetchingSettings === true) {
return; return;

View File

@ -23,10 +23,8 @@
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> <div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
<ng-container *ngIf="states.value.enabled !== false"> <ng-container *ngIf="states.value.enabled !== false">
<ng-container <ng-container
*ngTemplateOutlet="Recursion; context:{ rStates: states,skipEnabled:true,confPath:ConfigPath }" *ngTemplateOutlet="Recursion; context:{ rStates: states,skipEnabled:true,confPath:ConfigPath,skipJobs:true }"
></ng-container> ></ng-container>
</ng-container> </ng-container>
@ -35,6 +33,11 @@
{{Name}} <span i18n>config is not supported with these settings.</span> {{Name}} <span i18n>config is not supported with these settings.</span>
</div> </div>
<div class="row"> <div class="row">
<div class="col" *ngIf="states.tags?.uiJob && !states.tags?.uiJob[0].description">
<ng-container
*ngTemplateOutlet="JobTemplate; context:{ uiJob: states.tags?.uiJob }"
></ng-container>
</div>
<div class="col"> <div class="col">
<button class="btn btn-success float-end" <button class="btn btn-success float-end"
[disabled]="settingsForm.form.invalid || !changed || inProgress" [disabled]="settingsForm.form.invalid || !changed || inProgress"
@ -46,11 +49,26 @@
</button> </button>
</div> </div>
</div> </div>
<div *ngIf="states.tags?.uiJob && !states.tags?.uiJob[0].description">
<ng-container
*ngTemplateOutlet="JobProcessTemplate; context:{ uiJob: states.tags?.uiJob }"
></ng-container>
</div>
<div *ngIf="states.tags?.uiJob && states.tags?.uiJob[0].description">
<hr/>
<ng-container
*ngTemplateOutlet="JobTemplate; context:{ uiJob: states.tags?.uiJob }"
></ng-container>
<ng-container
*ngTemplateOutlet="JobProcessTemplate; context:{ uiJob: states.tags?.uiJob }"
></ng-container>
</div>
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>
</div> </div>
<ng-template #Recursion let-rStates="rStates" let-skipEnabled="skipEnabled" let-confPath="confPath"> <ng-template #Recursion let-rStates="rStates" let-skipEnabled="skipEnabled" let-skipJobs="skipJobs"
let-confPath="confPath">
<div class="alert alert-secondary" role="alert" *ngIf="rStates.description"> <div class="alert alert-secondary" role="alert" *ngIf="rStates.description">
{{rStates.description}} {{rStates.description}}
</div> </div>
@ -78,8 +96,18 @@
</ng-container> </ng-container>
</ng-container> </ng-container>
</ng-container> </ng-container>
<div *ngIf="rStates.tags?.uiJob"> <div *ngIf="rStates.tags?.uiJob && !skipJobs">
<ng-container *ngFor="let job of rStates.tags?.uiJob; let i = index"> <ng-container
*ngTemplateOutlet="JobTemplate; context:{ uiJob: rStates.tags?.uiJob }"
></ng-container>
<ng-container
*ngTemplateOutlet="JobProcessTemplate; context:{ uiJob: rStates.tags?.uiJob }"
></ng-container>
</div>
</ng-template>
<ng-template #JobTemplate let-uiJob="uiJob">
<div class="mb-2">
<ng-container *ngFor="let job of uiJob; let i = index">
<div class="alert alert-secondary" role="alert" *ngIf="job.description"> <div class="alert alert-secondary" role="alert" *ngIf="job.description">
{{job.description}} {{job.description}}
</div> </div>
@ -93,13 +121,17 @@
[jobName]="job.job"></app-settings-job-button> [jobName]="job.job"></app-settings-job-button>
</ng-container> </ng-container>
<ng-container *ngFor="let job of rStates.tags?.uiJob"> </div>
</ng-template>
<ng-template #JobProcessTemplate let-uiJob="uiJob">
<div>
<ng-container *ngFor="let job of uiJob">
<ng-container <ng-container
*ngIf="getProgress(job.job) && !job.hideProgress && (!job.relevant || job.relevant(settingsService.settings | async))"> *ngIf="getProgress(job) && !job.hideProgress && (!job.relevant || job.relevant(settingsService.settings | async))">
<hr class="mt-1"/> <hr class="mt-1"/>
<app-settings-job-progress <app-settings-job-progress
class="d-block mb-2" class="d-block mb-2"
[progress]="getProgress(job.job)"></app-settings-job-progress> [progress]="getProgress(job)"></app-settings-job-progress>
</ng-container> </ng-container>
</ng-container> </ng-container>
</div> </div>

View File

@ -300,7 +300,10 @@ export class TemplateComponent implements OnInit, OnChanges, OnDestroy, ISetting
return states.keys; return states.keys;
} }
getProgress(jobName: string): JobProgressDTO { getProgress(uiJob: { job: string, config?: any }): JobProgressDTO {
return this.jobsService.progress.value[JobDTOUtils.getHashName(jobName)]; if (!uiJob.config) {
uiJob.config = this.jobsService.getDefaultConfig(uiJob.job);
}
return this.jobsService.progress.value[JobDTOUtils.getHashName(uiJob.job, uiJob.config || {})];
} }
} }

View File

@ -1,13 +1,10 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import {Component, EventEmitter, Input, Output} from '@angular/core';
import { import {JobProgressDTO, JobProgressStates,} from '../../../../../../common/entities/job/JobProgressDTO';
JobProgressDTO, import {ErrorDTO} from '../../../../../../common/entities/Error';
JobProgressStates, import {ScheduledJobsService} from '../../scheduled-jobs.service';
} from '../../../../../../common/entities/job/JobProgressDTO'; import {NotificationService} from '../../../../model/notification.service';
import { ErrorDTO } from '../../../../../../common/entities/Error'; import {JobDTOUtils} from '../../../../../../common/entities/job/JobDTO';
import { ScheduledJobsService } from '../../scheduled-jobs.service'; import {BackendtextService} from '../../../../model/backendtext.service';
import { NotificationService } from '../../../../model/notification.service';
import { JobDTOUtils } from '../../../../../../common/entities/job/JobDTO';
import { BackendtextService } from '../../../../model/backendtext.service';
@Component({ @Component({
selector: 'app-settings-job-button', selector: 'app-settings-job-button',
@ -16,7 +13,7 @@ import { BackendtextService } from '../../../../model/backendtext.service';
}) })
export class JobButtonComponent { export class JobButtonComponent {
@Input() jobName: string; @Input() jobName: string;
@Input() config: any = {}; @Input() config: any;
@Input() shortName = false; @Input() shortName = false;
@Input() disabled = false; @Input() disabled = false;
@Input() soloRun = false; @Input() soloRun = false;
@ -29,7 +26,18 @@ export class JobButtonComponent {
private notification: NotificationService, private notification: NotificationService,
public jobsService: ScheduledJobsService, public jobsService: ScheduledJobsService,
public backendTextService: BackendtextService public backendTextService: BackendtextService
) {} ) {
}
private populateConfig() {
if (this.config) {
return;
}
const c = this.jobsService.getDefaultConfig(this.jobName); // can return with null
if (c) {
this.config = c;
}
}
public get Running(): boolean { public get Running(): boolean {
return ( return (
@ -40,13 +48,15 @@ export class JobButtonComponent {
} }
get Progress(): JobProgressDTO { get Progress(): JobProgressDTO {
this.populateConfig();
return this.jobsService.progress.value[ return this.jobsService.progress.value[
JobDTOUtils.getHashName(this.jobName, this.config) JobDTOUtils.getHashName(this.jobName, this.config)
]; ];
} }
public async start(): Promise<boolean> { public async start(): Promise<boolean> {
this.jobError.emit(''); this.jobError.emit('');
this.populateConfig();
try { try {
await this.jobsService.start( await this.jobsService.start(
this.jobName, this.jobName,
@ -56,8 +66,8 @@ export class JobButtonComponent {
); );
this.notification.success( this.notification.success(
$localize`Job started` + $localize`Job started` +
': ' + ': ' +
this.backendTextService.getJobName(this.jobName) this.backendTextService.getJobName(this.jobName)
); );
return true; return true;
} catch (err) { } catch (err) {
@ -76,8 +86,8 @@ export class JobButtonComponent {
await this.jobsService.stop(this.jobName); await this.jobsService.stop(this.jobName);
this.notification.info( this.notification.info(
$localize`Stopping job` + $localize`Stopping job` +
': ' + ': ' +
this.backendTextService.getJobName(this.jobName) this.backendTextService.getJobName(this.jobName)
); );
return true; return true;
} catch (err) { } catch (err) {

View File

@ -146,9 +146,9 @@
</div> </div>
<ng-container *ngIf="getConfigTemplate(schedule.jobName) "> <ng-container *ngIf="jobsService.getConfigTemplate(schedule.jobName) ">
<hr/> <hr/>
<div *ngFor="let configEntry of getConfigTemplate(schedule.jobName)"> <div *ngFor="let configEntry of jobsService.getConfigTemplate(schedule.jobName)">
<div class="mb-3 row"> <div class="mb-3 row">
<label class="col-md-2 control-label" <label class="col-md-2 control-label"
@ -235,7 +235,7 @@
(change)="jobTypeChanged(newSchedule)" (change)="jobTypeChanged(newSchedule)"
[(ngModel)]="newSchedule.jobName" [(ngModel)]="newSchedule.jobName"
name="newJobName" required> name="newJobName" required>
<option *ngFor="let availableJob of settingsService.availableJobs | async" <option *ngFor="let availableJob of jobsService.availableJobs | async"
[ngValue]="availableJob.Name">{{backendTextService.getJobName(availableJob.Name)}} [ngValue]="availableJob.Name">{{backendTextService.getJobName(availableJob.Name)}}
</option> </option>
</select> </select>

View File

@ -104,19 +104,11 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
return d; return d;
} }
getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
const job = this.settingsService.availableJobs.value.find(
(t) => t.Name === JobName
);
if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) {
return job.ConfigTemplate;
}
return null;
}
ngOnInit(): void { ngOnInit(): void {
this.jobsService.subscribeToProgress(); this.jobsService.subscribeToProgress();
this.settingsService.getAvailableJobs().catch(console.error); this.jobsService.getAvailableJobs().catch(console.error);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -131,7 +123,7 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
} }
jobTypeChanged(schedule: JobScheduleDTO): void { jobTypeChanged(schedule: JobScheduleDTO): void {
const job = this.settingsService.availableJobs.value.find( const job = this.jobsService.availableJobs.value.find(
(t) => t.Name === schedule.jobName (t) => t.Name === schedule.jobName
); );
schedule.config = schedule.config || {}; schedule.config = schedule.config || {};
@ -205,13 +197,13 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
} }
prepareNewJob(): void { prepareNewJob(): void {
const jobName = this.settingsService.availableJobs.value[0].Name; const jobName = this.jobsService.availableJobs.value[0].Name;
this.newSchedule = new JobScheduleConfig('new job', this.newSchedule = new JobScheduleConfig('new job',
jobName, jobName,
new NeverJobTriggerConfig()); new NeverJobTriggerConfig());
// setup job specific config // setup job specific config
const job = this.settingsService.availableJobs.value.find( const job = this.jobsService.availableJobs.value.find(
(t) => t.Name === jobName (t) => t.Name === jobName
); );
this.newSchedule.config = this.newSchedule.config || {}; this.newSchedule.config = this.newSchedule.config || {};