diff --git a/www/__tests__/metricsHelper.test.ts b/www/__tests__/metricsHelper.test.ts index 07f1495b1..96f192fa4 100644 --- a/www/__tests__/metricsHelper.test.ts +++ b/www/__tests__/metricsHelper.test.ts @@ -8,13 +8,8 @@ import { secondsToHours, secondsToMinutes, segmentDaysByWeeks, - metricToValue, tsForDayOfMetricData, valueForFieldOnDay, - generateSummaryFromData, - isCustomLabels, - isAllCustom, - isOnFoot, getUnitUtilsForMetric, } from '../js/metrics/metricsHelper'; import { DayOfMetricData } from '../js/metrics/metricsTypes'; @@ -95,34 +90,6 @@ describe('metricsHelper', () => { }); }); - describe('metricToValue', () => { - const metric = { - walking: 10, - nUsers: 5, - }; - it('returns correct value for user population', () => { - const result = metricToValue('user', metric, 'walking'); - expect(result).toBe(10); - }); - - it('returns correct value for aggregate population', () => { - const result = metricToValue('aggregate', metric, 'walking'); - expect(result).toBe(2); - }); - }); - - describe('isOnFoot', () => { - it('returns true for on foot mode', () => { - const result = isOnFoot('WALKING'); - expect(result).toBe(true); - }); - - it('returns false for non on foot mode', () => { - const result = isOnFoot('DRIVING'); - expect(result).toBe(false); - }); - }); - describe('calculatePercentChange', () => { it('calculates percent change correctly for low and high values', () => { const pastWeekRange = { low: 10, high: 30 }; @@ -169,132 +136,6 @@ describe('metricsHelper', () => { }); }); - describe('generateSummaryFromData', () => { - const modeMap = [ - { - key: 'mode1', - values: [ - ['value1', 10], - ['value2', 20], - ], - }, - { - key: 'mode2', - values: [ - ['value3', 30], - ['value4', 40], - ], - }, - ]; - it('returns summary with sum for non-speed metric', () => { - const metric = 'some_metric'; - const expectedResult = [ - { key: 'mode1', values: 30 }, - { key: 'mode2', values: 70 }, - ]; - const result = generateSummaryFromData(modeMap, metric); - expect(result).toEqual(expectedResult); - }); - - it('returns summary with average for speed metric', () => { - const metric = 'mean_speed'; - const expectedResult = [ - { key: 'mode1', values: 15 }, - { key: 'mode2', values: 35 }, - ]; - const result = generateSummaryFromData(modeMap, metric); - expect(result).toEqual(expectedResult); - }); - }); - - describe('isCustomLabels', () => { - it('returns true for all custom labels', () => { - const modeMap = [ - { - key: 'label_mode1', - values: [ - ['value1', 10], - ['value2', 20], - ], - }, - { - key: 'label_mode2', - values: [ - ['value3', 30], - ['value4', 40], - ], - }, - ]; - const result = isCustomLabels(modeMap); - expect(result).toBe(true); - }); - - it('returns true for all sensed labels', () => { - const modeMap = [ - { - key: 'label_mode1', - values: [ - ['value1', 10], - ['value2', 20], - ], - }, - { - key: 'label_mode2', - values: [ - ['value3', 30], - ['value4', 40], - ], - }, - ]; - const result = isCustomLabels(modeMap); - expect(result).toBe(true); - }); - - it('returns false for mixed custom and sensed labels', () => { - const modeMap = [ - { - key: 'label_mode1', - values: [ - ['value1', 10], - ['value2', 20], - ], - }, - { - key: 'MODE2', - values: [ - ['value3', 30], - ['value4', 40], - ], - }, - ]; - const result = isCustomLabels(modeMap); - expect(result).toBe(false); - }); - }); - - describe('isAllCustom', () => { - it('returns true when all keys are custom', () => { - const isSensedKeys = [false, false, false]; - const isCustomKeys = [true, true, true]; - const result = isAllCustom(isSensedKeys, isCustomKeys); - expect(result).toBe(true); - }); - - it('returns false when all keys are sensed', () => { - const isSensedKeys = [true, true, true]; - const isCustomKeys = [false, false, false]; - const result = isAllCustom(isSensedKeys, isCustomKeys); - expect(result).toBe(false); - }); - - it('returns undefined for mixed custom and sensed keys', () => { - const isSensedKeys = [true, false, true]; - const isCustomKeys = [false, true, false]; - const result = isAllCustom(isSensedKeys, isCustomKeys); - expect(result).toBe(undefined); - }); - }); - describe('getUnitUtilsForMetric', () => { const imperialConfig = { distanceSuffix: 'mi', diff --git a/www/js/metrics/metricsHelper.ts b/www/js/metrics/metricsHelper.ts index c3edf8524..f0cc458c5 100644 --- a/www/js/metrics/metricsHelper.ts +++ b/www/js/metrics/metricsHelper.ts @@ -80,31 +80,6 @@ export function getActiveModes(labelOptions: LabelOptions) { }).map((mode) => mode.value); } -/* formatting data form carbon footprint calculations */ - -//modes considered on foot for carbon calculation, expandable as needed -export const ON_FOOT_MODES = ['WALKING', 'RUNNING', 'ON_FOOT'] as const; - -/* - * metric2val is a function that takes a metric entry and a field and returns - * the appropriate value. - * for regular data (user-specific), this will return the field value - * for avg data (aggregate), this will return the field value/nUsers - */ -export const metricToValue = (population: 'user' | 'aggregate', metric, field) => - population == 'user' ? metric[field] : metric[field] / metric.nUsers; - -//testing agains global list of what is "on foot" -//returns true | false -export function isOnFoot(mode: string) { - for (let ped_mode of ON_FOOT_MODES) { - if (mode === ped_mode) { - return true; - } - } - return false; -} - //from two weeks fo low and high values, calculates low and high change export function calculatePercentChange(pastWeekRange, previousWeekRange) { let greaterLesserPct = { @@ -114,56 +89,6 @@ export function calculatePercentChange(pastWeekRange, previousWeekRange) { return greaterLesserPct; } -export function parseDataFromMetrics(metrics, population) { - logDebug(`parseDataFromMetrics: metrics = ${JSON.stringify(metrics)}; - population = ${population}`); - let mode_bins: { [k: string]: [number, number, string][] } = {}; - metrics?.forEach((metric) => { - let onFootVal = 0; - - for (let field in metric) { - /*For modes inferred from sensor data, we check if the string is all upper case - by converting it to upper case and seeing if it is changed*/ - if (field == field.toUpperCase()) { - /*sum all possible on foot modes: see https://github.com/e-mission/e-mission-docs/issues/422 */ - if (isOnFoot(field)) { - onFootVal += metricToValue(population, metric, field); - field = 'ON_FOOT'; - } - if (!(field in mode_bins)) { - mode_bins[field] = []; - } - //for all except onFoot, add to bin - could discover mult onFoot modes - if (field != 'ON_FOOT') { - mode_bins[field].push([ - metric.ts, - metricToValue(population, metric, field), - metric.fmt_time, - ]); - } - } - const trimmedField = trimGroupingPrefix(field); - if (trimmedField) { - logDebug('Mapped field ' + field + ' to mode ' + trimmedField); - if (!(trimmedField in mode_bins)) { - mode_bins[trimmedField] = []; - } - mode_bins[trimmedField].push([ - metric.ts, - Math.round(metricToValue(population, metric, field)), - DateTime.fromISO(metric.fmt_time).toISO() as string, - ]); - } - } - //handle the ON_FOOT modes once all have been summed - if ('ON_FOOT' in mode_bins) { - mode_bins['ON_FOOT'].push([metric.ts, Math.round(onFootVal), metric.fmt_time]); - } - }); - - return Object.entries(mode_bins).map(([key, values]) => ({ key, values })); -} - const _datesTsCache = {}; export const tsForDayOfMetricData = (day: DayOfMetricData) => { if (_datesTsCache[day.date] == undefined) @@ -174,71 +99,6 @@ export const tsForDayOfMetricData = (day: DayOfMetricData) => { export const valueForFieldOnDay = (day: MetricEntry, field: string, key: string) => day[`${field}_${key}`]; -export type MetricsSummary = { key: string; values: number }; -export function generateSummaryFromData(modeMap, metric) { - logDebug(`Invoked getSummaryDataRaw on ${JSON.stringify(modeMap)} with ${metric}`); - - let summaryMap: MetricsSummary[] = []; - - for (let i = 0; i < modeMap.length; i++) { - let vals = 0; - for (let j = 0; j < modeMap[i].values.length; j++) { - vals += modeMap[i].values[j][1]; //2nd item of array is value - } - if (metric === 'mean_speed') { - // For speed, we take the avg. For other metrics we keep the sum - vals = vals / modeMap[i].values.length; - } - summaryMap.push({ - key: modeMap[i].key, - values: Math.round(vals), - }); - } - - return summaryMap; -} - -/* - * We use the results to determine whether these results are from custom - * labels or from the automatically sensed labels. Automatically sensedV - * labels are in all caps, custom labels are prefixed by label, but have had - * the label_prefix stripped out before this. Results should have either all - * sensed labels or all custom labels. - */ -export function isCustomLabels(modeMap) { - const isSensed = (mode) => mode == mode.toUpperCase(); - const isCustom = (mode) => mode == mode.toLowerCase(); - const metricSummaryChecksCustom: boolean[] = []; - const metricSummaryChecksSensed: boolean[] = []; - - const distanceKeys = modeMap.map((e) => e.key); - const isSensedKeys = distanceKeys.map(isSensed); - const isCustomKeys = distanceKeys.map(isCustom); - logDebug(`Checking metric keys ${distanceKeys}; sensed ${isSensedKeys}; custom ${isCustomKeys}`); - const isAllCustomForMetric = isAllCustom(isSensedKeys, isCustomKeys); - metricSummaryChecksSensed.push(!isAllCustomForMetric); - metricSummaryChecksCustom.push(Boolean(isAllCustomForMetric)); - logDebug(`overall custom/not results for each metric - is ${JSON.stringify(metricSummaryChecksCustom)}`); - return isAllCustom(metricSummaryChecksSensed, metricSummaryChecksCustom); -} - -export function isAllCustom(isSensedKeys, isCustomKeys) { - const allSensed = isSensedKeys.reduce((a, b) => a && b, true); - const anySensed = isSensedKeys.reduce((a, b) => a || b, false); - const allCustom = isCustomKeys.reduce((a, b) => a && b, true); - const anyCustom = isCustomKeys.reduce((a, b) => a || b, false); - if (allSensed && !anyCustom) { - return false; // sensed, not custom - } - if (!anySensed && allCustom) { - return true; // custom, not sensed; false implies that the other option is true - } - // Logger.displayError("Mixed entries that combine sensed and custom labels", - // "Please report to your program admin"); - return undefined; -} - // [unit suffix, unit conversion function, unit display function] // e.g. ['hours', (seconds) => seconds/3600, (seconds) => seconds/3600 + ' hours'] type UnitUtils = [string, (v) => number, (v) => string];