src/app/services/classifier/classifier.service.ts
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2018 Georgi Marinov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { Injectable } from '@angular/core';
import { AccomplishmentClassifiableService } from '../accomplishment-classifiable/accomplishment-classifiable.service';
import { Accomplishment } from '../../interfaces/cv/accomplishment';
import { ClassifierKind } from '../../enums/classifier-kind.enum';
import { OntologyEntry } from '../../interfaces/ontology-entry/ontology-entry';
import { UiService } from '../../services/ui/ui.service';
import { PersistenceService } from '../../services/persistence/persistence.service';
import { OntologyService } from '../ontology/ontology.service';
import { Go } from '../../enums/go.enum';
/**
* Classifier connection service.
* ~extends {@link AccomplishmentClassifiableService}
*/
@Injectable({
providedIn: 'root'
})
export class ClassifierService extends AccomplishmentClassifiableService {
/** ClassifierKind values getter. */
public get ClassifierKindValues() {
return Object.values(ClassifierKind).filter((_) => !isNaN(Number(_))) as ClassifierKind[];
}
/** Subservice. */
public get subService() {
return {
/** Classifier kind. */
classifierKind: this.classifierKind,
/** Defaults. */
defaultKind: ClassifierKind.Strong,
/** Prefix for fully qualified names, getter. */
get prefix() { return 'classifier'; },
/** Fully qualified name, used to prefix other identifiers, getter. */
get full() { return this.prefix + ClassifierKind[this.classifierKind]; },
/** Persistence name getter. */
get sFull() { return this.full + 's'; },
/** Index name getter. */
get indexFull() { return this.full + 'Index'; },
/** Next direction getter. */
get nextDirection() { return ['ᐸᐸ', 'ᐸ', 'ᐳ']; },
};
}
/**
* Constructs the Classifier service.
* ~constructor
*
* @param uiService The ui service injected dependency.
* @param persistenceService The persistence service injected dependency.
* @param ontologyService The ontology service injected dependency.
*/
constructor(
public readonly uiService: UiService,
public override readonly persistenceService: PersistenceService,
public readonly ontologyService: OntologyService,
) {
super(persistenceService);
}
/**
* Whether accomplishment is of type specified.
*
* @param accomplishment The accomplishment to test.
* @param accomplishmentType The accomplishment type to test.
*
* @returns whether accomplishment is of the type specified.
*/
protected override isOfType(accomplishment: Accomplishment, accomplishmentType: string) {
if ([
ClassifierKind.Direct,
ClassifierKind.Classic
].includes(this.classifierKind)) {
return super.isOfType(accomplishment, accomplishmentType);
}
return this.isOfOntologyType(accomplishment, accomplishmentType);
}
/**
* Whether accomplishment is of ontology type specified.
*
* @param accomplishment The accomplishment to test.
* @param accomplishmentType The accomplishment ontology type to test.
*
* @returns whether accomplishment is of the ontology type specified.
*/
private isOfOntologyType(accomplishment: Accomplishment, accomplishmentType: string) {
const ontology = this.ontologyService.ontology;
const ontologyEntry = ontology.find((_) => _.Entity === accomplishment.Platform) as OntologyEntry;
switch (this.classifierKind) {
case ClassifierKind.Explicit: return accomplishmentType === accomplishment.Platform;
case ClassifierKind.Strong: return ontologyEntry?.category === accomplishmentType;
case ClassifierKind.Medium: return ontologyEntry?.path.includes(accomplishmentType);
case ClassifierKind.Weak: return ontologyEntry?.displayPath.includes(accomplishmentType);
default: return false;
}
}
/** Floored division modulo operation getter. */
private clamp(n: number, m: number) {
if (m === 0) { return n; }
const d = n % m;
return d < 0 ? d + m : d;
}
/** Next potential field. */
private nextPotentialField(classifierKind: number, classifierKindNext = Go.Forward) {
if (classifierKindNext === Go.Home) {
classifierKind = this.subService.defaultKind;
} else {
const nudgedPotentialField = this.nudgePotentialField(classifierKind, classifierKindNext);
classifierKind = nudgedPotentialField.classifierKind;
}
classifierKind = this.clamp(classifierKind, this.ClassifierKindValues.length);
return { classifierKind };
}
/** Nudge potential field to next adjacent one, raw. */
private nudgePotentialField(classifierKind: number, classifierKindNext = Go.Forward) {
if (classifierKindNext === Go.Back) {
classifierKind--;
} else if (classifierKindNext === Go.Forward) {
classifierKind++;
}
return { classifierKind };
}
/** Next. */
public next(event: MouseEvent, classifierKindNext = Go.Forward) {
const nextPotentialField = this.nextPotentialField(this.classifierKind, classifierKindNext);
this.classifierKind = nextPotentialField.classifierKind;
event.stopPropagation();
}
/** Next title. */
public nextTitle(classifierKindNext = Go.Forward) {
const nextPotentialField = this.nextPotentialField(this.classifierKind, classifierKindNext);
const tokens = [
this.uiService.uiText('Click here to change classification method from'),
ClassifierKind[this.classifierKind].toLowerCase(),
this.uiService.uiText('to'),
ClassifierKind[nextPotentialField.classifierKind].toLowerCase(),
];
return tokens.join(' ');
}
}