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.
*/
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[] = [];
directoryQueue: string[] = [];
fileQueue: string[] = [];

View File

@ -1,20 +1,16 @@
import { Logger } from '../../../Logger';
import { IJob } from './IJob';
import {
ConfigTemplateEntry,
JobDTO,
JobDTOUtils,
} from '../../../../common/entities/job/JobDTO';
import { JobProgress } from './JobProgress';
import { IJobListener } from './IJobListener';
import { JobProgressStates } from '../../../../common/entities/job/JobProgressDTO';
import {Logger} from '../../../Logger';
import {IJob} from './IJob';
import {ConfigTemplateEntry, JobDTO, JobDTOUtils,} 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 global: any;
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;
protected progress: JobProgress = null;
protected config: T;
@ -57,7 +53,15 @@ export abstract class Job<T = void> implements IJob<T> {
);
this.soloRun = soloRun;
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.Name,
JobDTOUtils.getHashName(this.Name, this.config)

View File

@ -8,8 +8,8 @@ import {FileDTO} from '../../../../common/entities/FileDTO';
import {backendTexts} from '../../../../common/BackendTexts';
export class ThumbnailGenerationJob extends FileJob<{
sizes: number[];
indexedOnly: boolean;
sizes?: number[];
indexedOnly?: boolean;
}> {
public readonly Name = DefaultsJobs[DefaultsJobs['Thumbnail Generation']];
@ -29,16 +29,13 @@ export class ThumbnailGenerationJob extends FileJob<{
}
start(
config: { sizes: number[]; indexedOnly: boolean },
config: { sizes?: number[]; indexedOnly?: boolean },
soloRun = false,
allowParallelRun = false
): Promise<void> {
if (!config.sizes || !Array.isArray(config.sizes) || config.sizes.length === 0) {
throw new Error(
'unknown thumbnails sizes: ' +
config.sizes +
'. It should be an array from:' + Config.Media.Thumbnail.thumbnailSizes
);
if (!config || !config.sizes || !Array.isArray(config.sizes) || config.sizes.length === 0) {
config = config || {};
config.sizes = this.ConfigTemplate.find(ct => ct.id == 'sizes').defaultValue;
}
for (const item of config.sizes) {
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 {NetworkService} from '../../model/network/network.service';
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 {NotificationService} from '../../model/notification.service';
@ -12,6 +12,7 @@ export class ScheduledJobsService {
public progress: BehaviorSubject<Record<string, JobProgressDTO>>;
public onJobFinish: EventEmitter<string> = new EventEmitter<string>();
timer: number = null;
public availableJobs: BehaviorSubject<JobDTO[]>;
public jobStartingStopping: { [key: string]: boolean } = {};
private subscribers = 0;
@ -21,6 +22,35 @@ export class ScheduledJobsService {
private backendTextService: BackendtextService
) {
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 {

View File

@ -7,7 +7,7 @@ import {WebConfigClassBuilder} from 'typeconfig/src/decorators/builders/WebConfi
import {ConfigPriority, TAGS} from '../../../../common/config/public/ClientConfig';
import {CookieNames} from '../../../../common/CookieNames';
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 {ScheduledJobsService} from './scheduled-jobs.service';
import {IWebConfigClassPrivate} from '../../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass';
@ -17,14 +17,12 @@ export class SettingsService {
public configPriority = ConfigPriority.basic;
public settings: BehaviorSubject<IWebConfigClassPrivate<TAGS> & WebConfig>;
private fetchingSettings = false;
public availableJobs: BehaviorSubject<JobDTO[]>;
public statistic: BehaviorSubject<StatisticDTO>;
constructor(private networkService: NetworkService,
private jobsService: ScheduledJobsService,
private cookieService: CookieService) {
this.statistic = new BehaviorSubject(null);
this.availableJobs = new BehaviorSubject([]);
this.settings = new BehaviorSubject<IWebConfigClassPrivate<TAGS> & WebConfig>(WebConfigClassBuilder.attachPrivateInterface(new WebConfig()));
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> {
if (this.fetchingSettings === true) {
return;

View File

@ -23,10 +23,8 @@
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
<ng-container *ngIf="states.value.enabled !== false">
<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>
@ -35,6 +33,11 @@
{{Name}} <span i18n>config is not supported with these settings.</span>
</div>
<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">
<button class="btn btn-success float-end"
[disabled]="settingsForm.form.invalid || !changed || inProgress"
@ -46,11 +49,26 @@
</button>
</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>
</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">
{{rStates.description}}
</div>
@ -78,8 +96,18 @@
</ng-container>
</ng-container>
</ng-container>
<div *ngIf="rStates.tags?.uiJob">
<ng-container *ngFor="let job of rStates.tags?.uiJob; let i = index">
<div *ngIf="rStates.tags?.uiJob && !skipJobs">
<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">
{{job.description}}
</div>
@ -93,13 +121,17 @@
[jobName]="job.job"></app-settings-job-button>
</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
*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"/>
<app-settings-job-progress
class="d-block mb-2"
[progress]="getProgress(job.job)"></app-settings-job-progress>
[progress]="getProgress(job)"></app-settings-job-progress>
</ng-container>
</ng-container>
</div>

View File

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

View File

@ -146,9 +146,9 @@
</div>
<ng-container *ngIf="getConfigTemplate(schedule.jobName) ">
<ng-container *ngIf="jobsService.getConfigTemplate(schedule.jobName) ">
<hr/>
<div *ngFor="let configEntry of getConfigTemplate(schedule.jobName)">
<div *ngFor="let configEntry of jobsService.getConfigTemplate(schedule.jobName)">
<div class="mb-3 row">
<label class="col-md-2 control-label"
@ -235,7 +235,7 @@
(change)="jobTypeChanged(newSchedule)"
[(ngModel)]="newSchedule.jobName"
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)}}
</option>
</select>

View File

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