@reekon-tools/boldr-utils 1.4.9 → 1.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formulas/calculateFormula.d.ts +2 -2
- package/dist/formulas/calculateFormula.js +25 -11
- package/dist/formulas/evaluateFormula.d.ts +1 -0
- package/dist/formulas/evaluateFormula.js +22 -10
- package/dist/types/firestore.d.ts +3 -0
- package/dist/utils/micrometersToUnit.d.ts +2 -2
- package/dist/utils/micrometersToUnit.js +69 -60
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Formula, Measurement, Units } from '../types/firestore.js';
|
|
2
|
-
export declare const calculateFormula: (formula: Formula, formulas: Formula[], columns: Record<string,
|
|
1
|
+
import { ColumnConfig, DecimalTolerance, FractionalTolerance, Formula, Measurement, Units } from '../types/firestore.js';
|
|
2
|
+
export declare const calculateFormula: (formula: Formula, formulas: Formula[], columns: Record<string, string>, tableConfig: ColumnConfig[], measurements: Measurement[], unit: Units, fractionalTolerance: FractionalTolerance, decimalTolerance: DecimalTolerance) => string | number | null;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { ColumnType, } from '../types/firestore.js';
|
|
2
|
+
import { evaluateFormula, parseMixedNumber } from './evaluateFormula.js';
|
|
3
|
+
import { convertMicrometers } from '../utils/micrometersToUnit.js';
|
|
4
|
+
export const calculateFormula = (formula, formulas, columns, tableConfig, measurements, unit, fractionalTolerance, decimalTolerance) => {
|
|
3
5
|
// Convert Firestore Map to plain object if necessary
|
|
4
6
|
let variableToColumnMap = {};
|
|
5
7
|
if (formula.variableToColumnMap) {
|
|
@@ -17,17 +19,29 @@ export const calculateFormula = (formula, formulas, columns, measurements, unit)
|
|
|
17
19
|
}
|
|
18
20
|
// Build valueMap from group.columns and measurementMap
|
|
19
21
|
const valueMap = {};
|
|
20
|
-
// Map column IDs to their actual measurement values
|
|
21
22
|
for (const [columnId, measurementId] of Object.entries(columns)) {
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
23
|
+
const column = tableConfig.find((c) => c.id === columnId);
|
|
24
|
+
if (!column)
|
|
25
|
+
continue;
|
|
26
|
+
if (column.type === ColumnType.Measurement) {
|
|
27
|
+
const measurement = measurements.find((m) => m.id === measurementId);
|
|
28
|
+
if (!measurement)
|
|
29
|
+
continue;
|
|
30
|
+
const { value, unit: displayUnit } = convertMicrometers(measurement.value, unit, fractionalTolerance, decimalTolerance);
|
|
31
|
+
valueMap[columnId] = parseMixedNumber(value);
|
|
32
|
+
}
|
|
33
|
+
else if (column.type === ColumnType.Angle) {
|
|
34
|
+
const measurement = measurements.find((m) => m.id === measurementId);
|
|
35
|
+
if (!measurement)
|
|
36
|
+
continue;
|
|
24
37
|
valueMap[columnId] = measurement.value;
|
|
25
38
|
}
|
|
26
|
-
else {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
else if (column.type === ColumnType.ConversionTable) {
|
|
40
|
+
const value = measurementId.split(':')[1];
|
|
41
|
+
valueMap[columnId] = parseFloat(value);
|
|
42
|
+
}
|
|
43
|
+
else if (column.type === ColumnType.Number) {
|
|
44
|
+
valueMap[columnId] = parseFloat(measurementId);
|
|
31
45
|
}
|
|
32
46
|
}
|
|
33
47
|
// Convert formulas to the expected FormulaDefinition format
|
|
@@ -73,7 +87,7 @@ export const calculateFormula = (formula, formulas, columns, measurements, unit)
|
|
|
73
87
|
const result = evaluateFormula({
|
|
74
88
|
expression: formula.expression,
|
|
75
89
|
mappings: currentMappings,
|
|
76
|
-
valueMap,
|
|
90
|
+
valueMap: valueMap,
|
|
77
91
|
unit: unit,
|
|
78
92
|
formulas: formulaDefinitions,
|
|
79
93
|
});
|
|
@@ -17,6 +17,7 @@ export interface FormulaEvaluationOptions {
|
|
|
17
17
|
formulas?: FormulaDefinition[];
|
|
18
18
|
scope?: Record<string, number>;
|
|
19
19
|
}
|
|
20
|
+
export declare function parseMixedNumber(str: string): number;
|
|
20
21
|
export declare function evaluateFormula({ expression, mappings, valueMap, unit, formulas, scope, }: FormulaEvaluationOptions): number | string | null;
|
|
21
22
|
export declare function createFormulaScope({ mappings, valueMap, unit, }: {
|
|
22
23
|
mappings: Record<string, string>;
|
|
@@ -2,6 +2,24 @@ import { Units } from '../types/firestore.js';
|
|
|
2
2
|
import { compile, unit as mathUnit } from 'mathjs';
|
|
3
3
|
// Cache for evaluated formulas to improve performance
|
|
4
4
|
const formulaCache = new Map();
|
|
5
|
+
export function parseMixedNumber(str) {
|
|
6
|
+
const parts = str.trim().split(' ');
|
|
7
|
+
let whole = 0;
|
|
8
|
+
let fraction = 0;
|
|
9
|
+
if (parts.length === 2) {
|
|
10
|
+
whole = parseFloat(parts[0]);
|
|
11
|
+
const [num, den] = parts[1].split('/').map(Number);
|
|
12
|
+
fraction = num / den;
|
|
13
|
+
}
|
|
14
|
+
else if (parts[0].includes('/')) {
|
|
15
|
+
const [num, den] = parts[0].split('/').map(Number);
|
|
16
|
+
fraction = num / den;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
whole = parseFloat(parts[0]);
|
|
20
|
+
}
|
|
21
|
+
return whole + fraction;
|
|
22
|
+
}
|
|
5
23
|
function normalizeUnitForMathJS(unit) {
|
|
6
24
|
switch (unit) {
|
|
7
25
|
case Units.Centimeters:
|
|
@@ -84,7 +102,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
84
102
|
if (micrometers === 0) {
|
|
85
103
|
console.warn(`⚠️ Zero or missing value for mapping ID "${mapping.id}"`);
|
|
86
104
|
}
|
|
87
|
-
const valueInUnit =
|
|
105
|
+
const valueInUnit = micrometers;
|
|
88
106
|
scope[variable] = valueInUnit;
|
|
89
107
|
}
|
|
90
108
|
else if (mapping.type === 'formula') {
|
|
@@ -128,25 +146,20 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
128
146
|
// Legacy createFormulaScope function for backward compatibility
|
|
129
147
|
export function createFormulaScope({ mappings, valueMap, unit, }) {
|
|
130
148
|
const scope = {};
|
|
131
|
-
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
132
149
|
for (const [variable, columnId] of Object.entries(mappings)) {
|
|
133
150
|
const micrometers = valueMap[columnId] ?? 0;
|
|
134
|
-
|
|
135
|
-
const valueInUnit = mathUnit(micrometers, 'um').toNumber(normalizedUnit);
|
|
136
|
-
scope[variable] = valueInUnit;
|
|
151
|
+
scope[variable] = micrometers;
|
|
137
152
|
}
|
|
138
153
|
return scope;
|
|
139
154
|
}
|
|
140
155
|
// Enhanced createFormulaScope for new mapping structure
|
|
141
156
|
export function createEnhancedFormulaScope({ mappings, valueMap, unit, formulas = [], }) {
|
|
142
157
|
const scope = {};
|
|
143
|
-
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
144
158
|
for (const [variable, rawMapping] of Object.entries(mappings)) {
|
|
145
159
|
const mapping = normalizeMapping(rawMapping);
|
|
146
160
|
if (mapping.type === 'measurement') {
|
|
147
161
|
const micrometers = valueMap[mapping.id] ?? 0;
|
|
148
|
-
|
|
149
|
-
scope[variable] = valueInUnit;
|
|
162
|
+
scope[variable] = micrometers;
|
|
150
163
|
}
|
|
151
164
|
else if (mapping.type === 'formula') {
|
|
152
165
|
// Find and evaluate the referenced formula
|
|
@@ -160,8 +173,7 @@ export function createEnhancedFormulaScope({ mappings, valueMap, unit, formulas
|
|
|
160
173
|
formulas,
|
|
161
174
|
});
|
|
162
175
|
if (typeof result === 'number') {
|
|
163
|
-
|
|
164
|
-
scope[variable] = valueInUnit;
|
|
176
|
+
scope[variable] = result;
|
|
165
177
|
}
|
|
166
178
|
else {
|
|
167
179
|
scope[variable] = 0; // Error case
|
|
@@ -152,6 +152,8 @@ export interface Job extends FirestoreDoc, Timestamps {
|
|
|
152
152
|
};
|
|
153
153
|
areaMapFileId?: string;
|
|
154
154
|
type?: JobType;
|
|
155
|
+
address?: string;
|
|
156
|
+
description?: string;
|
|
155
157
|
}
|
|
156
158
|
export interface Section extends Timestamps {
|
|
157
159
|
id: string;
|
|
@@ -187,6 +189,7 @@ export interface Group extends FirestoreDoc, Timestamps {
|
|
|
187
189
|
finish: string;
|
|
188
190
|
finishDate: string;
|
|
189
191
|
};
|
|
192
|
+
path?: string;
|
|
190
193
|
}
|
|
191
194
|
export declare enum ColumnType {
|
|
192
195
|
Text = "text",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DecimalTolerance, FractionalTolerance, Units } from '../types/firestore.js';
|
|
2
|
-
export declare const convertMicrometers: (micrometers: number, unit: Units, fractionalTolerance: FractionalTolerance, decimalTolerance: DecimalTolerance) => {
|
|
2
|
+
export declare const convertMicrometers: (micrometers: number | null | undefined, unit: Units, fractionalTolerance: FractionalTolerance, decimalTolerance: DecimalTolerance) => {
|
|
3
3
|
value: string;
|
|
4
|
-
unit: string
|
|
4
|
+
unit: string;
|
|
5
5
|
};
|
|
@@ -2,75 +2,84 @@ import { Units, convertUnitsToReadable, } from '../types/firestore.js';
|
|
|
2
2
|
import { create, all } from 'mathjs';
|
|
3
3
|
const math = create(all);
|
|
4
4
|
export const convertMicrometers = (micrometers, unit, fractionalTolerance, decimalTolerance) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
if (micrometers == null || isNaN(micrometers)) {
|
|
6
|
+
return { value: 'NaN', unit: '' };
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const converted = math.unit(micrometers, 'um');
|
|
10
|
+
let value = 0;
|
|
11
|
+
let displayUnit = '';
|
|
12
|
+
switch (unit) {
|
|
13
|
+
case Units.Inches:
|
|
14
|
+
case Units.FractionalInches: {
|
|
15
|
+
const inches = converted.toNumber('in');
|
|
16
|
+
displayUnit = 'in';
|
|
17
|
+
if (unit === Units.FractionalInches) {
|
|
18
|
+
const denominator = parseInt(fractionalTolerance, 10);
|
|
19
|
+
const whole = Math.floor(inches);
|
|
20
|
+
const fractional = inches - whole;
|
|
21
|
+
const numerator = Math.round(fractional * denominator);
|
|
22
|
+
if (numerator === denominator) {
|
|
23
|
+
value = `${whole + 1}`;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
value =
|
|
27
|
+
numerator === 0
|
|
28
|
+
? `${whole}`
|
|
29
|
+
: `${whole} ${numerator}/${denominator}`;
|
|
30
|
+
}
|
|
20
31
|
}
|
|
21
32
|
else {
|
|
22
|
-
value =
|
|
23
|
-
numerator === 0
|
|
24
|
-
? `${whole}`
|
|
25
|
-
: `${whole} ${numerator}/${denominator}`;
|
|
33
|
+
value = inches.toFixed(decimalTolerance.length - 2);
|
|
26
34
|
}
|
|
35
|
+
break;
|
|
27
36
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
case Units.Feet:
|
|
38
|
+
case Units.FeetInchesFractional:
|
|
39
|
+
case Units.FeetInchesDecimal: {
|
|
40
|
+
const feet = converted.toNumber('ft');
|
|
41
|
+
const wholeFeet = Math.floor(feet);
|
|
42
|
+
const fractionalFeet = feet - wholeFeet;
|
|
43
|
+
displayUnit = 'ft';
|
|
44
|
+
if (unit === Units.FeetInchesFractional) {
|
|
45
|
+
const inches = fractionalFeet * 12;
|
|
46
|
+
const wholeInches = Math.floor(inches);
|
|
47
|
+
const fractional = inches - wholeInches;
|
|
48
|
+
const denominator = parseInt(fractionalTolerance, 10);
|
|
49
|
+
const numerator = Math.round(fractional * denominator);
|
|
50
|
+
if (numerator === denominator) {
|
|
51
|
+
value = `${wholeFeet}' ${wholeInches + 1}"`;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
value =
|
|
55
|
+
numerator === 0
|
|
56
|
+
? `${wholeFeet}' ${wholeInches}"`
|
|
57
|
+
: `${wholeFeet}' ${wholeInches} ${numerator}/${denominator}"`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (unit === Units.FeetInchesDecimal) {
|
|
61
|
+
const inches = (fractionalFeet * 12).toFixed(decimalTolerance.length - 2);
|
|
62
|
+
value = `${wholeFeet}' ${inches}"`;
|
|
48
63
|
}
|
|
49
64
|
else {
|
|
50
|
-
value =
|
|
51
|
-
numerator === 0
|
|
52
|
-
? `${wholeFeet}' ${wholeInches}"`
|
|
53
|
-
: `${wholeFeet}' ${wholeInches} ${numerator}/${denominator}"`;
|
|
65
|
+
value = feet.toFixed(decimalTolerance.length - 2);
|
|
54
66
|
}
|
|
67
|
+
break;
|
|
55
68
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
case Units.Meters:
|
|
70
|
+
case Units.Centimeters:
|
|
71
|
+
case Units.Millimeters: {
|
|
72
|
+
displayUnit = convertUnitsToReadable(unit) ?? '';
|
|
73
|
+
value = converted.toNumber(unit).toFixed(decimalTolerance.length - 2);
|
|
74
|
+
break;
|
|
59
75
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
case Units.Meters:
|
|
66
|
-
case Units.Centimeters:
|
|
67
|
-
case Units.Millimeters: {
|
|
68
|
-
displayUnit = convertUnitsToReadable(unit);
|
|
69
|
-
value = converted.toNumber(unit).toFixed(decimalTolerance.length - 2);
|
|
70
|
-
break;
|
|
76
|
+
default:
|
|
77
|
+
throw new Error('Unsupported unit');
|
|
71
78
|
}
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
return { value, unit: displayUnit };
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.warn('Error converting micrometers:', err);
|
|
83
|
+
return { value: 'NaN', unit: '' };
|
|
74
84
|
}
|
|
75
|
-
return { value, unit: displayUnit };
|
|
76
85
|
};
|