Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, expect, it } from '@jest/globals';
import { createEmptyCombinedDataCollectionSettings, createMockResult, sampleEndDate, sampleResult, sampleStartDate, setupCombinedDataCollectionSettings, setupCombinedFirstValueResult, setupDailyDataProvider } from '../../fixtures/daily-data-providers';
import combinedMindfulMinutes from '../../../src/helpers/daily-data-providers/combined-mindful-minutes';
import * as dailyDataResultFunctions from '../../../src/helpers/daily-data-providers/daily-data/daily-data-result';
import { appleHealthMindfulMinutesDataProvider, googleFitMindfulMinutesDataProvider } from '../../../src/helpers/daily-data-providers';
import { appleHealthMindfulMinutesDataProvider, googleFitMindfulMinutesDataProvider, healthConnectMindfulMinutesDataProvider } from '../../../src/helpers/daily-data-providers';

jest.mock('../../../src/helpers/daily-data-providers/apple-health-mindful-minutes', () => ({
__esModule: true,
Expand All @@ -14,10 +14,16 @@ jest.mock('../../../src/helpers/daily-data-providers/google-fit-mindful-minutes'
default: jest.fn()
}));

jest.mock('../../../src/helpers/daily-data-providers/health-connect-mindful-minutes', () => ({
__esModule: true,
default: jest.fn()
}));

describe('Daily Data Provider - Combined Mindful Minutes', () => {

const appleHealthMindfulMinutesDataProviderMock = appleHealthMindfulMinutesDataProvider as jest.Mock;
const googleFitMindfulMinutesDataProviderMock = googleFitMindfulMinutesDataProvider as jest.Mock;
const healthConnectMindfulMinutesDataProviderMock = healthConnectMindfulMinutesDataProvider as jest.Mock;
const combinedFirstValueResultMock = jest.spyOn(dailyDataResultFunctions, 'combineResultsUsingFirstValue');

beforeEach(() => {
Expand All @@ -27,28 +33,31 @@ describe('Daily Data Provider - Combined Mindful Minutes', () => {
it('Should return an empty result when no providers are enabled.', async () => {
const combinedSettings = createEmptyCombinedDataCollectionSettings();

setupCombinedDataCollectionSettings(false, combinedSettings);
setupCombinedDataCollectionSettings(true, combinedSettings);

const result = await combinedMindfulMinutes(sampleStartDate, sampleEndDate);

expect(result).toEqual({});
expect(appleHealthMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(googleFitMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(healthConnectMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(combinedFirstValueResultMock).not.toHaveBeenCalled();
});

it('Should return an empty result when providers are enabled, but not the correct data types.', async () => {
const combinedSettings = createEmptyCombinedDataCollectionSettings();
combinedSettings.settings.appleHealthEnabled = true;
combinedSettings.settings.googleFitEnabled = true;
combinedSettings.settings.healthConnectEnabled = true;

setupCombinedDataCollectionSettings(false, combinedSettings);
setupCombinedDataCollectionSettings(true, combinedSettings);

const result = await combinedMindfulMinutes(sampleStartDate, sampleEndDate);

expect(result).toEqual({});
expect(appleHealthMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(googleFitMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(healthConnectMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(combinedFirstValueResultMock).not.toHaveBeenCalled();
});

Expand All @@ -61,13 +70,14 @@ describe('Daily Data Provider - Combined Mindful Minutes', () => {

const appleHealthResult = createMockResult();

setupCombinedDataCollectionSettings(false, combinedSettings);
setupCombinedDataCollectionSettings(true, combinedSettings);
setupDailyDataProvider(appleHealthMindfulMinutesDataProviderMock, sampleStartDate, sampleEndDate, appleHealthResult);

const result = await combinedMindfulMinutes(sampleStartDate, sampleEndDate);

expect(result).toBe(appleHealthResult);
expect(googleFitMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(healthConnectMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(combinedFirstValueResultMock).not.toHaveBeenCalled();
});

Expand All @@ -80,13 +90,34 @@ describe('Daily Data Provider - Combined Mindful Minutes', () => {

const googleFitResult = createMockResult();

setupCombinedDataCollectionSettings(false, combinedSettings);
setupCombinedDataCollectionSettings(true, combinedSettings);
setupDailyDataProvider(googleFitMindfulMinutesDataProviderMock, sampleStartDate, sampleEndDate, googleFitResult);

const result = await combinedMindfulMinutes(sampleStartDate, sampleEndDate);

expect(result).toBe(googleFitResult);
expect(appleHealthMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(healthConnectMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(combinedFirstValueResultMock).not.toHaveBeenCalled();
});

it('Should return the Health Connect result when fully enabled.', async () => {
const combinedSettings = createEmptyCombinedDataCollectionSettings();
combinedSettings.settings.healthConnectEnabled = true;
combinedSettings.deviceDataV2Types.push(
{ namespace: 'HealthConnect', type: 'mindfulness-sessions', enabled: true }
);

const healthConnectResult = createMockResult();

setupCombinedDataCollectionSettings(true, combinedSettings);
setupDailyDataProvider(healthConnectMindfulMinutesDataProviderMock, sampleStartDate, sampleEndDate, healthConnectResult);

const result = await combinedMindfulMinutes(sampleStartDate, sampleEndDate);

expect(result).toBe(healthConnectResult);
expect(appleHealthMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(googleFitMindfulMinutesDataProviderMock).not.toHaveBeenCalled();
expect(combinedFirstValueResultMock).not.toHaveBeenCalled();
});

Expand All @@ -98,14 +129,20 @@ describe('Daily Data Provider - Combined Mindful Minutes', () => {
{ namespace: 'AppleHealth', type: 'MindfulSession' },
{ namespace: 'GoogleFit', type: 'ActivitySegment' }
);
combinedSettings.settings.healthConnectEnabled = true;
combinedSettings.deviceDataV2Types.push(
{ namespace: 'HealthConnect', type: 'mindfulness-sessions', enabled: true }
);

const appleHealthResult = createMockResult();
const googleFitResult = createMockResult();
const healthConnectResult = createMockResult();

setupCombinedDataCollectionSettings(false, combinedSettings);
setupCombinedDataCollectionSettings(true, combinedSettings);
setupDailyDataProvider(appleHealthMindfulMinutesDataProviderMock, sampleStartDate, sampleEndDate, appleHealthResult);
setupDailyDataProvider(googleFitMindfulMinutesDataProviderMock, sampleStartDate, sampleEndDate, googleFitResult);
setupCombinedFirstValueResult(sampleStartDate, sampleEndDate, [appleHealthResult, googleFitResult], sampleResult);
setupDailyDataProvider(healthConnectMindfulMinutesDataProviderMock, sampleStartDate, sampleEndDate, healthConnectResult);
setupCombinedFirstValueResult(sampleStartDate, sampleEndDate, [appleHealthResult, googleFitResult, healthConnectResult], sampleResult);

const result = await combinedMindfulMinutes(sampleStartDate, sampleEndDate);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { describe, expect, it } from '@jest/globals';
import { sampleDataPointsV2, sampleEndDate, sampleResult, sampleStartDate, sampleTimeRanges, setupDailyDataPointsV2, setupDailyTimeRanges, setupMinutesResult } from '../../fixtures/daily-data-providers';
import healthConnectMindfulMinutes from '../../../src/helpers/daily-data-providers/health-connect-mindful-minutes';

describe('Daily Data Provider - Health Connect Mindful Minutes', () => {
it('Should query for daily data points and build a minutes result.', async () => {
setupDailyDataPointsV2('HealthConnect', 'mindfulness-sessions', sampleStartDate, sampleEndDate, undefined, undefined, sampleDataPointsV2);
setupDailyTimeRanges(sampleDataPointsV2, sampleTimeRanges);
setupMinutesResult(sampleStartDate, sampleEndDate, sampleTimeRanges, sampleResult);

expect(await healthConnectMindfulMinutes(sampleStartDate, sampleEndDate)).toBe(sampleResult);
});
});
2 changes: 1 addition & 1 deletion rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import terser from "@rollup/plugin-terser";
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import analyze from 'rollup-plugin-analyzer';

const limitBytes = 8.5e6;
const limitBytes = 9.0e6;

const onAnalysis = ({ bundleSize }) => {
if (bundleSize < limitBytes) return;
Expand Down
7 changes: 5 additions & 2 deletions src/helpers/daily-data-providers/combined-mindful-minutes.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { appleHealthMindfulMinutesDataProvider, googleFitMindfulMinutesDataProvider } from '.';
import { appleHealthMindfulMinutesDataProvider, googleFitMindfulMinutesDataProvider, healthConnectMindfulMinutesDataProvider } from '.';
import { DailyDataQueryResult } from '../query-daily-data';
import { getCombinedDataCollectionSettings } from './combined-data-collection-settings';
import { combineResultsUsingFirstValue } from './daily-data';

export default async function (startDate: Date, endDate: Date): Promise<DailyDataQueryResult> {
const providers: Promise<DailyDataQueryResult>[] = [];

const { settings } = await getCombinedDataCollectionSettings(false);
const { settings, deviceDataV2Types } = await getCombinedDataCollectionSettings(true);

if (settings.appleHealthEnabled && settings.queryableDeviceDataTypes.some(type => type.namespace === 'AppleHealth' && type.type === 'MindfulSession')) {
providers.push(appleHealthMindfulMinutesDataProvider(startDate, endDate));
}
if (settings.googleFitEnabled && settings.queryableDeviceDataTypes.some(type => type.namespace === 'GoogleFit' && type.type === 'ActivitySegment')) {
providers.push(googleFitMindfulMinutesDataProvider(startDate, endDate));
}
if (settings.healthConnectEnabled && deviceDataV2Types.some(type => type.namespace === 'HealthConnect' && type.type === 'mindfulness-sessions')) {
providers.push(healthConnectMindfulMinutesDataProvider(startDate, endDate));
}

if (providers.length === 0) return {};
if (providers.length === 1) return providers[0];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { DailyDataQueryResult } from '../query-daily-data';
import { buildMinutesResultFromDailyTimeRanges, computeDailyTimeRanges } from '../time-range';
import { queryForDailyDataPointsV2 } from './daily-data';

export default async function(startDate: Date, endDate: Date): Promise<DailyDataQueryResult> {
const dataPoints = await queryForDailyDataPointsV2('HealthConnect', 'mindfulness-sessions', startDate, endDate);
const dailyTimeRanges = computeDailyTimeRanges(dataPoints);
return buildMinutesResultFromDailyTimeRanges(startDate, endDate, dailyTimeRanges);
}
1 change: 1 addition & 0 deletions src/helpers/daily-data-providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export { default as healthConnectMinHeartRateDataProvider } from "./health-conne
export { default as healthConnectActiveCaloriesBurnedDataProvider } from "./health-connect-active-calories-burned";
export { default as healthConnectTotalCaloriesBurnedDataProvider } from "./health-connect-total-calories-burned";
export { default as healthConnectTherapyMinutesDataProvider } from "./health-connect-therapy-minutes";
export { default as healthConnectMindfulMinutesDataProvider } from "./health-connect-mindful-minutes";
export { default as ouraStepsDataProvider } from "./oura-daily-steps"
export { default as ouraSleepMinutesDataProvider } from "./oura-total-sleep"
export { default as ouraRestingHeartRateDataProvider } from "./oura-resting-heart-rate"
Expand Down
1 change: 1 addition & 0 deletions src/helpers/daily-data-types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export enum DailyDataType {
HealthConnectActiveCaloriesBurned = "HealthConnectActiveCaloriesBurned",
HealthConnectTotalCaloriesBurned = "HealthConnectTotalCaloriesBurned",
HealthConnectTherapyMinutes = "HealthConnectTherapyMinutes",
HealthConnectMindfulMinutes = "HealthConnectMindfulMinutes",
OuraSteps = "OuraSteps",
OuraRestingHeartRate = "OuraRestingHeartRate",
OuraSleepMinutes = "OuraSleepMinutes",
Expand Down
3 changes: 2 additions & 1 deletion src/helpers/daily-data-types/combined.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ const SLEEP_MINUTES_SOURCES = sources(

const MINDFUL_MINUTES_SOURCES = sources(
["AppleHealth", "MindfulSession"],
["GoogleFit", "ActivitySegment"]
["GoogleFit", "ActivitySegment"],
["HealthConnect", "mindfulness-sessions"]
);

const THERAPY_MINUTES_SOURCES = sources(
Expand Down
10 changes: 10 additions & 0 deletions src/helpers/daily-data-types/health-connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
healthConnectDistanceDataProvider,
healthConnectLightSleepMinutesDataProvider,
healthConnectMaxHeartRateDataProvider,
healthConnectMindfulMinutesDataProvider,
healthConnectMinHeartRateDataProvider,
healthConnectRemSleepMinutesDataProvider,
healthConnectRestingHeartRateDataProvider,
Expand Down Expand Up @@ -135,6 +136,15 @@ const healthConnectTypeDefinitions: DailyDataTypeDefinition[] = [
icon: <FontAwesomeSvgIcon icon={faHourglassHalf} />,
formatter: value => formatNumberForLocale(value),
previewDataRange: [0, 120]
},
{
type: DailyDataType.HealthConnectMindfulMinutes,
dataProvider: healthConnectMindfulMinutesDataProvider,
availabilityCheck: simpleAvailabilityCheck('HealthConnect', 'mindfulness-sessions'),
labelKey: 'mindful-minutes',
icon: <FontAwesomeSvgIcon icon={faHourglassHalf} />,
formatter: value => formatNumberForLocale(value),
previewDataRange: [0, 120]
}
];
healthConnectTypeDefinitions.forEach((def) => {
Expand Down
Loading