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

Add info panel to jobs #587

This commit is contained in:
Patrik J. Braun 2023-09-19 21:10:33 +02:00
parent 71c9df2d26
commit 07328a7ae2
6 changed files with 123 additions and 60 deletions

View File

@ -142,6 +142,7 @@ import {
ionHammerOutline,
ionImageOutline,
ionImagesOutline,
ionInformationCircleOutline,
ionInformationOutline,
ionLinkOutline,
ionLocationOutline,
@ -204,7 +205,7 @@ export class MyHammerConfig extends HammerGestureConfig {
export class CustomUrlSerializer implements UrlSerializer {
private defaultUrlSerializer: DefaultUrlSerializer =
new DefaultUrlSerializer();
new DefaultUrlSerializer();
parse(url: string): UrlTree {
// Encode parentheses
@ -215,9 +216,9 @@ export class CustomUrlSerializer implements UrlSerializer {
serialize(tree: UrlTree): string {
return this.defaultUrlSerializer
.serialize(tree)
.replace(/%28/g, '(')
.replace(/%29/g, ')');
.serialize(tree)
.replace(/%28/g, '(')
.replace(/%29/g, ')');
}
}
@ -245,6 +246,7 @@ Marker.prototype.options.icon = MarkerFactory.defIcon;
ionTextOutline, ionFolderOutline, ionDocumentOutline, ionImageOutline,
ionPricetagOutline, ionLocationOutline,
ionSunnyOutline, ionMoonOutline, ionVideocamOutline,
ionInformationCircleOutline,
ionInformationOutline, ionContractOutline, ionExpandOutline, ionCloseOutline,
ionTimerOutline,
ionPlayOutline, ionPauseOutline, ionVolumeMediumOutline, ionVolumeMuteOutline,

View File

@ -1,6 +1,8 @@
import {inject, TestBed} from '@angular/core/testing';
import {BackendtextService} from './backendtext.service';
import {backendTexts} from '../../../common/BackendTexts';
import {Utils} from '../../../common/Utils';
import {DefaultsJobs} from '../../../common/entities/job/JobDTO';
describe('BackendTextService', () => {
beforeEach(() => {
@ -10,18 +12,31 @@ describe('BackendTextService', () => {
});
it('should have valid text for all keys', inject(
[BackendtextService],
(backendTextService: BackendtextService) => {
const getTexts = (obj: any) => {
for (const key of Object.keys(obj)) {
if (typeof obj[key] === 'object') {
getTexts(obj[key]);
continue;
}
expect(backendTextService.get(obj[key])).not.toEqual(null, 'Error for key: ' + obj[key] + ', ' + key);
[BackendtextService],
(backendTextService: BackendtextService) => {
const getTexts = (obj: any) => {
for (const key of Object.keys(obj)) {
if (typeof obj[key] === 'object') {
getTexts(obj[key]);
continue;
}
};
getTexts(backendTexts);
expect(backendTextService.get(obj[key])).not.toEqual(null, 'Error for key: ' + obj[key] + ', ' + key);
}
};
getTexts(backendTexts);
}
));
it('should have valid text for all jobs', inject(
[BackendtextService],
(backendTextService: BackendtextService) => {
const allJobs = Utils.enumToArray(DefaultsJobs);
for (let i = 0; i < allJobs.length; ++i){
expect(backendTextService.getJobName(allJobs[i].value)).not.toEqual(null, 'Cant find job name: ' + allJobs[i].value);
expect(backendTextService.getJobDescription(allJobs[i].value)).not.toEqual(null, 'Cant find job name: ' + allJobs[i].value);
}
}
));
});

View File

@ -60,13 +60,51 @@ export class BackendtextService {
case DefaultsJobs['Temp Folder Cleaning']:
return $localize`Temp folder cleaning`;
case DefaultsJobs['Album Cover Filling']:
return $localize`Album cover filling`;
return $localize`Cover filling`;
case DefaultsJobs['Album Cover Reset']:
return $localize`Album cover reset`;
return $localize`Cover reset`;
case DefaultsJobs['GPX Compression']:
return $localize`GPX compression`;
case DefaultsJobs['Delete Compressed GPX']:
return $localize`Delete Compressed GPX`;
case DefaultsJobs['Top Pick Sending']:
return $localize`Top Pick Sending`;
default:
return DefaultsJobs[job as DefaultsJobs];
return null;
}
}
public getJobDescription(job: DefaultsJobs | string): string {
if (typeof job === 'string') {
job = DefaultsJobs[job as any];
}
switch (job as DefaultsJobs) {
case DefaultsJobs.Indexing:
return $localize`Scans the whole gallery from disk and indexes it to the DB.`;
case DefaultsJobs['Gallery Reset']:
return $localize`Deletes all directories, photos and videos from the DB.`;
case DefaultsJobs['Album Reset']:
return $localize`Removes all albums from the DB`;
case DefaultsJobs['Thumbnail Generation']:
return $localize`Generates thumbnails from all media files and stores them in the tmp folder.`;
case DefaultsJobs['Photo Converting']:
return $localize`Generates high res photos from all media files and stores them in the tmp folder.`;
case DefaultsJobs['Video Converting']:
return $localize`Transcodes all videos and stores them in the tmp folder.`;
case DefaultsJobs['Temp Folder Cleaning']:
return $localize`Removes unnecessary files from the tmp folder.`;
case DefaultsJobs['Album Cover Filling']:
return $localize`Updates the cover photo of all albums (both directories and saved searches) and and faces.`;
case DefaultsJobs['Album Cover Reset']:
return $localize`Deletes the cover photo of all albums and faces`;
case DefaultsJobs['GPX Compression']:
return $localize`Compresses all gpx files`;
case DefaultsJobs['Delete Compressed GPX']:
return $localize`Deletes all compressed GPX files`;
case DefaultsJobs['Top Pick Sending']:
return $localize`Gets the top photos of the selected search queries and sends them over email. You need to set up the SMTP server connection send e-mails.`;
default:
return null;
}
}
}

View File

@ -72,7 +72,7 @@
let-confPath="confPath">
<div class="alert alert-secondary" role="alert"
*ngIf="rStates.description && settingsService.configStyle == ConfigStyle.full">
{{rStates.description}}
<ng-icon size="1.3em" name="ionInformationCircleOutline"></ng-icon> {{rStates.description}}
<a *ngIf="rStates.tags?.githubIssue"
[href]="'https://github.com/bpatrik/pigallery2/issues/'+rStates.tags?.githubIssue">
<ng-container i18n>See</ng-container>
@ -130,7 +130,7 @@
<ng-container *ngFor="let job of uiJob; let i = index">
<div class="alert alert-secondary" role="alert"
*ngIf="job.description && settingsService.configStyle == ConfigStyle.full">
{{job.description}}
<ng-icon size="1.3em" name="ionInformationCircleOutline"></ng-icon> {{job.description}}
</div>
<app-settings-job-button
*ngIf="!job.relevant || job.relevant(settingsService.settings | async)"

View File

@ -40,6 +40,10 @@
<div class="row">
<div class="col-md-12">
<div class="alert alert-secondary" role="alert"
*ngIf="settingsService.configStyle == ConfigStyle.full">
<ng-icon size="1.3em" name="ionInformationCircleOutline"></ng-icon> {{getJobDescription(schedule.jobName)}}
</div>
<div class="mb-1 row">
<label class="col-md-2 control-label" i18n>Job:</label>
<div class="col-md-4">

View File

@ -70,9 +70,9 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
error: string;
constructor(
public settingsService: SettingsService,
public jobsService: ScheduledJobsService,
public backendTextService: BackendtextService,
public settingsService: SettingsService,
public jobsService: ScheduledJobsService,
public backendTextService: BackendtextService,
) {
this.JobTriggerTypeMap = [
{key: JobTriggerType.after, value: $localize`after`},
@ -116,27 +116,27 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
remove(schedule: JobScheduleDTO): void {
this.schedules.splice(
this.schedules.indexOf(schedule),
1
this.schedules.indexOf(schedule),
1
);
}
jobTypeChanged(schedule: JobScheduleDTO): void {
const job = this.jobsService.availableJobs.value.find(
(t) => t.Name === schedule.jobName
(t) => t.Name === schedule.jobName
);
schedule.config = schedule.config || {};
if (job.ConfigTemplate) {
job.ConfigTemplate.forEach(
(ct) => (schedule.config[ct.id] = ct.defaultValue)
(ct) => (schedule.config[ct.id] = ct.defaultValue)
);
}
}
jobTriggerTypeChanged(
triggerType: JobTriggerType,
schedule: JobScheduleDTO
triggerType: JobTriggerType,
schedule: JobScheduleDTO
): void {
switch (triggerType) {
case JobTriggerType.never:
@ -166,7 +166,7 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
value = value.replace(new RegExp(',', 'g'), ';');
value = value.replace(new RegExp(' ', 'g'), ';');
configElement[id] = value
.split(';').filter((i: string) => i != '');
.split(';').filter((i: string) => i != '');
}
getArray(configElement: Record<string, number[]>, id: string): string {
@ -177,46 +177,46 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
value = value.replace(new RegExp(',', 'g'), ';');
value = value.replace(new RegExp(' ', 'g'), ';');
configElement[id] = value
.split(';')
.map((s: string) => parseInt(s, 10))
.filter((i: number) => !isNaN(i) && i > 0);
.split(';')
.map((s: string) => parseInt(s, 10))
.filter((i: number) => !isNaN(i) && i > 0);
}
public shouldIdent(curr: JobScheduleDTO, prev: JobScheduleDTO): boolean {
return (
curr &&
curr.trigger.type === JobTriggerType.after &&
prev &&
prev.name === curr.trigger.afterScheduleName
curr &&
curr.trigger.type === JobTriggerType.after &&
prev &&
prev.name === curr.trigger.afterScheduleName
);
}
public sortedSchedules(): JobScheduleDTO[] {
return (this.schedules || [])
.slice()
.sort((a: JobScheduleDTO, b: JobScheduleDTO) => {
return (
this.getNextRunningDate(a, this.schedules) -
this.getNextRunningDate(b, this.schedules)
);
});
.slice()
.sort((a: JobScheduleDTO, b: JobScheduleDTO) => {
return (
this.getNextRunningDate(a, this.schedules) -
this.getNextRunningDate(b, this.schedules)
);
});
}
prepareNewJob(): void {
const jobName = this.jobsService.availableJobs.value[0].Name;
this.newSchedule = new JobScheduleConfig('new job',
jobName,
new NeverJobTriggerConfig());
jobName,
new NeverJobTriggerConfig());
// setup job specific config
const job = this.jobsService.availableJobs.value.find(
(t) => t.Name === jobName
(t) => t.Name === jobName
);
this.newSchedule.config = this.newSchedule.config || {};
if (job.ConfigTemplate) {
job.ConfigTemplate.forEach(
(ct) => (this.newSchedule.config[ct.id] = ct.defaultValue)
(ct) => (this.newSchedule.config[ct.id] = ct.defaultValue)
);
}
this.jobModalQL.first.show();
@ -226,12 +226,12 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
// make unique job name
const jobName = this.newSchedule.jobName;
const count = this.schedules.filter(
(s: JobScheduleDTO) => s.jobName === jobName
(s: JobScheduleDTO) => s.jobName === jobName
).length;
this.newSchedule.name =
count === 0
? jobName
: this.backendTextService.getJobName(jobName) + ' ' + (count + 1);
count === 0
? jobName
: this.backendTextService.getJobName(jobName) + ' ' + (count + 1);
this.schedules.push(this.newSchedule);
this.jobModalQL.first.hide();
@ -243,24 +243,24 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
}
private getNextRunningDate(
sch: JobScheduleDTO,
list: JobScheduleDTO[],
depth = 0
sch: JobScheduleDTO,
list: JobScheduleDTO[],
depth = 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
list
.map((s) => s.name)
.sort()
.indexOf(sch.name) * -1
);
}
if (sch.trigger.type === JobTriggerType.after) {
const parent = list.find(
(s) => s.name === (sch.trigger as AfterJobTrigger).afterScheduleName
(s) => s.name === (sch.trigger as AfterJobTrigger).afterScheduleName
);
if (parent) {
return this.getNextRunningDate(parent, list, depth + 1) + 0.001;
@ -282,6 +282,10 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
// empty
};
getJobDescription(jobName: string): string {
return this.backendTextService.getJobDescription(jobName);
}
public writeValue(obj: JobScheduleConfig[]): void {
this.schedules = obj;
}