diff --git a/ui-ngx/src/app/core/services/unit/converter-unit.ts b/ui-ngx/src/app/core/services/unit/converter-unit.ts index 8a6d707ed1..a97951db15 100644 --- a/ui-ngx/src/app/core/services/unit/converter-unit.ts +++ b/ui-ngx/src/app/core/services/unit/converter-unit.ts @@ -15,46 +15,20 @@ /// import { + Conversion, TbMeasure, TbUnitConvertor, Unit, + UnitCache, UnitDescription, UnitDescriptionGroupByMeasure, UnitSystem } from '@shared/models/unit.models'; import { AllMeasures } from '@core/services/unit/definitions/all'; 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 { -// val: number; -// unit: TUnits; -// name: string; -// tags: string[]; -// } type Entries = [S, T[keyof T]]; -export type UnitCache = Map< - string, - { - system: UnitSystem; - measure: TMeasures; - unit: Unit; - abbr: TUnits; - } ->; - export class Converter< TMeasures extends AllMeasures, TUnits extends string, @@ -78,77 +52,40 @@ export class Converter< this.unitCache = unitCache; } - convertor(from: TUnits | (string & {}), to: TUnits | (string & {})): TbUnitConvertor{ - const origin = this.getUnit(from); - 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; - }; + convertor(from: TUnits | string, to: TUnits | string): TbUnitConvertor { + return (value: number) => this.convert(value, from, to); } - 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); - if (origin === null) { - throw Error(`Unsupported unit ${from}`); - } 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) { return value; } 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; 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; + const measureUnits = this.measureData[origin.measure][origin.system]; + const transform = measureUnits?.transform; + const ratio = measureUnits?.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.'); + throw Error('System anchor requires a defined ratio or transform function'); } } @@ -162,26 +99,16 @@ export class Converter< if (!this.isMeasure(measureName)) { return null; } - const measure = this.measureData[measureName]; - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem].units; - 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; - } + const units = this.getUnitsForMeasure(measureName, unitSystem); + if (!units) { + return null; } - for (const [abbr, unit] of Object.entries( - units as Partial> - ) as [TUnits, Unit][]) { - if (unit.to_anchor === 1 && (isUndefinedOrNull(unit.anchor_shift) || unit.anchor_shift === 0)) { + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) { return abbr; } } + return null; } getUnit(abbr: TUnits | (string & {})): Conversion | null { @@ -189,271 +116,113 @@ export class Converter< } describe(abbr: TUnits | (string & {})): UnitDescription { - const result = this.getUnit(abbr); - - if (result != null) { - return this.describeUnit(result); - } - return null; + const unit = this.getUnit(abbr); + return unit ? this.describeUnit(unit) : null; } - private describeUnit(unit: Conversion): UnitDescription { - return { - abbr: unit.abbr, - measure: unit.measure, - system: unit.system, - name: unit.unit.name, - tags: unit.unit.tags - }; - } + list(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescription[] { + const results: UnitDescription[] = []; - list(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescription[] | never { - const list = []; + const measures = measureName + ? { [measureName]: this.measureData[measureName] } as Record> + : this.measureData; - if (isDefinedAndNotNull(measureName)) { - if (!this.isMeasure(measureName)) { - console.log(`Measure "${measureName}" not found.`); - return list; + for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; } - const measure = this.measureData[measureName]; - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem]; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem]; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return list; - } + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; } - for (const [abbr, unit] of Object.entries( - units.units - )) { - list.push( + + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + results.push( this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, + abbr, + measure: name as TMeasures, + system, + unit, }) ); } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - 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)[currentUnitSystem]?.units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = (measure as TbMeasure)[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> - )) { - 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, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - list.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } } } - - return list; + return results; } - listGroupByMeasure(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { - const list: UnitDescriptionGroupByMeasure = {}; + listGroupByMeasure(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { + const results: UnitDescriptionGroupByMeasure = {}; - if (isDefinedAndNotNull(measureName)) { - if (!this.isMeasure(measureName)) { - console.log(`Measure "${measureName}" not found.`); - return list; + const measures = measureName + ? { [measureName]: this.measureData[measureName]} as Record> + : this.measureData; + + for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; } - const measure = this.measureData[measureName]; - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem]; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem]; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return list; - } + + results[name] = []; + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; } - list[measureName] = []; - const unitsDescription = list[measureName]; - for (const [abbr, unit] of Object.entries( - units.units - )) { - unitsDescription.push( + + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + results[name].push( this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, + abbr, + measure: name as TMeasures, + system, + unit, }) ); } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - list[measureName] = []; - const unitsDescription = list[measureName]; - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - 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)[currentUnitSystem]?.units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = (measure as TbMeasure)[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> - )) { - 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, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - 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): 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 { return measureName in this.measureData; } - // possibilities(forMeasure?: TMeasures | (string & {})): TUnits[] { - // let possibilities: TUnits[] = []; - // let list_measures: TMeasures[] = []; - // - // if (typeof forMeasure == 'string' && this.isMeasure(forMeasure)) { - // list_measures.push(forMeasure); - // } else if (this.origin != null) { - // list_measures.push(this.origin.measure); - // } else { - // list_measures = Object.keys(this.measureData) as TMeasures[]; - // } - // - // 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) as TUnits[]), - // ]; - // } - // } - // - // return possibilities; - // } - - // measures(): TMeasures[] { - // return Object.keys(this.measureData) as TMeasures[]; - // } + private getUnitsForMeasure( + measureName: TMeasures, + unitSystem: UnitSystem + ): Partial> | null { + const measure = this.measureData[measureName]; + let system = unitSystem; + let units = measure[system]?.units; + if (!units && unitSystem === UnitSystem.IMPERIAL) { + system = UnitSystem.METRIC; + units = measure[system]?.units; + } + return units ?? null; + } } export function buildUnitCache< diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 8a62b2d9d7..01f88c3406 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -63,6 +63,23 @@ export interface TbMeasureUnits { units?: Partial>; } +export interface Conversion { + abbr: TUnits; + measure: TMeasures; + system: UnitSystem; + unit: Unit; +} + +export type UnitCache = Map< + string, + { + system: UnitSystem; + measure: TMeasures; + unit: Unit; + abbr: TUnits; + } +>; + const searchUnitTags = (unit: UnitDescription, searchText: string): boolean => !!unit.tags.find(t => t.toUpperCase().includes(searchText));