Browse Source

UI: Refactoring convert unit

pull/13282/head
Vladyslav_Prykhodko 1 year ago
parent
commit
82ced843d3
  1. 429
      ui-ngx/src/app/core/services/unit/converter-unit.ts
  2. 17
      ui-ngx/src/app/shared/models/unit.models.ts

429
ui-ngx/src/app/core/services/unit/converter-unit.ts

@ -15,46 +15,20 @@
/// ///
import { import {
Conversion,
TbMeasure, TbMeasure,
TbUnitConvertor, TbUnitConvertor,
Unit, Unit,
UnitCache,
UnitDescription, UnitDescription,
UnitDescriptionGroupByMeasure, UnitDescriptionGroupByMeasure,
UnitSystem UnitSystem
} from '@shared/models/unit.models'; } from '@shared/models/unit.models';
import { AllMeasures } from '@core/services/unit/definitions/all'; import { AllMeasures } from '@core/services/unit/definitions/all';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils';
export interface Conversion<
TMeasures extends string,
TUnits extends string,
> {
abbr: TUnits;
measure: TMeasures;
system: UnitSystem;
unit: Unit;
}
// export interface BestResult<TUnits extends string> {
// val: number;
// unit: TUnits;
// name: string;
// tags: string[];
// }
type Entries<T, S extends keyof T> = [S, T[keyof T]]; type Entries<T, S extends keyof T> = [S, T[keyof T]];
export type UnitCache<TMeasures, TUnits> = Map<
string,
{
system: UnitSystem;
measure: TMeasures;
unit: Unit;
abbr: TUnits;
}
>;
export class Converter< export class Converter<
TMeasures extends AllMeasures, TMeasures extends AllMeasures,
TUnits extends string, TUnits extends string,
@ -78,77 +52,40 @@ export class Converter<
this.unitCache = unitCache; this.unitCache = unitCache;
} }
convertor(from: TUnits | (string & {}), to: TUnits | (string & {})): TbUnitConvertor{ convertor(from: TUnits | string, to: TUnits | string): TbUnitConvertor {
const origin = this.getUnit(from); return (value: number) => this.convert(value, from, to);
if (origin === null) {
throw Error(`Unsupported unit ${from}`);
}
const destination = this.getUnit(to);
if (destination === null) {
throw Error(`Unsupported unit ${from}`);
}
if (origin.abbr === destination.abbr) {
return (value: number) => value;
}
if (destination.measure !== origin.measure) {
throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`);
}
return (value: number): number => {
let result = value * origin.unit.to_anchor;
if (origin.unit.anchor_shift) {
result -= origin.unit.anchor_shift;
}
if (origin.system !== destination.system) {
const measure = this.measureData[origin.measure];
const transform = measure[origin.system]?.transform;
const ratio = measure[origin.system]?.ratio;
if (typeof transform === 'function') {
result = transform(result);
} else if (typeof ratio === 'number') {
result *= ratio;
} else {
throw Error('A system anchor needs to either have a defined ratio number or a transform function.');
}
}
if (destination.unit.anchor_shift) {
result += destination.unit.anchor_shift;
}
return result / destination.unit.to_anchor;
};
} }
convert(value: number, from: TUnits | (string & {}), to: TUnits | (string & {})): number { convert(value: number, from: TUnits | string, to: TUnits | string): number {
const origin = this.getUnit(from); const origin = this.getUnit(from);
if (origin === null) {
throw Error(`Unsupported unit ${from}`);
}
const destination = this.getUnit(to); const destination = this.getUnit(to);
if (destination === null) {
throw Error(`Unsupported unit ${from}`); if (!origin) {
throw new Error(`Unsupported unit: ${from}`);
}
if (!destination) {
throw new Error(`Unsupported unit: ${to}`);
} }
if (origin.abbr === destination.abbr) { if (origin.abbr === destination.abbr) {
return value; return value;
} }
if (destination.measure !== origin.measure) { if (destination.measure !== origin.measure) {
throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); throw Error(`Cannot convert incompatible measures: ${origin.measure} to ${destination.measure}`);
} }
let result = value * origin.unit.to_anchor; let result = value * origin.unit.to_anchor;
if (origin.unit.anchor_shift) { if (origin.unit.anchor_shift) {
result -= origin.unit.anchor_shift; result -= origin.unit.anchor_shift;
} }
if (origin.system !== destination.system) { if (origin.system !== destination.system) {
const measure = this.measureData[origin.measure]; const measureUnits = this.measureData[origin.measure][origin.system];
const transform = measure[origin.system]?.transform; const transform = measureUnits?.transform;
const ratio = measure[origin.system]?.ratio; const ratio = measureUnits?.ratio;
if (typeof transform === 'function') { if (typeof transform === 'function') {
result = transform(result); result = transform(result);
} else if (typeof ratio === 'number') { } else if (typeof ratio === 'number') {
result *= ratio; result *= ratio;
} else { } else {
throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); throw Error('System anchor requires a defined ratio or transform function');
} }
} }
@ -162,26 +99,16 @@ export class Converter<
if (!this.isMeasure(measureName)) { if (!this.isMeasure(measureName)) {
return null; return null;
} }
const measure = this.measureData[measureName]; const units = this.getUnitsForMeasure(measureName, unitSystem);
let currentUnitSystem = unitSystem; if (!units) {
let units = measure[currentUnitSystem].units; return null;
if (isUndefinedOrNull(units)) {
if (currentUnitSystem === UnitSystem.IMPERIAL) {
currentUnitSystem = UnitSystem.METRIC;
units = measure[currentUnitSystem].units;
}
if (!units) {
console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`);
return null;
}
} }
for (const [abbr, unit] of Object.entries( for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) {
units as Partial<Record<TUnits, Unit>> if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) {
) as [TUnits, Unit][]) {
if (unit.to_anchor === 1 && (isUndefinedOrNull(unit.anchor_shift) || unit.anchor_shift === 0)) {
return abbr; return abbr;
} }
} }
return null;
} }
getUnit(abbr: TUnits | (string & {})): Conversion<TMeasures, TUnits> | null { getUnit(abbr: TUnits | (string & {})): Conversion<TMeasures, TUnits> | null {
@ -189,271 +116,113 @@ export class Converter<
} }
describe(abbr: TUnits | (string & {})): UnitDescription { describe(abbr: TUnits | (string & {})): UnitDescription {
const result = this.getUnit(abbr); const unit = this.getUnit(abbr);
return unit ? this.describeUnit(unit) : null;
if (result != null) {
return this.describeUnit(result);
}
return null;
} }
private describeUnit(unit: Conversion<TMeasures, TUnits>): UnitDescription { list(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescription[] {
return { const results: UnitDescription[] = [];
abbr: unit.abbr,
measure: unit.measure,
system: unit.system,
name: unit.unit.name,
tags: unit.unit.tags
};
}
list(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescription[] | never { const measures = measureName
const list = []; ? { [measureName]: this.measureData[measureName] } as Record<TMeasures, TbMeasure<TUnits>>
: this.measureData;
if (isDefinedAndNotNull(measureName)) { for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure<TUnits>][]) {
if (!this.isMeasure(measureName)) { if (!this.isMeasure(name)) {
console.log(`Measure "${measureName}" not found.`); continue;
return list;
} }
const measure = this.measureData[measureName];
if (isDefinedAndNotNull(unitSystem)) { const systems = unitSystem
let currentUnitSystem = unitSystem; ? [unitSystem]
let units = measure[currentUnitSystem]; : (Object.keys(measure) as UnitSystem[]);
if (isUndefinedOrNull(units)) {
if (currentUnitSystem === UnitSystem.IMPERIAL) { for (const system of systems) {
currentUnitSystem = UnitSystem.METRIC; const units = this.getUnitsForMeasure(name, system);
units = measure[currentUnitSystem]; if (!units) {
} continue;
if (!units) {
console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`);
return list;
}
} }
for (const [abbr, unit] of Object.entries(
units.units for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) {
)) { results.push(
list.push(
this.describeUnit({ this.describeUnit({
abbr: abbr as TUnits, abbr,
measure: measureName as TMeasures, measure: name as TMeasures,
system: currentUnitSystem, system,
unit: unit as Unit, unit,
}) })
); );
} }
} else {
for (const [systemName, units] of Object.entries(
measure
) as Entries<TbMeasure<TUnits>, UnitSystem>[]) {
for (const [abbr, unit] of Object.entries(
units.units as Partial<Record<TUnits, Unit>>
)) {
list.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: measureName as TMeasures,
system: systemName,
unit: unit as Unit,
})
);
}
}
}
} else {
for (const [name, measure] of Object.entries(this.measureData)) {
if (isDefinedAndNotNull(unitSystem)) {
let currentUnitSystem = unitSystem;
let units = (measure as TbMeasure<TUnits>)[currentUnitSystem]?.units;
if (isUndefinedOrNull(units)) {
if (currentUnitSystem === UnitSystem.IMPERIAL) {
currentUnitSystem = UnitSystem.METRIC;
units = (measure as TbMeasure<TUnits>)[currentUnitSystem]?.units;
}
if (!units) {
console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`);
continue;
}
}
for (const [abbr, unit] of Object.entries(
units as Partial<Record<TUnits, Unit>>
)) {
list.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: name as TMeasures,
system: currentUnitSystem,
unit: unit as Unit,
})
);
}
} else {
for (const [systemName, units] of Object.entries(
measure
) as Entries<TbMeasure<TUnits>, UnitSystem>[]) {
for (const [abbr, unit] of Object.entries(
units.units as Partial<Record<TUnits, Unit>>
)) {
list.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: name as TMeasures,
system: systemName,
unit: unit as Unit,
})
);
}
}
}
} }
} }
return results;
return list;
} }
listGroupByMeasure(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure<TMeasures> | never { listGroupByMeasure(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure<TMeasures> | never {
const list: UnitDescriptionGroupByMeasure<TMeasures> = {}; const results: UnitDescriptionGroupByMeasure<TMeasures> = {};
if (isDefinedAndNotNull(measureName)) { const measures = measureName
if (!this.isMeasure(measureName)) { ? { [measureName]: this.measureData[measureName]} as Record<TMeasures, TbMeasure<TUnits>>
console.log(`Measure "${measureName}" not found.`); : this.measureData;
return list;
for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure<TUnits>][]) {
if (!this.isMeasure(name)) {
continue;
} }
const measure = this.measureData[measureName];
if (isDefinedAndNotNull(unitSystem)) { results[name] = [];
let currentUnitSystem = unitSystem;
let units = measure[currentUnitSystem]; const systems = unitSystem
if (isUndefinedOrNull(units)) { ? [unitSystem]
if (currentUnitSystem === UnitSystem.IMPERIAL) { : (Object.keys(measure) as UnitSystem[]);
currentUnitSystem = UnitSystem.METRIC;
units = measure[currentUnitSystem]; for (const system of systems) {
} const units = this.getUnitsForMeasure(name, system);
if (!units) { if (!units) {
console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); continue;
return list;
}
} }
list[measureName] = [];
const unitsDescription = list[measureName]; for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) {
for (const [abbr, unit] of Object.entries( results[name].push(
units.units
)) {
unitsDescription.push(
this.describeUnit({ this.describeUnit({
abbr: abbr as TUnits, abbr,
measure: measureName as TMeasures, measure: name as TMeasures,
system: currentUnitSystem, system,
unit: unit as Unit, unit,
}) })
); );
} }
} else {
for (const [systemName, units] of Object.entries(
measure
) as Entries<TbMeasure<TUnits>, UnitSystem>[]) {
list[measureName] = [];
const unitsDescription = list[measureName];
for (const [abbr, unit] of Object.entries(
units.units as Partial<Record<TUnits, Unit>>
)) {
unitsDescription.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: measureName as TMeasures,
system: systemName,
unit: unit as Unit,
})
);
}
}
}
} else {
for (const [name, measure] of Object.entries(this.measureData)) {
if (isDefinedAndNotNull(unitSystem)) {
let currentUnitSystem = unitSystem;
let units = (measure as TbMeasure<TUnits>)[currentUnitSystem]?.units;
if (isUndefinedOrNull(units)) {
if (currentUnitSystem === UnitSystem.IMPERIAL) {
currentUnitSystem = UnitSystem.METRIC;
units = (measure as TbMeasure<TUnits>)[currentUnitSystem]?.units;
}
if (!units) {
console.log(`Measure "${name}" in ${currentUnitSystem} system is not found.`);
continue;
}
}
list[name] = [];
const unitsDescription = list[name];
for (const [abbr, unit] of Object.entries(
units as Partial<Record<TUnits, Unit>>
)) {
unitsDescription.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: name as TMeasures,
system: currentUnitSystem,
unit: unit as Unit,
})
);
}
} else {
list[name] = [];
const unitsDescription = list[name];
for (const [systemName, units] of Object.entries(
measure
) as Entries<TbMeasure<TUnits>, UnitSystem>[]) {
for (const [abbr, unit] of Object.entries(
units.units as Partial<Record<TUnits, Unit>>
)) {
unitsDescription.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: name as TMeasures,
system: systemName,
unit: unit as Unit,
})
);
}
}
}
} }
} }
return results;
}
return list; private describeUnit(unit: Conversion<TMeasures, TUnits>): UnitDescription {
return {
abbr: unit.abbr,
measure: unit.measure,
system: unit.system,
name: unit.unit.name,
tags: unit.unit.tags
};
} }
private isMeasure(measureName: string): measureName is TMeasures { private isMeasure(measureName: string): measureName is TMeasures {
return measureName in this.measureData; return measureName in this.measureData;
} }
// possibilities(forMeasure?: TMeasures | (string & {})): TUnits[] { private getUnitsForMeasure(
// let possibilities: TUnits[] = []; measureName: TMeasures,
// let list_measures: TMeasures[] = []; unitSystem: UnitSystem
// ): Partial<Record<TUnits, Unit>> | null {
// if (typeof forMeasure == 'string' && this.isMeasure(forMeasure)) { const measure = this.measureData[measureName];
// list_measures.push(forMeasure); let system = unitSystem;
// } else if (this.origin != null) { let units = measure[system]?.units;
// list_measures.push(this.origin.measure); if (!units && unitSystem === UnitSystem.IMPERIAL) {
// } else { system = UnitSystem.METRIC;
// list_measures = Object.keys(this.measureData) as TMeasures[]; units = measure[system]?.units;
// } }
// return units ?? null;
// for (const measure of list_measures) { }
// const systems = this.measureData[measure].systems;
//
// for (const system of Object.values(systems)) {
// possibilities = [
// ...possibilities,
// ...(Object.keys(system as Record<TUnits, Unit>) as TUnits[]),
// ];
// }
// }
//
// return possibilities;
// }
// measures(): TMeasures[] {
// return Object.keys(this.measureData) as TMeasures[];
// }
} }
export function buildUnitCache< export function buildUnitCache<

17
ui-ngx/src/app/shared/models/unit.models.ts

@ -63,6 +63,23 @@ export interface TbMeasureUnits<TUnits extends string> {
units?: Partial<Record<TUnits, Unit>>; units?: Partial<Record<TUnits, Unit>>;
} }
export interface Conversion<TMeasures extends string, TUnits extends string> {
abbr: TUnits;
measure: TMeasures;
system: UnitSystem;
unit: Unit;
}
export type UnitCache<TMeasures, TUnits> = Map<
string,
{
system: UnitSystem;
measure: TMeasures;
unit: Unit;
abbr: TUnits;
}
>;
const searchUnitTags = (unit: UnitDescription, searchText: string): boolean => const searchUnitTags = (unit: UnitDescription, searchText: string): boolean =>
!!unit.tags.find(t => t.toUpperCase().includes(searchText)); !!unit.tags.find(t => t.toUpperCase().includes(searchText));

Loading…
Cancel
Save