diff --git a/README.md b/README.md index 7679e23d..2b8b1938 100644 --- a/README.md +++ b/README.md @@ -296,18 +296,20 @@ The currently supported shims are: | fitbit1 | [step_count](https://dev.fitbit.com/docs/activity/#get-activity-intraday-time-series) (intraday)2 | [omh:step-count:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [FitbitIntradayStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/fitbit/mapper/FitbitIntradayStepCountDataPointMapper.java) | | googlefit | [body_height](https://developers.google.com/fit/rest/v1/data-types) | [omh:body-height:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-height) | [GoogleFitBodyHeightDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapper.java) | | googlefit | [body_weight](https://developers.google.com/fit/rest/v1/data-types) | [omh:body-weight:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight) | [GoogleFitBodyWeightDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapper.java) | -| googlefit | [calories_burned](https://developers.google.com/fit/rest/v1/data-types) | [omh:calories-burned:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [GoogleFitCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapper.java) | +| googlefit | [calories_burned](https://developers.google.com/fit/rest/v1/data-types) | [omh:calories-burned:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [GoogleFitCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapper.java) | +| googlefit | [geoposition](https://developers.google.com/fit/rest/v1/data-types) | [omh:geoposition:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_geoposition) | [GoogleFitGeopositionDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitGeopositionDataPointMapper.java) | | googlefit | [heart_rate](https://developers.google.com/fit/rest/v1/data-types) | [omh:heart-rate:1.1](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate) | [GoogleFitHeartRateDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapper.java) | | googlefit | [physical_activity](https://developers.google.com/fit/rest/v1/data-types) | [omh:physical-activity:1.2](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_physical-activity) | [GoogleFitPhysicalActivityDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapper.java) | -| googlefit | [step_count](https://developers.google.com/fit/rest/v1/data-types) | [omh:step-count:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [GoogleFitStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapper.java) | +| googlefit | [speed](https://developers.google.com/fit/rest/v1/data-types) | [omh:speed:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_speed) | [GoogleFitSpeedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitSpeedDataPointMapper.java) | +| googlefit | [step_count](https://developers.google.com/fit/rest/v1/data-types) | [omh:step-count:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [GoogleFitStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapper.java) | | ihealth | [blood_glucose](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofBG.htm) | [omh:blood-glucose:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-glucose) | [IHealthBloodGlucoseDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthBloodGlucoseDataPointMapper.java) | | ihealth | [blood_pressure](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofBloodPressure.htm) | [omh:blood-pressure:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-pressure) | [IHealthBloodPressureDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthBloodPressureDataPointMapper.java) | | ihealth | [body_mass_index](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofWeight.htm) | [omh:body-mass-index:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-mass-index) | [IHealthBodyMassIndexDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthBodyMassIndexDataPointMapper.java) | | ihealth | [body_weight](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofWeight.htm) | [omh:body-weight:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight) | [IHealthBodyWeightDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthBodyWeightDataPointMapper.java) | | ihealth | [heart_rate](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofActivityReport.htm) | [omh:heart-rate:1.1](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate) | [IHealthHeartRateDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthHeartRateDataPointMapper.java) | | ihealth | [physical_activity](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofSport.htm) | [omh:physical-activity:1.2](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_physical-activity) | [IHealthPhysicalActivityDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthPhysicalActivityDataPointMapper.java) | -| ihealth | [sleep_duration](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofSleepReport.htm) | [omh:sleep-duration:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) | [IHealthSleepDurationDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapper.java) | -| ihealth | [step_count](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofActivityReport.htm) | [omh:step-count:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [IHealthStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapper.java) | +| ihealth | [sleep_duration](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofSleepReport.htm) | [omh:sleep-duration:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) | [IHealthSleepDurationDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapper.java) | +| ihealth | [step_count](http://developer.ihealthlabs.com/dev_documentation_RequestfordataofActivityReport.htm) | [omh:step-count:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [IHealthStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapper.java) | | jawbone | [body_mass_index](https://jawbone.com/up/developer/endpoints/body) | [omh:body-mass-index:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-mass-index) | [JawboneBodyMassIndexDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/jawbone/mapper/JawboneBodyMassIndexDataPointMapper.java) | | jawbone | [body_weight](https://jawbone.com/up/developer/endpoints/body) | [omh:body-weight:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight) | [JawboneBodyWeightDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/jawbone/mapper/JawboneBodyWeightDataPointMapper.java) | | jawbone | [heart_rate](https://jawbone.com/up/developer/endpoints)3 | [omh:heart-rate:1.1](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate) | [JawboneHeartRateDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/jawbone/mapper/JawboneHeartRateDataPointMapper.java) | @@ -315,21 +317,24 @@ The currently supported shims are: | jawbone | [sleep_duration](https://jawbone.com/up/developer/endpoints/sleeps) | [omh:sleep-duration:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) | [JawboneSleepDurationDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/jawbone/mapper/JawboneSleepDurationDataPointMapper.java) | | jawbone | [step_count](https://jawbone.com/up/developer/endpoints) | [omh:step-count:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [JawboneStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/jawbone/mapper/JawboneStepCountDataPointMapper.java) | | misfit | [physical_activity](https://build.misfit.com/docs/cloudapi/api_references#session) | [omh:physical-activity:1.2](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_physical-activity) | [MisfitPhysicalActivityDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitPhysicalActivityDataPointMapper.java) | -| misfit | [step_count](https://build.misfit.com/docs/cloudapi/api_references#steps) | [omh:step-count:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [MisfitStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapper.java) | -| misfit | [sleep_duration](https://build.misfit.com/docs/cloudapi/api_references#sleep) | [omh:sleep-duration:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) | [MisfitSleepDurationDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapper.java) | +| misfit | [step_count](https://build.misfit.com/docs/cloudapi/api_references#steps) | [omh:step-count21.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [MisfitStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapper.java) | +| misfit | [sleep_duration](https://build.misfit.com/docs/cloudapi/api_references#sleep) | [omh:sleep-duration:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) | [MisfitSleepDurationDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapper.java) | +| misfit | [sleep_episode](https://build.misfit.com/docs/cloudapi/api_references#sleep) | [omh:sleep-episode:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-episode) | [MisfitSleepEpisodeDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepEpisodeDataPointMapper.java) | | moves4 | [physical_activity](https://dev.moves-app.com/docs/api_activities) | [omh:physical-activity:1.2](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_physical-activity) | [MovesPhysicalActivityDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapper.java) | | moves4 | [step_count](https://dev.moves-app.com/docs/api_storyline) | [omh:step-count:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [MovesStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapper.java) | -| runkeeper | [calories_burned](http://runkeeper.com/developer/healthgraph/fitness-activities#past) | [omh:calories-burned:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [RunkeeperCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapper.java) | +| runkeeper | [calories_burned](http://runkeeper.com/developer/healthgraph/fitness-activities#past) | [omh:calories-burned:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [RunkeeperCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapper.java) | | runkeeper | [physical_activity](http://runkeeper.com/developer/healthgraph/fitness-activities#past) | [omh:physical-activity:1.2](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_physical-activity) | [RunkeeperPhysicalActivityDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperPhysicalActivityDataPointMapper.java) | | withings | [blood_pressure](https://oauth.withings.com/api/doc#api-Measure-get_measure) | [omh:blood-pressure:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-pressure) | [WithingsBloodPressureDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBloodPressureDataPointMapper.java) | | withings | [body_height](https://oauth.withings.com/api/doc#api-Measure-get_measure) | [omh:body-height:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-height) | [WithingsBodyHeightDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapper.java) | | withings | [body_weight](https://oauth.withings.com/api/doc#api-Measure-get_measure) | [omh:body-weight:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight) | [WithingsBodyWeightDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapper.java) | -| withings | [calories_burned](http://oauth.withings.com/api/doc#api-Measure-get_activity)5 | [omh:calories-burned:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [WithingsDailyCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapper.java) | -| withings | [calories_burned](http://oauth.withings.com/api/doc#api-Measure-get_intraday_measure) (intraday)5 | [omh:calories-burned:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [WithingsIntradayCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapper.java) | +| withings | [calories_burned](http://oauth.withings.com/api/doc#api-Measure-get_activity)5 | [omh:calories-burned:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [WithingsDailyCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapper.java) | +| withings | [calories_burned](http://oauth.withings.com/api/doc#api-Measure-get_intraday_measure) (intraday)5 | [omh:calories-burned:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned) | [WithingsIntradayCaloriesBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapper.java) | +| withings | [body_temperature](http://oauth.withings.com/api/doc#api-Measure-get_measure) | [omh:body_temperature:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-temperature) | [WithingsBodyTemperatureDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyTemperatureDataPointMapper.java) | | withings | [heart_rate](http://oauth.withings.com/api/doc#api-Measure-get_measure) | [omh:heart-rate:1.1](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate) | [WithingsHeartRateDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapper.java) | -| withings | [sleep_duration](http://oauth.withings.com/api/doc#api-Measure-get_sleep)6 | [omh:sleep-duration:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) | [WithingsSleepDurationDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapper.java) | -| withings | [step_count](http://oauth.withings.com/api/doc#api-Measure-get_activity)5 | [omh:step-count:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [WithingsDailyStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapper.java) | -| withings | [step_count](http://oauth.withings.com/api/doc#api-Measure-get_intraday_measure) (intraday)5 | [omh:step-count:1.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [WithingsIntradayStepCountBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountBurnedDataPointMapper.java) | +| withings | [sleep_duration](http://oauth.withings.com/api/doc#api-Measure-get_sleep)6 | [omh:sleep-duration:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) | [WithingsSleepDurationDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapper.java) | +| withings | [sleep_episode](http://oauth.withings.com/api/doc#api-Measure-get_sleep)6 | [omh:sleep-episode:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-episode) | [WithingsSleepEpisodeDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepEpisodeDataPointMapper.java) | +| withings | [step_count](http://oauth.withings.com/api/doc#api-Measure-get_activity)5 | [omh:step-count:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [WithingsDailyStepCountDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapper.java) | +| withings | [step_count](http://oauth.withings.com/api/doc#api-Measure-get_intraday_measure) (intraday)5 | [omh:step-count:2.0](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) | [WithingsIntradayStepCountBurnedDataPointMapper](https://github.com/openmhealth/shimmer/blob/master/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountBurnedDataPointMapper.java) | 1 *The Fitbit API doesn't provide time zone information for the data points it returns. Furthermore, it is not possible to infer the time zone from any of the information provided. Because Open mHealth schemas require timestamps to have a time zone, we need to assign a time zone to timestamps. We set the time zone of all timestamps to UTC for consistency, even if the data may not have occurred in that time zone. This means that unless the event actually occurred in UTC, the timestamps will contain an incorrect time zone. Please consider this when working with data normalized into OmH schemas that are retrieved from the Fitbit shim. We will fix this as soon as Fitbit makes changes to their API to provide time zone information.* diff --git a/build.gradle b/build.gradle index 3a670e1f..d1c044dd 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ subprojects { ext { javaVersion = 1.8 shimmerVersion = "0.6.0" - omhSchemaSdkVersion = "1.1.0" + omhSchemaSdkVersion = "1.2.1" } sourceCompatibility = javaVersion diff --git a/shim-server/build.gradle b/shim-server/build.gradle index e88abd0b..e404534f 100644 --- a/shim-server/build.gradle +++ b/shim-server/build.gradle @@ -33,6 +33,7 @@ ext { dependencies { compile project(":java-shim-sdk") compile "commons-io:commons-io:2.4" + compile "com.google.guava:guava:23.0" compile "org.hibernate:hibernate-validator" compile "org.apache.httpcomponents:httpclient" compile "org.apache.httpcomponents:httpcore" diff --git a/shim-server/src/main/java/org/openmhealth/shim/CaseStandardizingOAuth2RequestAuthenticator.java b/shim-server/src/main/java/org/openmhealth/shim/CaseStandardizingOAuth2RequestAuthenticator.java new file mode 100644 index 00000000..34b9319e --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/CaseStandardizingOAuth2RequestAuthenticator.java @@ -0,0 +1,56 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim; + +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.security.oauth2.client.DefaultOAuth2RequestAuthenticator; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RequestAuthenticator; +import org.springframework.security.oauth2.client.http.AccessTokenRequiredException; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.util.StringUtils; + + +/** + * A customization of {@link DefaultOAuth2RequestAuthenticator} that standardizes the case of the Authorization header + * token type to "Bearer". This is necessary because the default implementation doesn't work for Moves, which serves up + * a "bearer" token but only accepts "Bearer" authorization headers. + * + * @author Dave Syer + * @author Emerson Farrugia + */ +public class CaseStandardizingOAuth2RequestAuthenticator implements OAuth2RequestAuthenticator { + + @Override + public void authenticate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext clientContext, + ClientHttpRequest request) { + + OAuth2AccessToken accessToken = clientContext.getAccessToken(); + if (accessToken == null) { + throw new AccessTokenRequiredException(resource); + } + + String tokenType = accessToken.getTokenType(); + + if (!StringUtils.hasText(tokenType) || tokenType.equalsIgnoreCase(OAuth2AccessToken.BEARER_TYPE)) { + tokenType = OAuth2AccessToken.BEARER_TYPE; // we'll assume basic bearer token type if none is specified. + } + + request.getHeaders().set("Authorization", String.format("%s %s", tokenType, accessToken.getValue())); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/OAuth2Shim.java b/shim-server/src/main/java/org/openmhealth/shim/OAuth2Shim.java index bcc71308..8f9e9115 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/OAuth2Shim.java +++ b/shim-server/src/main/java/org/openmhealth/shim/OAuth2Shim.java @@ -274,6 +274,8 @@ protected OAuth2RestOperations restTemplate(String stateKey, String code) { new AccessParameterClientTokenServices(accessParametersRepo)); restTemplate.setAccessTokenProvider(tokenProviderChain); + restTemplate.setAuthenticator(new CaseStandardizingOAuth2RequestAuthenticator()); + return restTemplate; } diff --git a/shim-server/src/main/java/org/openmhealth/shim/OptionalStreamSupport.java b/shim-server/src/main/java/org/openmhealth/shim/OptionalStreamSupport.java new file mode 100644 index 00000000..28f1cf43 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/OptionalStreamSupport.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim; + +import java.util.Optional; +import java.util.stream.Stream; + + +/** + * A set of utility methods to help with {@link Optional} {@link Stream} objects. + * + * @author Emerson Farrugia + */ +public class OptionalStreamSupport { + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public static Stream asStream(Optional optional) { + + return optional.map(Stream::of).orElseGet(Stream::empty); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapper.java index e5f5f9a0..b41d4629 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapper.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.fitbit.mapper; import com.fasterxml.jackson.databind.JsonNode; diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitClientSettings.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitClientSettings.java index 56bf4c57..fb3360cb 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitClientSettings.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitClientSettings.java @@ -34,7 +34,8 @@ public class GoogleFitClientSettings extends OAuth2ClientSettings { private List scopes = Arrays.asList( "https://www.googleapis.com/auth/fitness.activity.read", - "https://www.googleapis.com/auth/fitness.body.read" + "https://www.googleapis.com/auth/fitness.body.read", + "https://www.googleapis.com/auth/fitness.location.read" ); @Override diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitShim.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitShim.java index b2ea660e..00a2bf8d 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitShim.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/GoogleFitShim.java @@ -48,7 +48,6 @@ import java.time.ZoneOffset; import java.util.Map; -import static java.util.Collections.singletonList; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.http.ResponseEntity.ok; @@ -114,8 +113,10 @@ public ShimDataType[] getShimDataTypes() { GoogleFitDataTypes.BODY_HEIGHT, GoogleFitDataTypes.BODY_WEIGHT, GoogleFitDataTypes.CALORIES_BURNED, + GoogleFitDataTypes.GEOPOSITION, GoogleFitDataTypes.HEART_RATE, GoogleFitDataTypes.PHYSICAL_ACTIVITY, + GoogleFitDataTypes.SPEED, GoogleFitDataTypes.STEP_COUNT }; } @@ -125,22 +126,21 @@ public enum GoogleFitDataTypes implements ShimDataType { BODY_HEIGHT("derived:com.google.height:com.google.android.gms:merge_height"), BODY_WEIGHT("derived:com.google.weight:com.google.android.gms:merge_weight"), CALORIES_BURNED("derived:com.google.calories.expended:com.google.android.gms:merge_calories_expended"), + GEOPOSITION("derived:com.google.location.sample:com.google.android.gms:merge_location_samples"), HEART_RATE("derived:com.google.heart_rate.bpm:com.google.android.gms:merge_heart_rate_bpm"), PHYSICAL_ACTIVITY("derived:com.google.activity.segment:com.google.android.gms:merge_activity_segments"), + SPEED("derived:com.google.speed:com.google.android.gms:merge_speed"), STEP_COUNT("derived:com.google.step_count.delta:com.google.android.gms:merge_step_deltas"); private final String streamId; GoogleFitDataTypes(String streamId) { - this.streamId = streamId; } public String getStreamId() { - return streamId; } - } protected ResponseEntity getData(OAuth2RestOperations restTemplate, @@ -157,31 +157,34 @@ protected ResponseEntity getData(OAuth2RestOperations restTemp + " in shimDataRequest, cannot retrieve data."); } - OffsetDateTime todayInUTC = LocalDate.now().atStartOfDay().atOffset(ZoneOffset.UTC); OffsetDateTime startDateInUTC = shimDataRequest.getStartDateTime() == null ? + todayInUTC.minusDays(1) : shimDataRequest.getStartDateTime(); - long startTimeNanos = (startDateInUTC.toEpochSecond() * 1000000000) + startDateInUTC.toInstant().getNano(); + long startTimeNanos = (startDateInUTC.toEpochSecond() * 1_000_000_000) + startDateInUTC.toInstant().getNano(); OffsetDateTime endDateInUTC = shimDataRequest.getEndDateTime() == null ? todayInUTC.plusDays(1) : shimDataRequest.getEndDateTime().plusDays(1); // We are inclusive of the last day, so add 1 day to get + // the end of day on the last day, which captures the // entire last day - long endTimeNanos = (endDateInUTC.toEpochSecond() * 1000000000) + endDateInUTC.toInstant().getNano(); + long endTimeNanos = (endDateInUTC.toEpochSecond() * 1_000_000_000) + endDateInUTC.toInstant().getNano(); - UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(DATA_URL) - .pathSegment(googleFitDataType.getStreamId(), "datasets", "{startDate}-{endDate}"); // TODO: Add limits back into the request once Google has fixed the 'limit' query parameter and paging - - URI uriRequest = uriBuilder.buildAndExpand(startTimeNanos, endTimeNanos).encode().toUri(); + URI uri = UriComponentsBuilder + .fromUriString(DATA_URL) + .pathSegment(googleFitDataType.getStreamId(), "datasets", "{startDate}-{endDate}") + .buildAndExpand(startTimeNanos, endTimeNanos) + .encode() + .toUri(); ResponseEntity responseEntity; try { - responseEntity = restTemplate.getForEntity(uriRequest, JsonNode.class); + responseEntity = restTemplate.getForEntity(uri, JsonNode.class); } catch (HttpClientErrorException | HttpServerErrorException e) { // TODO figure out how to handle this @@ -190,36 +193,46 @@ protected ResponseEntity getData(OAuth2RestOperations restTemp } if (shimDataRequest.getNormalize()) { - GoogleFitDataPointMapper dataPointMapper; - switch (googleFitDataType) { - case BODY_WEIGHT: - dataPointMapper = new GoogleFitBodyWeightDataPointMapper(); - break; - case BODY_HEIGHT: - dataPointMapper = new GoogleFitBodyHeightDataPointMapper(); - break; - case PHYSICAL_ACTIVITY: - dataPointMapper = new GoogleFitPhysicalActivityDataPointMapper(); - break; - case STEP_COUNT: - dataPointMapper = new GoogleFitStepCountDataPointMapper(); - break; - case HEART_RATE: - dataPointMapper = new GoogleFitHeartRateDataPointMapper(); - break; - case CALORIES_BURNED: - dataPointMapper = new GoogleFitCaloriesBurnedDataPointMapper(); - break; - default: - throw new UnsupportedOperationException(); - } + GoogleFitDataPointMapper dataPointMapper = getDataPointMapper(googleFitDataType); - return ok().body(ShimDataResponse.result(GoogleFitShim.SHIM_KEY, dataPointMapper.asDataPoints( - singletonList(responseEntity.getBody())))); + return ok().body(ShimDataResponse + .result(GoogleFitShim.SHIM_KEY, dataPointMapper.asDataPoints(responseEntity.getBody()))); } else { + return ok().body(ShimDataResponse + .result(GoogleFitShim.SHIM_KEY, responseEntity.getBody())); + } + } + + private GoogleFitDataPointMapper getDataPointMapper(GoogleFitDataTypes googleFitDataType) { + + switch (googleFitDataType) { + case BODY_HEIGHT: + return new GoogleFitBodyHeightDataPointMapper(); + + case BODY_WEIGHT: + return new GoogleFitBodyWeightDataPointMapper(); + + case CALORIES_BURNED: + return new GoogleFitCaloriesBurnedDataPointMapper(); + + case GEOPOSITION: + return new GoogleFitGeopositionDataPointMapper(); + + case HEART_RATE: + return new GoogleFitHeartRateDataPointMapper(); + + case PHYSICAL_ACTIVITY: + return new GoogleFitPhysicalActivityDataPointMapper(); + + case SPEED: + return new GoogleFitSpeedDataPointMapper(); + + case STEP_COUNT: + return new GoogleFitStepCountDataPointMapper(); - return ok().body(ShimDataResponse.result(GoogleFitShim.SHIM_KEY, responseEntity.getBody())); + default: + throw new UnsupportedOperationException(); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapper.java index 990e1a31..55e37b6e 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapper.java @@ -46,11 +46,11 @@ public Optional> asDataPoint(JsonNode listNode) { return Optional.empty(); } - BodyHeight.Builder bodyHeightBuilder = new BodyHeight.Builder(new LengthUnitValue(METER, bodyHeightValue)); + BodyHeight.Builder measureBuilder = new BodyHeight.Builder(new LengthUnitValue(METER, bodyHeightValue)); - setEffectiveTimeFrameIfPresent(bodyHeightBuilder, listNode); + getOptionalTimeFrame(listNode).ifPresent(measureBuilder::setEffectiveTimeFrame); - BodyHeight bodyHeight = bodyHeightBuilder.build(); + BodyHeight bodyHeight = measureBuilder.build(); Optional originDataSourceId = asOptionalString(listNode, "originDataSourceId"); return Optional.of(newDataPoint(bodyHeight, originDataSourceId.orElse(null))); diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapper.java index d81465de..4fcbd79f 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapper.java @@ -47,12 +47,13 @@ public Optional> asDataPoint(JsonNode listNode) { return Optional.empty(); } - BodyWeight.Builder bodyWeightBuilder = new BodyWeight.Builder(new MassUnitValue(KILOGRAM, bodyWeightValue)); - setEffectiveTimeFrameIfPresent(bodyWeightBuilder, listNode); + BodyWeight.Builder measureBuilder = new BodyWeight.Builder(new MassUnitValue(KILOGRAM, bodyWeightValue)); + + getOptionalTimeFrame(listNode).ifPresent(measureBuilder::setEffectiveTimeFrame); Optional originDataSourceId = asOptionalString(listNode, "originDataSourceId"); - BodyWeight bodyWeight = bodyWeightBuilder.build(); + BodyWeight bodyWeight = measureBuilder.build(); return Optional.of(newDataPoint(bodyWeight, originDataSourceId.orElse(null))); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapper.java index 2f3dc36a..e9c4838f 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapper.java @@ -17,9 +17,8 @@ package org.openmhealth.shim.googlefit.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.CaloriesBurned; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.KcalUnitValue; import java.util.Optional; @@ -28,32 +27,29 @@ /** - * A mapper from Google Fit "merged calories expended" endpoint responses - * (derived:com.google.calories.expended:com.google.android.gms:merge_calories_expended) to {@link CaloriesBurned} - * objects. + * A mapper from Google Fit "merged calories expended" endpoint responses (derived:com.google.calories.expended:com.google.android.gms:merge_calories_expended) + * to {@link CaloriesBurned2} objects. * * @author Chris Schaefbauer * @see Google Fit Data Type Documentation */ -public class GoogleFitCaloriesBurnedDataPointMapper extends GoogleFitDataPointMapper { +public class GoogleFitCaloriesBurnedDataPointMapper extends GoogleFitDataPointMapper { @Override - protected Optional> asDataPoint(JsonNode listNode) { + protected Optional> asDataPoint(JsonNode listNode) { JsonNode listValueNode = asRequiredNode(listNode, "value"); - // TODO isn't this just "value.fpVal"? double caloriesBurnedValue = asRequiredDouble(listValueNode.get(0), "fpVal"); - CaloriesBurned.Builder caloriesBurnedBuilder = - new CaloriesBurned.Builder(new KcalUnitValue(KILOCALORIE, caloriesBurnedValue)); + CaloriesBurned2.Builder measureBuilder = + new CaloriesBurned2.Builder(KILOCALORIE.newUnitValue(caloriesBurnedValue), getTimeFrame(listNode)); - setEffectiveTimeFrameIfPresent(caloriesBurnedBuilder, listNode); + CaloriesBurned2 caloriesBurned = measureBuilder.build(); - CaloriesBurned caloriesBurned = caloriesBurnedBuilder.build(); Optional originDataSourceId = asOptionalString(listNode, "originDataSourceId"); // Google Fit calories burned endpoint returns calories burned by basal metabolic rate (BMR), however these - // are not activity related calories burned so we do not create a datapoint for values from this source + // are not activity related calories burned so we do not create a data point for values from this source if (originDataSourceId.isPresent()) { if (originDataSourceId.get().contains("bmr")) { return Optional.empty(); diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapper.java index 952271f2..4f97d62d 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapper.java @@ -20,16 +20,20 @@ import com.google.common.collect.Lists; import org.openmhealth.schema.domain.omh.*; import org.openmhealth.shim.common.mapper.JsonNodeDataPointMapper; +import org.openmhealth.shim.common.mapper.MissingJsonNodeMappingException; import java.time.Instant; import java.time.OffsetDateTime; -import java.time.ZoneId; import java.util.List; import java.util.Optional; import java.util.UUID; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Long.parseLong; +import static java.time.ZoneOffset.UTC; +import static java.util.Optional.empty; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalNode; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalString; @@ -43,6 +47,9 @@ public abstract class GoogleFitDataPointMapper implements Jso public static final String RESOURCE_API_SOURCE_NAME = "Google Fit API"; + private static final String EPOCH_NS_START_DATE_TIME_PATH = "startTimeNanos"; + private static final String EPOCH_NS_END_DATE_TIME_PATH = "endTimeNanos"; + /** * Maps a JSON response from the Google Fit API containing a JSON array of data points to a list of {@link * DataPoint} objects of the appropriate measure type. Splits individual nodes based on the name of the list node, @@ -67,7 +74,6 @@ public List> asDataPoints(List responseNodes) { } return dataPoints; - } /** @@ -114,40 +120,52 @@ public DataPoint newDataPoint(T measure, String fitDataSourceId) { } /** - * Converts a nanosecond timestamp from the Google Fit API into an offset datetime value. + * Converts a nanosecond timestamp from the Google Fit API into an offset date time value. * - * @param unixEpochNanosString the timestamp directly from the Google JSON document + * @param epochNanosecondString the timestamp directly from the Google JSON document * @return an offset datetime object representing the input timestamp */ - public OffsetDateTime convertGoogleNanosToOffsetDateTime(String unixEpochNanosString) { + private OffsetDateTime asOffsetDateTime(String epochNanosecondString) { - return OffsetDateTime.ofInstant(Instant.ofEpochSecond(0, Long.parseLong(unixEpochNanosString)), ZoneId.of("Z")); + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(0, parseLong(epochNanosecondString)), UTC); } /** - * @param builder a measure builder of type T - * @param listNode the JSON node representing an individual datapoint, which contains the start and end time - * properties, from within the response array + * @param node a JSON node optionally containing time frame properties */ - public void setEffectiveTimeFrameIfPresent(T.Builder builder, JsonNode listNode) { + public Optional getOptionalTimeFrame(JsonNode node) { - Optional startTimeNanosString = asOptionalString(listNode, "startTimeNanos"); - Optional endTimeNanosString = asOptionalString(listNode, "endTimeNanos"); + Optional startTimeNanosString = asOptionalString(node, EPOCH_NS_START_DATE_TIME_PATH); + Optional endTimeNanosString = asOptionalString(node, EPOCH_NS_END_DATE_TIME_PATH); // When the start and end times are identical, such as for a single body weight measure, then we only need to // create an effective time frame with a single date time value if (startTimeNanosString.isPresent() && endTimeNanosString.isPresent()) { if (startTimeNanosString.equals(endTimeNanosString)) { - builder.setEffectiveTimeFrame(convertGoogleNanosToOffsetDateTime(startTimeNanosString.get())); - + return Optional.of(new TimeFrame(asOffsetDateTime(startTimeNanosString.get()))); } else { - builder.setEffectiveTimeFrame(TimeInterval.ofStartDateTimeAndEndDateTime( - convertGoogleNanosToOffsetDateTime(startTimeNanosString.get()), - convertGoogleNanosToOffsetDateTime(endTimeNanosString.get()))); + return Optional.of(new TimeFrame(ofStartDateTimeAndEndDateTime( + asOffsetDateTime(startTimeNanosString.get()), + asOffsetDateTime(endTimeNanosString.get())) + )); } - } + + return empty(); + } + + public TimeFrame getTimeFrame(JsonNode node) { + + return getOptionalTimeFrame(node) + .orElseThrow(() -> { + if (!asOptionalString(node, EPOCH_NS_START_DATE_TIME_PATH).isPresent()) { + return new MissingJsonNodeMappingException(node, EPOCH_NS_START_DATE_TIME_PATH); + } + else { + return new MissingJsonNodeMappingException(node, EPOCH_NS_END_DATE_TIME_PATH); + } + }); } /** @@ -163,5 +181,4 @@ protected String getListNodeName() { protected String getValueListNodeName() { return "value"; } - } diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitGeopositionDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitGeopositionDataPointMapper.java new file mode 100644 index 00000000..79873604 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitGeopositionDataPointMapper.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.googlefit.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.Geoposition; + +import java.util.Optional; + +import static org.openmhealth.schema.domain.omh.LengthUnit.METER; +import static org.openmhealth.schema.domain.omh.PlaneAngleUnit.DEGREE_OF_ARC; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; + + +/** + * A mapper from Google Fit "merged location samples" endpoint responses (derived:com.google.location.sample:com.google.android.gms:merge_location_samples) + * to {@link Geoposition} objects. + * + * @author Emerson Farrugia + * @see Google Fit Data Type Documentation + */ +public class GoogleFitGeopositionDataPointMapper extends GoogleFitDataPointMapper { + + @Override + protected Optional> asDataPoint(JsonNode listNode) { + + JsonNode listValueNode = asRequiredNode(listNode, "value"); + double latitude = asRequiredDouble(listValueNode.get(0), "fpVal"); + double longitude = asRequiredDouble(listValueNode.get(1), "fpVal"); + // TODO add accuracy to geoposition + Optional accuracyInM = asOptionalDouble(listValueNode.get(2), "fpVal"); + + Geoposition.Builder measureBuilder = + new Geoposition.Builder( + DEGREE_OF_ARC.newUnitValue(latitude), + DEGREE_OF_ARC.newUnitValue(longitude), + getTimeFrame(listNode)); + + if (listValueNode.size() >= 4) { + measureBuilder.setElevation(METER.newUnitValue(asRequiredDouble(listValueNode.get(3), "fpVal"))); + } + + Geoposition geoposition = measureBuilder.build(); + + Optional originDataSourceId = asOptionalString(listNode, "originDataSourceId"); + + return Optional.of(newDataPoint(geoposition, originDataSourceId.orElse(null))); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapper.java index ee715492..ba2bd9c6 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapper.java @@ -39,13 +39,18 @@ protected Optional> asDataPoint(JsonNode listNode) { JsonNode valueListNode = asRequiredNode(listNode, "value"); double heartRateValue = asRequiredDouble(valueListNode.get(0), "fpVal"); + if (heartRateValue == 0) { return Optional.empty(); } - HeartRate.Builder heartRateBuilder = new HeartRate.Builder(heartRateValue); - setEffectiveTimeFrameIfPresent(heartRateBuilder, listNode); - HeartRate heartRate = heartRateBuilder.build(); + + HeartRate.Builder measureBuilder = new HeartRate.Builder(heartRateValue); + + getOptionalTimeFrame(listNode).ifPresent(measureBuilder::setEffectiveTimeFrame); + + HeartRate heartRate = measureBuilder.build(); Optional originDataSourceId = asOptionalString(listNode, "originDataSourceId"); + return Optional.of(newDataPoint(heartRate, originDataSourceId.orElse(null))); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapper.java index fe5ce23c..59beb4f0 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapper.java @@ -68,13 +68,14 @@ protected Optional> asDataPoint(JsonNode listNode) { } String activityName = googleFitDataTypes.get((int) activityTypeId); - PhysicalActivity.Builder physicalActivityBuilder = new PhysicalActivity.Builder( - activityName); - setEffectiveTimeFrameIfPresent(physicalActivityBuilder, listNode); - PhysicalActivity physicalActivity = physicalActivityBuilder.build(); + PhysicalActivity.Builder measureBuilder = new PhysicalActivity.Builder(activityName); + + getOptionalTimeFrame(listNode).ifPresent(measureBuilder::setEffectiveTimeFrame); + + PhysicalActivity physicalActivity = measureBuilder.build(); Optional originSourceId = asOptionalString(listNode, "originDataSourceId"); - return Optional.of(newDataPoint(physicalActivity, originSourceId.orElse(null))); + return Optional.of(newDataPoint(physicalActivity, originSourceId.orElse(null))); } /** @@ -205,7 +206,7 @@ private void initializeActivityMap() { .put(110, "Deep sleep") .put(111, "REM sleep") .put(112, "Awake (during sleep cycle)"); + googleFitDataTypes = activityDataTypeBuilder.build(); } - } diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitSpeedDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitSpeedDataPointMapper.java new file mode 100644 index 00000000..92c7c61f --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitSpeedDataPointMapper.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.googlefit.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.Speed; + +import java.util.Optional; + +import static org.openmhealth.schema.domain.omh.SpeedUnit.METERS_PER_SECOND; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; + + +/** + * A mapper from Google Fit "merged speed" endpoint responses (derived:com.google.speed:com.google.android.gms:merge_speed) + * to {@link Speed} objects. + * + * @author Emerson Farrugia + * @see Google Fit Data Type Documentation + */ +public class GoogleFitSpeedDataPointMapper extends GoogleFitDataPointMapper { + + @Override + protected Optional> asDataPoint(JsonNode listNode) { + + JsonNode listValueNode = asRequiredNode(listNode, "value"); + double speedValue = asRequiredDouble(listValueNode.get(0), "fpVal"); + + Speed speed = new Speed.Builder(METERS_PER_SECOND.newUnitValue(speedValue), getTimeFrame(listNode)).build(); + + Optional originDataSourceId = asOptionalString(listNode, "originDataSourceId"); + + return Optional.of(newDataPoint(speed, originDataSourceId.orElse(null))); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapper.java index c7685452..cf4f3f4f 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapper.java @@ -18,7 +18,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.StepCount1; +import org.openmhealth.schema.domain.omh.StepCount2; import java.util.Optional; @@ -26,27 +26,30 @@ /** - * A mapper from Google Fit "merged step count delta" endpoint responses - * (derived:com.google.step_count.delta:com.google.android.gms:merge_step_deltas) to {@link StepCount1} - * objects. + * A mapper from Google Fit "merged step count delta" endpoint responses (derived:com.google.step_count.delta:com.google.android.gms:merge_step_deltas) + * to {@link StepCount2} objects. * * @author Chris Schaefbauer + * @author Emerson Farrugia * @see Google Fit Data Type Documentation */ -public class GoogleFitStepCountDataPointMapper extends GoogleFitDataPointMapper { +public class GoogleFitStepCountDataPointMapper extends GoogleFitDataPointMapper { @Override - protected Optional> asDataPoint(JsonNode listNode) { + protected Optional> asDataPoint(JsonNode listNode) { JsonNode listValueNode = asRequiredNode(listNode, "value"); long stepCountValue = asRequiredLong(listValueNode.get(0), "intVal"); + if (stepCountValue == 0) { return Optional.empty(); } - StepCount1.Builder stepCountBuilder = new StepCount1.Builder(stepCountValue); - setEffectiveTimeFrameIfPresent(stepCountBuilder, listNode); - StepCount1 stepCount = stepCountBuilder.build(); + + StepCount2 stepCount = new StepCount2.Builder(stepCountValue, getTimeFrame(listNode)) + .build(); + Optional originSourceId = asOptionalString(listNode, "originDataSourceId"); + return Optional.of(newDataPoint(stepCount, originSourceId.orElse(null))); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/ihealth/IHealthShim.java b/shim-server/src/main/java/org/openmhealth/shim/ihealth/IHealthShim.java index ca64cf8b..98b42e04 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/ihealth/IHealthShim.java +++ b/shim-server/src/main/java/org/openmhealth/shim/ihealth/IHealthShim.java @@ -22,6 +22,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import org.openmhealth.shim.*; import org.openmhealth.shim.ihealth.mapper.*; import org.slf4j.Logger; @@ -134,24 +135,29 @@ public ShimDataType[] getShimDataTypes() { public enum IHealthDataTypes implements ShimDataType { - PHYSICAL_ACTIVITY(singletonList("sport.json")), - BLOOD_GLUCOSE(singletonList("glucose.json")), - BLOOD_PRESSURE(singletonList("bp.json")), - BODY_WEIGHT(singletonList("weight.json")), - BODY_MASS_INDEX(singletonList("weight.json")), - HEART_RATE(newArrayList("bp.json", "spo2.json")), - STEP_COUNT(singletonList("activity.json")), - SLEEP_DURATION(singletonList("sleep.json")), - OXYGEN_SATURATION(singletonList("spo2.json")); + PHYSICAL_ACTIVITY("sport.json"), + BLOOD_GLUCOSE("glucose.json"), + BLOOD_PRESSURE("bp.json"), + BODY_WEIGHT("weight.json"), + BODY_MASS_INDEX("weight.json"), + HEART_RATE("bp.json", "spo2.json"), + STEP_COUNT("activity.json"), + SLEEP_DURATION("sleep.json"), + OXYGEN_SATURATION("spo2.json"); private List endPoint; - IHealthDataTypes(List endPoint) { + IHealthDataTypes(String endpoint) { - this.endPoint = endPoint; + this.endPoint = singletonList(endpoint); } - public List getEndPoint() { + IHealthDataTypes(String... endpoints) { + + this.endPoint = Lists.newArrayList(endpoints); + } + + public List getEndpoints() { return endPoint; } @@ -198,7 +204,7 @@ protected ResponseEntity getData(OAuth2RestOperations restTemp // We iterate because one of the measures (Heart rate) comes from multiple endpoints, so we submit // requests to each of these endpoints, map the responses separately and then combine them - for (String endPoint : dataType.getEndPoint()) { + for (String endpoint : dataType.getEndpoints()) { UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(clientSettings.getApiBaseUrl() + "/"); @@ -217,7 +223,7 @@ protected ResponseEntity getData(OAuth2RestOperations restTemp uriBuilder.path("/user/") .path(userId + "/") - .path(endPoint) + .path(endpoint) .queryParam("client_id", restTemplate.getResource().getClientId()) .queryParam("client_secret", restTemplate.getResource().getClientSecret()) .queryParam("start_time", startDate.toEpochSecond()) @@ -268,11 +274,11 @@ protected ResponseEntity getData(OAuth2RestOperations restTemp break; case HEART_RATE: // there are two different mappers for heart rate because the data can come from two endpoints - if (endPoint == "bp.json") { + if (endpoint.equals("bp.json")) { mapper = new IHealthBloodPressureEndpointHeartRateDataPointMapper(); break; } - else if (endPoint == "spo2.json") { + else if (endpoint.equals("spo2.json")) { mapper = new IHealthBloodOxygenEndpointHeartRateDataPointMapper(); break; } diff --git a/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthDataPointMapper.java index 08e4c686..24964939 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthDataPointMapper.java @@ -105,7 +105,6 @@ protected DataPointHeader createDataPointHeader(JsonNode listEntryNode, Measure return new DataPointHeader.Builder(UUID.randomUUID().toString(), measure.getSchemaId()) .setAcquisitionProvenance(acquisitionProvenance) .build(); - } /** @@ -117,9 +116,9 @@ protected DataPointHeader createDataPointHeader(JsonNode listEntryNode, Measure */ protected static Optional getEffectiveTimeFrameAsDateTime(JsonNode listEntryNode) { - Optional weirdSeconds = asOptionalLong(listEntryNode, "MDate"); + Optional epochSecondsInLocalTime = asOptionalLong(listEntryNode, "MDate"); - if (!weirdSeconds.isPresent()) { + if (!epochSecondsInLocalTime.isPresent()) { return Optional.empty(); } @@ -152,14 +151,14 @@ else if (asOptionalLong(listEntryNode, "TimeZone").isPresent()) { return Optional.empty(); } - return Optional.of(new TimeFrame(getDateTimeWithCorrectOffset(weirdSeconds.get(), zoneOffset))); + return Optional.of(new TimeFrame(getDateTimeWithCorrectOffset(epochSecondsInLocalTime.get(), zoneOffset))); } /** - * This method transforms a timestamp from an iHealth response (which is in the form of local time as epoch - * seconds) into an {@link OffsetDateTime} with the correct date/time and offset. The timestamps provided in - * iHealth responses are not unix epoch seconds in UTC but instead a unix epoch seconds value that is offset by the - * time zone of the data point. + * This method transforms a timestamp from an iHealth response (which is in the form of local time as epoch seconds) + * into an {@link OffsetDateTime} with the correct date/time and offset. The timestamps provided in iHealth + * responses are not unix epoch seconds in UTC but instead a unix epoch seconds value that is offset by the time + * zone of the data point. */ protected static OffsetDateTime getDateTimeWithCorrectOffset(Long localTimeAsEpochSeconds, ZoneOffset zoneOffset) { @@ -173,20 +172,19 @@ protected static OffsetDateTime getDateTimeWithCorrectOffset(Long localTimeAsEpo } /** - * @param dateTimeInUnixSecondsWithLocalTimeOffset A unix epoch timestamp in local time. + * @param epochSecondsInLocalTime A UNIX epoch timestamp in local time. * @param timeZoneString The time zone offset as a String (e.g., "+0200","-2"). * @return The date time with the correct offset. */ protected static OffsetDateTime getDateTimeAtStartOfDayWithCorrectOffset( - Long dateTimeInUnixSecondsWithLocalTimeOffset, String timeZoneString) { + Long epochSecondsInLocalTime, + String timeZoneString) { // Since the timestamps are in local time, we can use the local date time provided by rendering the timestamp // in UTC, then translating that local time to the appropriate offset. - OffsetDateTime dateTimeFromOffsetInstant = - ofInstant(ofEpochSecond(dateTimeInUnixSecondsWithLocalTimeOffset), - ZoneId.of("Z")); + OffsetDateTime dateTimeFromInstant = ofInstant(ofEpochSecond(epochSecondsInLocalTime), ZoneId.of("Z")); - return dateTimeFromOffsetInstant.toLocalDate().atStartOfDay().atOffset(ZoneOffset.of(timeZoneString)); + return dateTimeFromInstant.toLocalDate().atStartOfDay().atOffset(ZoneOffset.of(timeZoneString)); } /** @@ -207,7 +205,7 @@ protected static Optional getUserNoteIfExists(JsonNode listEntryNode) { } /** - * Sets the correct DataPointModality based on the iHealth value indicating the source of the DataPoint. + * Sets the correct modality based on the iHealth value indicating the source of the data point. * * @param dataSourceValue The iHealth value in the list entry node indicating the source of the DataPoint. * @param builder The DataPointAcquisitionProvenance builder to set the modality. diff --git a/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapper.java index 08705cb5..422860c5 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapper.java @@ -17,24 +17,29 @@ package org.openmhealth.shim.ihealth.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DurationUnitValue; +import org.openmhealth.schema.domain.omh.SleepDuration2; +import org.openmhealth.schema.domain.omh.TimeInterval; import java.time.ZoneOffset; import java.util.Optional; -import static org.openmhealth.schema.domain.omh.TimeInterval.*; +import static org.openmhealth.schema.domain.omh.DurationUnit.MINUTE; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; /** - * A mapper that translates responses from the iHealth /sleep.json endpoint into {@link SleepDuration1} + * A mapper that translates responses from the iHealth /sleep.json endpoint into {@link SleepDuration2} * measures. * * @author Chris Schaefbauer + * @author Emerson Farrugia * @see endpoint * documentation */ -public class IHealthSleepDurationDataPointMapper extends IHealthDataPointMapper { +public class IHealthSleepDurationDataPointMapper extends IHealthDataPointMapper { @Override protected String getListNodeName() { @@ -47,32 +52,24 @@ protected Optional getMeasureUnitNodeName() { } @Override - protected Optional> asDataPoint(JsonNode listEntryNode, Integer measureUnitMagicNumber) { + protected Optional> asDataPoint(JsonNode listEntryNode, Integer measureUnitMagicNumber) { - SleepDuration1.Builder sleepDurationBuilder = new SleepDuration1.Builder( - new DurationUnitValue(DurationUnit.MINUTE, asRequiredBigDecimal(listEntryNode, "HoursSlept"))); + Long effectiveStartEpochSecondsInLocalTime = asRequiredLong(listEntryNode, "StartTime"); + Long effectiveEndEpochSecondsInLocalTime = asRequiredLong(listEntryNode, "EndTime"); + ZoneOffset effectiveTimeZoneOffset = ZoneOffset.of(asRequiredString(listEntryNode, "TimeZone")); - Optional startTime = asOptionalLong(listEntryNode, "StartTime"); - Optional endTime = asOptionalLong(listEntryNode, "EndTime"); + TimeInterval effectiveTimeInterval = ofStartDateTimeAndEndDateTime( + getDateTimeWithCorrectOffset(effectiveStartEpochSecondsInLocalTime, effectiveTimeZoneOffset), + getDateTimeWithCorrectOffset(effectiveEndEpochSecondsInLocalTime, effectiveTimeZoneOffset)); - if (startTime.isPresent() && endTime.isPresent()) { - - Optional timeZone = asOptionalString(listEntryNode, "TimeZone"); - - if (timeZone.isPresent()) { - - sleepDurationBuilder.setEffectiveTimeFrame(ofStartDateTimeAndEndDateTime( - getDateTimeWithCorrectOffset(startTime.get(), ZoneOffset.of(timeZone.get())), - getDateTimeWithCorrectOffset(endTime.get(), ZoneOffset.of(timeZone.get())))); - } - } + SleepDuration2.Builder sleepDurationBuilder = new SleepDuration2.Builder( + // property is called HoursSlept but it's in minutes + new DurationUnitValue(MINUTE, asRequiredBigDecimal(listEntryNode, "HoursSlept")), + effectiveTimeInterval); getUserNoteIfExists(listEntryNode).ifPresent(sleepDurationBuilder::setUserNotes); - SleepDuration1 sleepDuration = sleepDurationBuilder.build(); - - asOptionalBigDecimal(listEntryNode, "Awaken") - .ifPresent(awaken -> sleepDuration.setAdditionalProperty("wakeup_count", awaken)); + SleepDuration2 sleepDuration = sleepDurationBuilder.build(); return Optional.of(new DataPoint<>(createDataPointHeader(listEntryNode, sleepDuration), sleepDuration)); } diff --git a/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapper.java index a5fabb7a..30e585d1 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapper.java @@ -19,7 +19,8 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.StepCount1; +import org.openmhealth.schema.domain.omh.StepCount2; +import org.openmhealth.schema.domain.omh.TimeInterval; import java.math.BigDecimal; import java.util.Optional; @@ -30,14 +31,14 @@ /** - * A mapper that translates responses from the iHealth /activity.json endpoint into {@link StepCount1} + * A mapper that translates responses from the iHealth /activity.json endpoint into {@link StepCount2} * measures. * * @author Chris Schaefbauer * @see endpoint * documentation */ -public class IHealthStepCountDataPointMapper extends IHealthDataPointMapper { +public class IHealthStepCountDataPointMapper extends IHealthDataPointMapper { @Override protected String getListNodeName() { @@ -50,7 +51,7 @@ protected Optional getMeasureUnitNodeName() { } @Override - protected Optional> asDataPoint(JsonNode listEntryNode, Integer measureUnitMagicNumber) { + protected Optional> asDataPoint(JsonNode listEntryNode, Integer measureUnitMagicNumber) { BigDecimal steps = asRequiredBigDecimal(listEntryNode, "Steps"); @@ -58,30 +59,21 @@ protected Optional> asDataPoint(JsonNode listEntryNode, In return Optional.empty(); } - StepCount1.Builder stepCountBuilder = new StepCount1.Builder(steps); + Long effectiveEpochSecondsInLocalTime = asRequiredLong(listEntryNode, "MDate"); + String effectiveTimeZoneOffset = asRequiredString(listEntryNode, "TimeZone"); - Optional dateTimeString = asOptionalLong(listEntryNode, "MDate"); + /* iHealth provides daily summaries for step counts and timestamp the data point at either the end of + the day (23:50) or at the latest time that data point was synced. */ + TimeInterval effectiveTimeInterval = ofStartDateTimeAndDuration( + getDateTimeAtStartOfDayWithCorrectOffset(effectiveEpochSecondsInLocalTime, effectiveTimeZoneOffset), + new DurationUnitValue(DAY, 1)); - if (dateTimeString.isPresent()) { + StepCount2.Builder measureBuilder = new StepCount2.Builder(steps, effectiveTimeInterval); - Optional timeZone = asOptionalString(listEntryNode, "TimeZone"); + getUserNoteIfExists(listEntryNode).ifPresent(measureBuilder::setUserNotes); - if (timeZone.isPresent()) { - - /* iHealth provides daily summaries for step counts and timestamp the datapoint at either the end of - the day (23:50) or at the latest time that datapoint was synced */ - stepCountBuilder.setEffectiveTimeFrame(ofStartDateTimeAndDuration( - getDateTimeAtStartOfDayWithCorrectOffset(dateTimeString.get(), timeZone.get()), - new DurationUnitValue(DAY, 1))); - } - } - - getUserNoteIfExists(listEntryNode).ifPresent(stepCountBuilder::setUserNotes); - - StepCount1 stepCount = stepCountBuilder.build(); + StepCount2 stepCount = measureBuilder.build(); return Optional.of(new DataPoint<>(createDataPointHeader(listEntryNode, stepCount), stepCount)); } - - } diff --git a/shim-server/src/main/java/org/openmhealth/shim/misfit/MisfitShim.java b/shim-server/src/main/java/org/openmhealth/shim/misfit/MisfitShim.java index c443f6d3..74ec4a7d 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/misfit/MisfitShim.java +++ b/shim-server/src/main/java/org/openmhealth/shim/misfit/MisfitShim.java @@ -21,10 +21,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Splitter; import org.openmhealth.shim.*; -import org.openmhealth.shim.misfit.mapper.MisfitDataPointMapper; -import org.openmhealth.shim.misfit.mapper.MisfitPhysicalActivityDataPointMapper; -import org.openmhealth.shim.misfit.mapper.MisfitSleepDurationDataPointMapper; -import org.openmhealth.shim.misfit.mapper.MisfitStepCountDataPointMapper; +import org.openmhealth.shim.misfit.mapper.*; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; @@ -71,6 +68,7 @@ public class MisfitShim extends OAuth2Shim { private MisfitPhysicalActivityDataPointMapper physicalActivityMapper = new MisfitPhysicalActivityDataPointMapper(); private MisfitSleepDurationDataPointMapper sleepDurationMapper = new MisfitSleepDurationDataPointMapper(); + private MisfitSleepEpisodeDataPointMapper sleepEpisodeMapper = new MisfitSleepEpisodeDataPointMapper(); private MisfitStepCountDataPointMapper stepCountMapper = new MisfitStepCountDataPointMapper(); @Override @@ -121,6 +119,7 @@ public enum MisfitDataTypes implements ShimDataType { PHYSICAL_ACTIVITY("activity/sessions"), SLEEP_DURATION("activity/sleeps"), + SLEEP_EPISODE("activity/sleeps"), STEP_COUNT("activity/summary"); private String endPoint; @@ -196,6 +195,9 @@ protected ResponseEntity getData(OAuth2RestOperations restTemp case SLEEP_DURATION: dataPointMapper = sleepDurationMapper; break; + case SLEEP_EPISODE: + dataPointMapper = sleepEpisodeMapper; + break; case STEP_COUNT: dataPointMapper = stepCountMapper; break; diff --git a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitPhysicalActivityDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitPhysicalActivityDataPointMapper.java index f1ff3730..4a67c2bc 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitPhysicalActivityDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitPhysicalActivityDataPointMapper.java @@ -35,7 +35,7 @@ * * @author Emerson Farrugia * @author Eric Jain - * @see API documentation + * @see API documentation */ public class MisfitPhysicalActivityDataPointMapper extends MisfitDataPointMapper { @@ -53,11 +53,8 @@ public Optional> asDataPoint(JsonNode sessionNode) { PhysicalActivity.Builder builder = new PhysicalActivity.Builder(activityName); - Optional distance = asOptionalDouble(sessionNode, "distance"); - - if (distance.isPresent()) { - builder.setDistance(new LengthUnitValue(MILE, distance.get())); - } + asOptionalDouble(sessionNode, "distance") + .ifPresent(distanceInMi -> builder.setDistance(new LengthUnitValue(MILE, distanceInMi))); Optional startDateTime = asOptionalOffsetDateTime(sessionNode, "startTime"); Optional durationInSec = asOptionalDouble(sessionNode, "duration"); diff --git a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapper.java index 4d2f1d80..5aac493c 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapper.java @@ -19,32 +19,31 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.SleepDuration1; -import org.openmhealth.shim.common.mapper.JsonNodeMappingException; +import org.openmhealth.schema.domain.omh.SleepDuration2; +import org.openmhealth.schema.domain.omh.SleepEpisode; -import java.time.Duration; import java.time.OffsetDateTime; +import java.util.List; import java.util.Optional; -import static java.lang.String.format; +import static java.util.Optional.empty; import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalBoolean; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalString; +import static org.openmhealth.shim.misfit.mapper.MisfitSleepMeasureDataPointMapper.SleepSegmentType.AWAKE; /** - * A mapper from Misfit Resource API /activity/sleeps responses to {@link SleepDuration1} objects. This mapper - * currently creates a single data point per sleep node in the response, subtracting the duration of awake segments - * from the sleep duration. It's also possible to create a single data point per sleep segment, which would help - * preserve the granularity of the original data. This mapper may be updated to return a data point per segment in the - * future. + * A mapper from Misfit Resource API /activity/sleeps responses to {@link SleepDuration2} objects. This mapper currently + * creates a single data point per sleep node in the response, subtracting the duration of awake segments from the sleep + * duration. It's also possible to create a single data point per sleep segment, which would help preserve the + * granularity of the original data, but that may be better suited to {@link SleepEpisode} measures. * * @author Emerson Farrugia - * @see API documentation + * @see API documentation */ -public class MisfitSleepDurationDataPointMapper extends MisfitDataPointMapper { - - public static final int AWAKE_SEGMENT_TYPE = 1; +public class MisfitSleepDurationDataPointMapper extends MisfitSleepMeasureDataPointMapper { @Override protected String getListNodeName() { @@ -52,55 +51,30 @@ protected String getListNodeName() { } @Override - public Optional> asDataPoint(JsonNode sleepNode) { - - // The sleep details array contains segments corresponding to whether the user was awake, sleeping lightly, - // or sleeping restfully for the duration of that segment. To discount the awake segments, we have to deduct - // their duration from the total sleep duration. - JsonNode sleepDetailsNode = asRequiredNode(sleepNode, "sleepDetails"); - - long awakeDurationInSec = 0; + public Optional> asDataPoint(JsonNode sleepNode) { - OffsetDateTime previousSegmentStartDateTime = null; - Long previousSegmentType = null; + List sleepSegments = asSleepSegments(sleepNode); - for (JsonNode sleepDetailSegmentNode : sleepDetailsNode) { + Optional effectiveStartDateTime = getSleepOnsetDateTime(sleepSegments); - OffsetDateTime startDateTime = asRequiredOffsetDateTime(sleepDetailSegmentNode, "datetime"); - Long value = asRequiredLong(sleepDetailSegmentNode, "value"); - - // if the user was awake, add it to the awake tally - if (previousSegmentType != null && previousSegmentType == AWAKE_SEGMENT_TYPE) { - awakeDurationInSec += Duration.between(previousSegmentStartDateTime, startDateTime).getSeconds(); - } - - previousSegmentStartDateTime = startDateTime; - previousSegmentType = value; + if (!effectiveStartDateTime.isPresent()) { + return empty(); } - // checking if the segment array is empty this way avoids compiler confusion later - if (previousSegmentType == null) { - throw new JsonNodeMappingException(format("The Misfit sleep node '%s' has no sleep details.", sleepNode)); - } + OffsetDateTime effectiveEndDateTime = getArisingDateTime(sleepSegments) + .orElseThrow(IllegalStateException::new); - // to calculate the duration of last segment, first determine the overall end time - OffsetDateTime startDateTime = asRequiredOffsetDateTime(sleepNode, "startTime"); - Long totalDurationInSec = asRequiredLong(sleepNode, "duration"); - OffsetDateTime endDateTime = startDateTime.plusSeconds(totalDurationInSec); - - if (previousSegmentType == AWAKE_SEGMENT_TYPE) { - awakeDurationInSec += Duration.between(previousSegmentStartDateTime, endDateTime).getSeconds(); - } - - Long sleepDurationInSec = totalDurationInSec - awakeDurationInSec; - - if (sleepDurationInSec == 0) { - return Optional.empty(); - } + long sleepDurationInSec = sleepSegments.stream() + .filter((segment) -> segment.getType() != AWAKE) + .mapToLong(SleepSegment::getDurationInSec) + .sum(); - SleepDuration1 measure = new SleepDuration1.Builder(new DurationUnitValue(SECOND, sleepDurationInSec)) - .setEffectiveTimeFrame(ofStartDateTimeAndEndDateTime(startDateTime, endDateTime)) - .build(); + SleepDuration2 measure = + new SleepDuration2.Builder( + new DurationUnitValue(SECOND, sleepDurationInSec), + ofStartDateTimeAndEndDateTime(effectiveStartDateTime.get(), effectiveEndDateTime) + ) + .build(); String externalId = asOptionalString(sleepNode, "id").orElse(null); Boolean sensed = asOptionalBoolean(sleepNode, "autoDetected").orElse(null); diff --git a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepEpisodeDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepEpisodeDataPointMapper.java new file mode 100644 index 00000000..1832c8b6 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepEpisodeDataPointMapper.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.misfit.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DurationUnitValue; +import org.openmhealth.schema.domain.omh.SleepEpisode; +import org.openmhealth.schema.domain.omh.TimeInterval; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; + +import static java.util.Optional.empty; +import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalBoolean; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalString; +import static org.openmhealth.shim.misfit.mapper.MisfitSleepMeasureDataPointMapper.SleepSegmentType.AWAKE; + + +/** + * A mapper from Misfit Resource API /activity/sleeps responses to {@link SleepEpisode} objects. + * + * @author Emerson Farrugia + * @see API documentation + */ +public class MisfitSleepEpisodeDataPointMapper extends MisfitSleepMeasureDataPointMapper { + + @Override + protected String getListNodeName() { + return "sleeps"; + } + + @Override + public Optional> asDataPoint(JsonNode sleepNode) { + + List sleepSegments = asSleepSegments(sleepNode); + + Optional effectiveTimeInterval = getEffectiveTimeInterval(sleepSegments); + + if (!effectiveTimeInterval.isPresent()) { + return empty(); + } + + SleepEpisode.Builder measureBuilder = new SleepEpisode.Builder(effectiveTimeInterval.get()); + + long sleepDurationInSec = sleepSegments.stream() + .filter((segment) -> segment.getType() != AWAKE) + .mapToLong(SleepSegment::getDurationInSec) + .sum(); + + measureBuilder.setTotalSleepTime(new DurationUnitValue(SECOND, sleepDurationInSec)); + + SleepEpisode measure = measureBuilder.build(); + + String externalId = asOptionalString(sleepNode, "id").orElse(null); + Boolean sensed = asOptionalBoolean(sleepNode, "autoDetected").orElse(null); + + return Optional.of(newDataPoint(measure, RESOURCE_API_SOURCE_NAME, externalId, sensed)); + } + + private Optional getEffectiveTimeInterval(List sleepSegments) { + + Optional effectiveStartDateTime = getSleepOnsetDateTime(sleepSegments); + + if (!effectiveStartDateTime.isPresent()) { + return empty(); + } + + OffsetDateTime effectiveEndDateTime = getArisingDateTime(sleepSegments) + .orElseThrow(IllegalStateException::new); + + return Optional.of(ofStartDateTimeAndEndDateTime(effectiveStartDateTime.get(), effectiveEndDateTime)); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepMeasureDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepMeasureDataPointMapper.java new file mode 100644 index 00000000..431e4116 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitSleepMeasureDataPointMapper.java @@ -0,0 +1,166 @@ +/* + * Copyright 2015 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.misfit.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.SchemaSupport; +import org.openmhealth.shim.common.mapper.JsonNodeMappingException; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.StreamSupport; + +import static java.lang.String.format; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; +import static org.openmhealth.shim.misfit.mapper.MisfitSleepMeasureDataPointMapper.SleepSegmentType.AWAKE; + + +/** + * @author Emerson Farrugia + */ +public abstract class MisfitSleepMeasureDataPointMapper extends MisfitDataPointMapper { + + protected List asSleepSegments(JsonNode node) { + + JsonNode sleepDetailsNode = asRequiredNode(node, "sleepDetails"); + + List sleepSegments = new ArrayList<>(); + SleepSegment previousSegment = null; + + for (JsonNode sleepDetailSegmentNode : sleepDetailsNode) { + + SleepSegment sleepSegment = new SleepSegment(); + + sleepSegment.setStartDateTime(asRequiredOffsetDateTime(sleepDetailSegmentNode, "datetime")); + sleepSegment.setType(SleepSegmentType.getByMagicNumber(asRequiredInteger(sleepDetailSegmentNode, "value"))); + + sleepSegments.add(sleepSegment); + + // finish constructing previous segment + if (previousSegment != null) { + previousSegment.setEndDateTime(sleepSegment.getStartDateTime()); + } + + previousSegment = sleepSegment; + } + + // checking if the segment array is empty this way avoids compiler confusion later + if (previousSegment == null) { + throw new JsonNodeMappingException(format("The Misfit sleep node '%s' has no sleep details.", node)); + } + + // to calculate the end time of last segment, first determine the overall end time + OffsetDateTime startDateTime = asRequiredOffsetDateTime(node, "startTime"); + Long totalDurationInSec = asRequiredLong(node, "duration"); + OffsetDateTime endDateTime = startDateTime.plusSeconds(totalDurationInSec); + + previousSegment.setEndDateTime(endDateTime); + + return sleepSegments; + } + + + class SleepSegment { + + private SleepSegmentType type; + private OffsetDateTime startDateTime; + private OffsetDateTime endDateTime; + + public SleepSegmentType getType() { + return type; + } + + public void setType(SleepSegmentType type) { + this.type = type; + } + + public OffsetDateTime getStartDateTime() { + return startDateTime; + } + + public void setStartDateTime(OffsetDateTime startDateTime) { + this.startDateTime = startDateTime; + } + + public OffsetDateTime getEndDateTime() { + return endDateTime; + } + + public void setEndDateTime(OffsetDateTime endDateTime) { + this.endDateTime = endDateTime; + } + + public long getDurationInSec() { + return Duration.between(startDateTime, endDateTime).getSeconds(); + } + } + + + enum SleepSegmentType { + + AWAKE(1), + SLEEP(2), + DEEP_SLEEP(3); + + private int magicNumber; + + SleepSegmentType(int magicNumber) { + this.magicNumber = magicNumber; + } + + /** + * @param magicNumber a magic number + * @return the constant corresponding to the magic number + */ + public static SleepSegmentType getByMagicNumber(Integer magicNumber) { + + for (SleepSegmentType constant : values()) { + if (constant.magicNumber == magicNumber) { + return constant; + } + } + + throw new IllegalArgumentException( + format("A sleep segment type with value %d doesn't exist.", magicNumber)); + } + } + + /** + * @return the start time of the first non-awake entry, if any + */ + protected Optional getSleepOnsetDateTime(List sleepSegments) { + + return sleepSegments.stream() + .filter((segment) -> segment.getType() != AWAKE) + .map(SleepSegment::getStartDateTime) + .findFirst(); + } + + /** + * @return the end time of the last non-awake entry, if any + */ + protected Optional getArisingDateTime(List sleepSegments) { + + return sleepSegments.stream() + .filter((segment) -> segment.getType() != AWAKE) + .map(SleepSegment::getEndDateTime) + .reduce((first, second) -> second); // get last + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapper.java index 11c3e5e1..7c6362cc 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapper.java @@ -19,7 +19,8 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.StepCount1; +import org.openmhealth.schema.domain.omh.StepCount2; +import org.openmhealth.schema.domain.omh.TimeInterval; import java.time.LocalDate; import java.time.OffsetDateTime; @@ -27,6 +28,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static java.time.ZoneOffset.UTC; +import static java.util.Optional.empty; import static org.openmhealth.schema.domain.omh.DurationUnit.DAY; import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredLocalDate; @@ -34,13 +36,13 @@ /** - * A mapper from Misfit Resource API /activity/summary?detail=true responses to {@link StepCount1} objects. + * A mapper from Misfit Resource API /activity/summary?detail=true responses to {@link StepCount2} objects. * * @author Emerson Farrugia * @author Eric Jain - * @see API documentation + * @see API documentation */ -public class MisfitStepCountDataPointMapper extends MisfitDataPointMapper { +public class MisfitStepCountDataPointMapper extends MisfitDataPointMapper { @Override protected String getListNodeName() { @@ -48,28 +50,27 @@ protected String getListNodeName() { } @Override - public Optional> asDataPoint(JsonNode summaryNode) { + public Optional> asDataPoint(JsonNode summaryNode) { checkNotNull(summaryNode); Long stepCount = asRequiredLong(summaryNode, "steps"); if (stepCount == 0) { - return Optional.empty(); + return empty(); } - StepCount1.Builder builder = new StepCount1.Builder(stepCount); - // this property isn't listed in the table, but does appear in the second Example section where detail is true LocalDate localDate = asRequiredLocalDate(summaryNode, "date"); // FIXME fix the time zone offset once Misfit add it to the API OffsetDateTime startDateTime = localDate.atStartOfDay().atOffset(UTC); - DurationUnitValue durationUnitValue = new DurationUnitValue(DAY, 1); - builder.setEffectiveTimeFrame(ofStartDateTimeAndDuration(startDateTime, durationUnitValue)); - StepCount1 measure = builder.build(); + TimeInterval effectiveTimeInterval = ofStartDateTimeAndDuration(startDateTime, durationUnitValue); + + StepCount2 measure = new StepCount2.Builder(stepCount, effectiveTimeInterval) + .build(); return Optional.of(newDataPoint(measure, RESOURCE_API_SOURCE_NAME, null, null)); } diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/Moves.md b/shim-server/src/main/java/org/openmhealth/shim/moves/Moves.md index 05d780ad..e3a2c018 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/moves/Moves.md +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/Moves.md @@ -20,6 +20,7 @@ supports ETag header: yes - protocol: OAuth 2.0 - https://dev.moves-app.com/docs/authentication - authorization url: https://api.moves-app.com/oauth/v1/authorize?response_type=code&client_id=&scope= +- also supports non-browser authorization for mobile apps - supports refresh token: yes # pagination @@ -38,11 +39,49 @@ supports ETag header: yes # endpoints -steps -- raw: summaries (/user/summary/daily) +The storyline, activities, and summaries endpoints are similar. Storyline contains a superset of activities +data (adds track points and locations), and activities contains a superset of summary data (adds segments). + +## get daily storyline + +- endpoint: /user/storyline/daily +- required scopes: activity, location +- reference: https://dev.moves-app.com/docs/api_storyline +- limited to 7 day ranges + +# measures +- calories-burned: not yet mapped +- step-count: mapped +- geo-position: not yet mapped +- physical-activity: mapped + +## get daily activities + +- endpoint: /user/activities/daily +- required scopes: activity +- reference: https://dev.moves-app.com/docs/api_activities +- probably limited to 31 days, not explicitly mentioned + +# measures +- calories-burned: use storyline instead +- step-count: use storyline instead +- physical-activity: use storyline instead + +## get daily summaries + +- endpoint: /user/summary/daily +- required scopes: activity +- reference: https://dev.moves-app.com/docs/api_activities +- limited to 31 days + +# measures +- calories-burned: use storyline instead +- step-count: use storyline instead +- physical-activity: use storyline instead + + + -activity -- raw: storyline (/user/storyline/daily) # notifications diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/MovesClientSettings.java b/shim-server/src/main/java/org/openmhealth/shim/moves/MovesClientSettings.java index 5702f565..c3067db7 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/moves/MovesClientSettings.java +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/MovesClientSettings.java @@ -37,6 +37,8 @@ public class MovesClientSettings extends OAuth2ClientSettings { "location" ); + private boolean authorizationInitiatedFromBrowser = true; + @Override public List getScopes() { @@ -47,4 +49,12 @@ public void setScopes(List scopes) { this.scopes = scopes; } + + public boolean isAuthorizationInitiatedFromBrowser() { + return authorizationInitiatedFromBrowser; + } + + public void setAuthorizationInitiatedFromBrowser(boolean authorizationInitiatedFromBrowser) { + this.authorizationInitiatedFromBrowser = authorizationInitiatedFromBrowser; + } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/MovesShim.java b/shim-server/src/main/java/org/openmhealth/shim/moves/MovesShim.java index 220289f0..c68a6934 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/moves/MovesShim.java +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/MovesShim.java @@ -17,19 +17,16 @@ package org.openmhealth.shim.moves; -import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.base.Splitter; +import com.google.common.base.Joiner; +import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.shim.*; -import org.openmhealth.shim.moves.mapper.MovesDataPointMapper; import org.openmhealth.shim.moves.mapper.MovesPhysicalActivityDataPointMapper; import org.openmhealth.shim.moves.mapper.MovesStepCountDataPointMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.OAuth2RestOperations; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; @@ -39,16 +36,15 @@ import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.util.UriComponentsBuilder; -import java.time.Duration; -import java.time.OffsetDateTime; +import java.time.LocalDate; +import java.time.Period; +import java.util.List; import java.util.Map; -import static java.util.Collections.singletonList; import static org.springframework.http.ResponseEntity.ok; @@ -63,34 +59,40 @@ public class MovesShim extends OAuth2Shim { public static final String SHIM_KEY = "moves"; private static final String DATA_URL = "https://api.moves-app.com/api/1.1"; - private static final String USER_AUTHORIZATION_URL = "https://api.moves-app.com/oauth/v1/authorize"; + private static final String WEB_BASED_USER_AUTHORIZATION_URL = "https://api.moves-app.com/oauth/v1/authorize"; + private static final String APP_BASED_USER_AUTHORIZATION_URL = "moves://app/authorize"; private static final String ACCESS_TOKEN_URL = "https://api.moves-app.com/oauth/v1/access_token"; - private static final long MAX_DURATION_IN_DAYS = 31; - @Autowired private MovesClientSettings clientSettings; - private MovesStepCountDataPointMapper stepCountMapper = new MovesStepCountDataPointMapper(); private MovesPhysicalActivityDataPointMapper physicalActivityMapper = new MovesPhysicalActivityDataPointMapper(); + private MovesStepCountDataPointMapper stepCountMapper = new MovesStepCountDataPointMapper(); + @Override public String getLabel() { + return "Moves"; } @Override public String getShimKey() { + return SHIM_KEY; } @Override public String getUserAuthorizationUrl() { - return USER_AUTHORIZATION_URL; + + return clientSettings.isAuthorizationInitiatedFromBrowser() + ? WEB_BASED_USER_AUTHORIZATION_URL + : APP_BASED_USER_AUTHORIZATION_URL; } @Override public String getAccessTokenUrl() { + return ACCESS_TOKEN_URL; } @@ -102,31 +104,45 @@ protected OAuth2ClientSettings getClientSettings() { public enum MovesDataType implements ShimDataType { - STEPS("/user/summary/daily"), - ACTIVITY("/user/storyline/daily"); + PHYSICAL_ACTIVITY("/user/storyline/daily"), + STEP_COUNT("/user/storyline/daily"); - private String endPoint; + private String endpoint; + private int maximumRetrievalPeriodInDays = 31; - private JsonDeserializer normalizer; + MovesDataType(String endpoint) { - MovesDataType(String endPoint) { - this.endPoint = endPoint; + this.endpoint = endpoint; + } + + MovesDataType(String endpoint, int maximumRetrievalPeriodInDays) { + + this.endpoint = endpoint; + this.maximumRetrievalPeriodInDays = maximumRetrievalPeriodInDays; } public String getEndPoint() { - return endPoint; + + return endpoint; + } + + public int getMaximumRetrievalPeriodInDays() { + + return maximumRetrievalPeriodInDays; } } public AuthorizationCodeAccessTokenProvider getAuthorizationCodeAccessTokenProvider() { + AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider(); - provider.setTokenRequestEnhancer(new MovesTokenRequestEnhancer()); + provider.setTokenRequestEnhancer(new MovesAccessTokenRequestEnhancer()); return provider; } @Override public ShimDataType[] getShimDataTypes() { + return MovesDataType.values(); } @@ -147,63 +163,56 @@ protected ResponseEntity getData( + dataTypeKey + " in shimDataRequest, cannot retrieve data."); } - OffsetDateTime now = OffsetDateTime.now(); - - OffsetDateTime startDateTime = shimDataRequest.getStartDateTime() == null ? - now.minusDays(1) : shimDataRequest.getStartDateTime(); - - OffsetDateTime endDateTime = shimDataRequest.getEndDateTime() == null ? - now.plusDays(1) : shimDataRequest.getEndDateTime(); + LocalDate today = LocalDate.now(); - if (Duration.between(startDateTime, endDateTime).toDays() > MAX_DURATION_IN_DAYS) { - endDateTime = - startDateTime.plusDays(MAX_DURATION_IN_DAYS - 1); // TODO when refactoring, break apart queries - } + LocalDate startDate = shimDataRequest.getStartDateTime() == null + ? today + : shimDataRequest.getStartDateTime().toLocalDate(); - UriComponentsBuilder uriBuilder = UriComponentsBuilder - .fromUriString(DATA_URL); + LocalDate endDate = shimDataRequest.getEndDateTime() == null + ? today + : shimDataRequest.getEndDateTime().toLocalDate(); - for (String pathSegment : Splitter.on("/").split(movesDataType.getEndPoint())) { - uriBuilder.pathSegment(pathSegment); + if (Period.between(startDate, endDate).getDays() > movesDataType.getMaximumRetrievalPeriodInDays()) { + endDate = startDate.plusDays(movesDataType.getMaximumRetrievalPeriodInDays()); } - uriBuilder - .queryParam("from", startDateTime.toLocalDate()) - .queryParam("to", endDateTime.toLocalDate()) - .queryParam("trackPoints", false); - - HttpHeaders headers = new HttpHeaders(); - headers.add("Authorization", "Bearer " + restTemplate.getAccessToken().getValue()); - HttpEntity entity = new HttpEntity("parameters", headers); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(DATA_URL) + .path(movesDataType.getEndPoint()) + .queryParam("from", startDate) + .queryParam("to", endDate) + .queryParam("trackPoints", false); // TODO make dynamic ResponseEntity responseEntity; + try { - responseEntity = - restTemplate.exchange(uriBuilder.build().encode().toUri(), HttpMethod.GET, entity, JsonNode.class); + responseEntity = restTemplate.getForEntity(uriBuilder.build().encode().toUri(), JsonNode.class); } catch (HttpClientErrorException | HttpServerErrorException e) { - // FIXME figure out how to handle this + // TODO figure out how to handle this logger.error("A request for Moves data failed.", e); throw e; } - if (shimDataRequest.getNormalize()) { - MovesDataPointMapper dataPointMapper; + List> dataPoints; + + if (shimDataRequest.getNormalize()) { switch (movesDataType) { - case STEPS: - dataPointMapper = stepCountMapper; + case PHYSICAL_ACTIVITY: + dataPoints = physicalActivityMapper.asDataPoints(responseEntity.getBody()); break; - case ACTIVITY: - dataPointMapper = physicalActivityMapper; + + case STEP_COUNT: + dataPoints = stepCountMapper.asDataPoints(responseEntity.getBody()); break; + default: throw new UnsupportedOperationException(); } - return ok().body(ShimDataResponse.result(SHIM_KEY, - dataPointMapper.asDataPoints(singletonList(responseEntity.getBody())))); + return ok().body(ShimDataResponse.result(SHIM_KEY, dataPoints)); } else { return ok().body(ShimDataResponse.result(SHIM_KEY, responseEntity.getBody())); @@ -211,32 +220,31 @@ protected ResponseEntity getData( } @Override - protected String getAuthorizationUrl(UserRedirectRequiredException exception, Map addlParameters) { + protected String getAuthorizationUrl( + UserRedirectRequiredException exception, + Map additionalParameters) { final OAuth2ProtectedResourceDetails resource = getResource(); UriComponentsBuilder uriBuilder = UriComponentsBuilder .fromUriString(exception.getRedirectUri()) - .queryParam("state", exception.getStateKey()) - .queryParam("client_id", resource.getClientId()) .queryParam("response_type", "code") - .queryParam("access_type", "offline") - .queryParam("approval_prompt", "force") - .queryParam("scope", StringUtils.collectionToDelimitedString(resource.getScope(), " ")) - .queryParam("redirect_uri", getDefaultRedirectUrl()); + .queryParam("client_id", resource.getClientId()) + .queryParam("redirect_uri", getDefaultRedirectUrl()) + .queryParam("scope", Joiner.on(" ").join(resource.getScope())) + .queryParam("state", exception.getStateKey()); return uriBuilder.build().encode().toUriString(); } - /** - * Adds required parameters to authorization token requests. - */ - private class MovesTokenRequestEnhancer implements RequestEnhancer { + private class MovesAccessTokenRequestEnhancer implements RequestEnhancer { @Override - public void enhance(AccessTokenRequest request, + public void enhance( + AccessTokenRequest request, OAuth2ProtectedResourceDetails resource, - MultiValueMap form, HttpHeaders headers) { + MultiValueMap form, + HttpHeaders headers) { form.set("client_id", resource.getClientId()); form.set("client_secret", resource.getClientSecret()); diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesActivityNodeDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesActivityNodeDataPointMapper.java new file mode 100644 index 00000000..561f0717 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesActivityNodeDataPointMapper.java @@ -0,0 +1,93 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.moves.mapper; + + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.SchemaSupport; +import org.openmhealth.schema.domain.omh.TimeFrame; +import org.openmhealth.shim.OptionalStreamSupport; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.openmhealth.shim.OptionalStreamSupport.asStream; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalNode; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredString; + + +/** + * A mapper from activity nodes in Moves Resource API /user/storyline/daily responses to data points. + * + * @author Emerson Farrugia + * @author Jared Sieling + * @see API documentation + */ +public abstract class MovesActivityNodeDataPointMapper extends MovesDataPointMapper { + + @Override + public List> asDataPoints(List responseNodes) { + + checkNotNull(responseNodes); + checkArgument(responseNodes.size() == 1, "A single response node is allowed per call."); + + return StreamSupport.stream(responseNodes.get(0).spliterator(), false) + .flatMap(dayNode -> asStream(asOptionalNode(dayNode, "segments"))) + .flatMap(segmentsNode -> StreamSupport.stream(segmentsNode.spliterator(), false)) + .flatMap(segmentNode -> asStream(asOptionalNode(segmentNode, "activities"))) + .flatMap(activitiesNode -> StreamSupport.stream(activitiesNode.spliterator(), false)) + .map(this::asDataPoint) + .flatMap(OptionalStreamSupport::asStream) + .collect(Collectors.toList()); + } + + /** + * Creates a data point. + * + * @param node a node containing all the information required to build the data point + * @return a data point + */ + protected Optional> asDataPoint(JsonNode node) { + + Optional measure = newMeasure(node); + + return measure.map(m -> asDataPoint(node, m, newExternalId(node))); + } + + /** + * @param node a node containing all the information required to build the measure + * @return a measure + */ + protected abstract Optional newMeasure(JsonNode node); + + /** + * @param node a node containing all the information required to construct a unique identifier + * @return a unique identifier for this node + */ + private String newExternalId(JsonNode node) { + + String qualifier = asRequiredString(node, "activity"); + TimeFrame timeFrame = getTimeFrame(node).orElseThrow(IllegalStateException::new); + + return String.format("%s-%d", qualifier, timeFrame.getTimeInterval().getStartDateTime().toEpochSecond()); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesDataPointMapper.java index eabbd5ee..3f0801fd 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesDataPointMapper.java @@ -1,104 +1,102 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.moves.mapper; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.collect.Lists; import org.openmhealth.schema.domain.omh.*; import org.openmhealth.shim.common.mapper.JsonNodeDataPointMapper; -import javax.annotation.Nullable; -import java.time.LocalDateTime; import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.List; +import java.time.format.DateTimeFormatter; import java.util.Optional; -import java.util.UUID; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalLocalDateTime; +import static java.util.Optional.empty; +import static java.util.UUID.randomUUID; +import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; +import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalBoolean; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalOffsetDateTime; + /** * The base class for mappers that translate Moves API responses to data points. * - * @author Jared Sieling. + * @author Jared Sieling + * @author Emerson Farrugia */ public abstract class MovesDataPointMapper implements JsonNodeDataPointMapper { public static final String RESOURCE_API_SOURCE_NAME = "Moves Resource API"; - @Override - public List> asDataPoints(List responseNodes) { + protected static final DateTimeFormatter OFFSET_DATE_TIME_FORMATTER = + DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssx"); - checkNotNull(responseNodes); - checkArgument(responseNodes.size() == 1, "A single response node is allowed per call."); - JsonNode listNode = responseNodes.get(0); + /** + * @param node a node containing optional "startTime" and "endTime" fields + * @return the equivalent time frame, if any + */ + public Optional getTimeFrame(JsonNode node) { + + Optional startDateTime = + asOptionalOffsetDateTime(node, "startTime", OFFSET_DATE_TIME_FORMATTER); - List> dataPoints = Lists.newArrayList(); + Optional endDateTime = asOptionalOffsetDateTime(node, "endTime", OFFSET_DATE_TIME_FORMATTER); - for (JsonNode listEntryNode : listNode) { - asDataPoint(listEntryNode).ifPresent(dataPoints::add); + if (!startDateTime.isPresent() || !endDateTime.isPresent()) { + return empty(); } - return dataPoints; + return Optional.of(new TimeFrame(ofStartDateTimeAndEndDateTime(startDateTime.get(), endDateTime.get()))); } - /** - * Creates a data point. - * - * @param measure the measure to set as the body of the data point - * @param externalId the identifier of the measure as recorded by the data provider - * @param the measure type - * @return a data point + /** + * @param node a node containing an optional "manual" field + * @return the equivalent modality, if any */ - protected DataPoint newDataPoint(T measure, @Nullable Long externalId) { - - DataPointAcquisitionProvenance acquisitionProvenance = - new DataPointAcquisitionProvenance.Builder(RESOURCE_API_SOURCE_NAME).build(); - - if (externalId != null) { - acquisitionProvenance.setAdditionalProperty("external_id", externalId); - } + public Optional getModality(JsonNode node) { - DataPointHeader header = new DataPointHeader.Builder(UUID.randomUUID().toString(), measure.getSchemaId()) - .setAcquisitionProvenance(acquisitionProvenance).build(); - - return new DataPoint<>(header, measure); + return asOptionalBoolean(node, "manual") + .map(manual -> manual ? SELF_REPORTED : SENSED); } /** - * FIXME this is copy-pasted from Fitbit, and it's not clear why this would apply - * @param node a JSON node containing date and time properties - * @return the equivalent OffsetDateTime + * Creates a data point. + * + * @param node a node containing all the information required to build the data point + * @return a data point */ - protected Optional combineDateTimeAndTimezone(JsonNode node) { + protected DataPoint asDataPoint(JsonNode node, T measure, String externalId) { - Optional dateTime = asOptionalLocalDateTime(node, "date", "time"); - Optional offsetDateTime = null; + DataPointAcquisitionProvenance.Builder acquisitionProvenanceBuilder = + new DataPointAcquisitionProvenance.Builder(RESOURCE_API_SOURCE_NAME); - if (dateTime.isPresent()) { - offsetDateTime = Optional.of(OffsetDateTime.of(dateTime.get(), ZoneOffset.UTC)); - } + Optional modality = getModality(node); + modality.ifPresent(acquisitionProvenanceBuilder::setModality); - return offsetDateTime; - } + DataPointAcquisitionProvenance acquisitionProvenance = acquisitionProvenanceBuilder.build(); - /** - * FIXME this is copy-pasted from Fitbit, and it's not clear why this would apply - * Transforms a {@link LocalDateTime} object into an {@link OffsetDateTime} object with a UTC time zone - * - * @param dateTime local date and time for the Moves response JSON node - * @return the date and time based on the input dateTime parameter - */ - protected OffsetDateTime combineDateTimeAndTimezone(LocalDateTime dateTime) { + acquisitionProvenance.setAdditionalProperty("external_id", externalId); - return OffsetDateTime.of(dateTime, ZoneOffset.UTC); - } + DataPointHeader header = new DataPointHeader.Builder(randomUUID().toString(), measure.getSchemaId()) + .setAcquisitionProvenance(acquisitionProvenance) + .build(); - /** - * Maps a JSON response node from the Moves API into a data point. - * - * @return the data point - */ - protected abstract Optional> asDataPoint(JsonNode node); + return new DataPoint<>(header, measure); + } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapper.java index c6ff3593..d1943642 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapper.java @@ -1,90 +1,66 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.moves.mapper; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.collect.Lists; -import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.DurationUnitValue; +import org.openmhealth.schema.domain.omh.KcalUnitValue; import org.openmhealth.schema.domain.omh.LengthUnitValue; import org.openmhealth.schema.domain.omh.PhysicalActivity; +import org.openmhealth.schema.domain.omh.TimeFrame; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; +import static java.util.Optional.empty; +import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; import static org.openmhealth.schema.domain.omh.LengthUnit.METER; -import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalDouble; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredString; + /** - * A mapper that translates responses from the Moves Resource API /user/storyline/daily endpoint into {@link - * PhysicalActivity} data points. + * A mapper that translates responses from the Moves Resource API /user/storyline/daily endpoint into + * {@link PhysicalActivity} data points. * + * @author Emerson Farrugia * @author Jared Sieling * @see API documentation */ -public class MovesPhysicalActivityDataPointMapper extends MovesDataPointMapper{ +public class MovesPhysicalActivityDataPointMapper extends MovesActivityNodeDataPointMapper { - /** - * Override because the the day-to-dataPoint relationship isn't one-to-one. - */ @Override - public List> asDataPoints(List responseNodes) { - - checkNotNull(responseNodes); - checkArgument(responseNodes.size() == 1, "A single response node is allowed per call."); - - JsonNode listNode = responseNodes.get(0); - - List> dataPoints = Lists.newArrayList(); - - for (JsonNode listEntryNode : listNode) { - JsonNode segments = asRequiredNode(listEntryNode, "segments"); + protected Optional newMeasure(JsonNode node) { - // Filter out segments that are of type 'place' or activity 'transport'. - for (JsonNode segment : segments) { - if(segment.get("type").asText().equals("move")) { - JsonNode activities = asRequiredNode(segment, "activities"); + Optional timeFrame = getTimeFrame(node); - for (JsonNode activity : activities) { - if(!activity.get("group").asText().equals("transport")) { - asDataPoint(activity).ifPresent(dataPoints::add); - } - } - } - } + if (!timeFrame.isPresent()) { + return empty(); } - return dataPoints; - } - - @Override - protected Optional> asDataPoint(JsonNode node) { - String activityName = asRequiredString(node, "activity"); - PhysicalActivity.Builder builder = new PhysicalActivity.Builder(activityName); - Optional distance = asOptionalDouble(node, "distance"); - - distance.ifPresent(aDouble -> builder.setDistance(new LengthUnitValue(METER, aDouble))); - - // TODO update JSON utilities - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssx"); - OffsetDateTime dateTime = OffsetDateTime.parse(node.get("startTime").asText(), formatter); - Optional startDateTime = Optional.ofNullable(dateTime); - - Optional durationInSec = asOptionalDouble(node, "duration"); + PhysicalActivity.Builder builder = new PhysicalActivity.Builder(activityName); + builder.setEffectiveTimeFrame(timeFrame.get()); - if (startDateTime.isPresent() && durationInSec.isPresent()) { - DurationUnitValue durationUnitValue = new DurationUnitValue(SECOND, durationInSec.get()); - builder.setEffectiveTimeFrame(ofStartDateTimeAndDuration(startDateTime.get(), durationUnitValue)); - } + asOptionalDouble(node, "distance") + .ifPresent(distanceInM -> builder.setDistance(new LengthUnitValue(METER, distanceInM))); - PhysicalActivity measure = builder.build(); + asOptionalDouble(node, "calories") + .ifPresent(calories -> builder.setCaloriesBurned(new KcalUnitValue(KILOCALORIE, calories))); - return Optional.of(newDataPoint(measure, null)); + return Optional.of(builder.build()); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesSegmentNodeDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesSegmentNodeDataPointMapper.java new file mode 100644 index 00000000..9345f4bf --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesSegmentNodeDataPointMapper.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.moves.mapper; + + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.SchemaSupport; +import org.openmhealth.schema.domain.omh.TimeFrame; +import org.openmhealth.shim.OptionalStreamSupport; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.openmhealth.shim.OptionalStreamSupport.asStream; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalNode; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredString; + + +/** + * A mapper from segment nodes in Moves Resource API /user/storyline/daily responses to data points. + * + * @author Emerson Farrugia + * @author Jared Sieling + * @see API documentation + */ +public abstract class MovesSegmentNodeDataPointMapper extends MovesDataPointMapper { + + @Override + public List> asDataPoints(List responseNodes) { + + checkNotNull(responseNodes); + checkArgument(responseNodes.size() == 1, "A single response node is allowed per call."); + + return StreamSupport.stream(responseNodes.get(0).spliterator(), false) + .flatMap(dayNode -> asStream(asOptionalNode(dayNode, "segments"))) + .flatMap(segmentsNode -> StreamSupport.stream(segmentsNode.spliterator(), false)) + .map(this::asDataPoint) + .flatMap(OptionalStreamSupport::asStream) + .collect(Collectors.toList()); + } + + /** + * Creates a data point. + * + * @param node a node containing all the information required to build the data point + * @return a data point + */ + protected Optional> asDataPoint(JsonNode node) { + + Optional measure = newMeasure(node); + + return measure.map(m -> asDataPoint(node, m, newExternalId(node))); + } + + /** + * @param node a node containing all the information required to build the measure + * @return a measure + */ + protected abstract Optional newMeasure(JsonNode node); + + /** + * @param node a node containing all the information required to construct a unique identifier + * @return a unique identifier for this node + */ + private String newExternalId(JsonNode node) { + + String qualifier = asRequiredString(node, "type"); + TimeFrame timeFrame = getTimeFrame(node).orElseThrow(IllegalStateException::new); + + return String.format("%s-%d", qualifier, timeFrame.getTimeInterval().getStartDateTime().toEpochSecond()); + } +} \ No newline at end of file diff --git a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapper.java index bc9262fe..7c7bcf46 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapper.java @@ -1,58 +1,52 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.moves.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.StepCount1; -import org.openmhealth.schema.domain.omh.TimeInterval; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import org.openmhealth.schema.domain.omh.StepCount2; +import org.openmhealth.schema.domain.omh.TimeFrame; + import java.util.Optional; -import static org.openmhealth.schema.domain.omh.DurationUnit.DAY; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalLocalDate; +import static java.util.Optional.empty; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalLong; + /** - * A mapper from Moves Resource API /user/summary/daily responses to {@link StepCount1} objects. + * A mapper that translates responses from the Moves Resource API /user/storyline/daily endpoint into + * {@link StepCount2} data points. * - * @author Jared Sieling - * @see API documentation + * @author Emerson Farrugia + * @see API documentation */ -public class MovesStepCountDataPointMapper extends MovesDataPointMapper{ +public class MovesStepCountDataPointMapper extends MovesActivityNodeDataPointMapper { @Override - protected Optional> asDataPoint(JsonNode node) { - - // Sum steps from all individual activities - int stepCountValue = 0; - for(JsonNode activity : node.get("summary")){ - if(activity.has("steps")){ - stepCountValue = stepCountValue + activity.get("steps").asInt(); - } - } - - if (stepCountValue == 0) { - return Optional.empty(); - } + protected Optional newMeasure(JsonNode node) { - StepCount1.Builder builder = new StepCount1.Builder(stepCountValue); + Optional timeFrame = getTimeFrame(node); - Optional stepDate = asOptionalLocalDate(node, "date", DateTimeFormatter.BASIC_ISO_DATE); - - if (stepDate.isPresent()) { - LocalDateTime startDateTime = stepDate.get().atTime(0, 0, 0, 0); - - // FIXME the time zone handling here is suspect; if the code is going to assume UTC, the shim should be - // asking for UTC in the initial request - builder.setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndDuration(combineDateTimeAndTimezone(startDateTime), - new DurationUnitValue(DAY, 1))); + // a time frame seems to not be present for manually entered data, making it impossible to deduplicate + if (!timeFrame.isPresent()) { + return empty(); } - StepCount1 measure = builder.build(); - - return Optional.of(newDataPoint(measure, null)); + return asOptionalLong(node, "steps") + .filter(count -> count > 0) + .map(count -> new StepCount2.Builder(count, timeFrame.get()).build()); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapper.java index 887db860..11e5b466 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapper.java @@ -17,57 +17,48 @@ package org.openmhealth.shim.runkeeper.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.CaloriesBurned; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.KcalUnit; -import org.openmhealth.schema.domain.omh.KcalUnitValue; import java.util.Optional; +import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalDouble; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalString; /** * A mapper from RunKeeper HealthGraph API application/vnd.com.runkeeper.FitnessActivityFeed+json responses to {@link - * CaloriesBurned} objects. + * CaloriesBurned2} objects. * * @author Chris Schaefbauer * @author Emerson Farrugia */ -public class RunkeeperCaloriesBurnedDataPointMapper extends RunkeeperDataPointMapper { - +public class RunkeeperCaloriesBurnedDataPointMapper extends RunkeeperDataPointMapper { @Override - protected Optional> asDataPoint(JsonNode itemNode) { - - Optional caloriesBurned = getMeasure(itemNode); + protected Optional> asDataPoint(JsonNode itemNode) { - if (caloriesBurned.isPresent()) { - return Optional - .of(new DataPoint<>(getDataPointHeader(itemNode, caloriesBurned.get()), caloriesBurned.get())); - } - else { - return Optional.empty(); // return empty if there was no calories information to generate a datapoint - } + Optional caloriesBurned = newMeasure(itemNode); + // return empty if there was no calories information to generate a data point + return caloriesBurned + .map(measure -> new DataPoint<>(getDataPointHeader(itemNode, measure), measure)); } - private Optional getMeasure(JsonNode itemNode) { + private Optional newMeasure(JsonNode itemNode) { Optional calorieValue = asOptionalDouble(itemNode, "total_calories"); - if (!calorieValue.isPresent()) { // Not all activity datapoints have the "total_calories" property + + if (!calorieValue.isPresent()) { // not all activity data points have the "total_calories" property return Optional.empty(); } - CaloriesBurned.Builder caloriesBurnedBuilder = - new CaloriesBurned.Builder(new KcalUnitValue(KcalUnit.KILOCALORIE, calorieValue.get())); - setEffectiveTimeFrameIfPresent(itemNode, caloriesBurnedBuilder); + CaloriesBurned2.Builder caloriesBurnedBuilder = + new CaloriesBurned2.Builder(KILOCALORIE.newUnitValue(calorieValue.get()), getTimeFrame(itemNode)); - Optional activityType = asOptionalString(itemNode, "type"); - activityType.ifPresent(at -> caloriesBurnedBuilder.setActivityName(at)); + asOptionalString(itemNode, "type").ifPresent(caloriesBurnedBuilder::setActivityName); return Optional.of(caloriesBurnedBuilder.build()); - } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperDataPointMapper.java index 1ee638dd..31a54554 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperDataPointMapper.java @@ -102,7 +102,6 @@ protected DataPointHeader getDataPointHeader(JsonNode itemNode, Measure measure) asOptionalInteger(itemNode, "userId").ifPresent(userId -> headerBuilder.setUserId(userId.toString())); return headerBuilder.build(); - } /** @@ -129,31 +128,47 @@ public Optional getModality(JsonNode itemNode) { } return Optional.empty(); - } /** - * Sets the effective time frame property for a measure builder. - * - * @param itemNode an individual datapoint from the list of datapoints returned in the API response - * @param builder the measure builder to have the effective date property set + * @param node a JSON node optionally containing time frame properties */ - protected void setEffectiveTimeFrameIfPresent(JsonNode itemNode, Measure.Builder builder) { + protected Optional getOptionalTimeFrame(JsonNode node) { Optional localStartDateTime = - asOptionalLocalDateTime(itemNode, "start_time", DATE_TIME_FORMATTER); + asOptionalLocalDateTime(node, "start_time", DATE_TIME_FORMATTER); // RunKeeper doesn't support fractional time zones - Optional utcOffset = asOptionalInteger(itemNode, "utc_offset"); - Optional durationInS = asOptionalDouble(itemNode, "duration"); + Optional utcOffset = asOptionalInteger(node, "utc_offset"); + Optional durationInSeconds = asOptionalDouble(node, "duration"); + + if (!localStartDateTime.isPresent() || !utcOffset.isPresent() || !durationInSeconds.isPresent()) { + return Optional.empty(); + } - if (localStartDateTime.isPresent() && utcOffset.isPresent() && durationInS.isPresent()) { + return Optional.of(asTimeFrame(localStartDateTime.get(), utcOffset.get(), durationInSeconds.get())); + } - OffsetDateTime startDateTime = localStartDateTime.get().atOffset(ZoneOffset.ofHours(utcOffset.get())); - DurationUnitValue duration = new DurationUnitValue(SECOND, durationInS.get()); + private TimeFrame asTimeFrame(LocalDateTime localStartDateTime, int utcOffsetInHours, Double durationInSeconds) { - builder.setEffectiveTimeFrame(ofStartDateTimeAndDuration(startDateTime, duration)); - } + OffsetDateTime startDateTime = localStartDateTime.atOffset(ZoneOffset.ofHours(utcOffsetInHours)); + + return new TimeFrame(ofStartDateTimeAndDuration(startDateTime, SECOND.newUnitValue(durationInSeconds))); + } + + /** + * @param node a JSON node containing time frame properties + */ + protected TimeFrame getTimeFrame(JsonNode node) { + + LocalDateTime localStartDateTime = + asRequiredLocalDateTime(node, "start_time", DATE_TIME_FORMATTER); + + // RunKeeper doesn't support fractional time zones + Integer utcOffset = asRequiredInteger(node, "utc_offset"); + Double durationInSeconds = asRequiredDouble(node, "duration"); + + return asTimeFrame(localStartDateTime, utcOffset, durationInSeconds); } /** diff --git a/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperPhysicalActivityDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperPhysicalActivityDataPointMapper.java index 9ce25e72..eb4378d9 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperPhysicalActivityDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperPhysicalActivityDataPointMapper.java @@ -54,7 +54,7 @@ private PhysicalActivity getMeasure(JsonNode itemNode) { PhysicalActivity.Builder builder = new PhysicalActivity.Builder(activityName); - setEffectiveTimeFrameIfPresent(itemNode, builder); + getOptionalTimeFrame(itemNode).ifPresent(builder::setEffectiveTimeFrame); asOptionalDouble(itemNode, "total_distance") .ifPresent(distanceInM -> builder.setDistance(new LengthUnitValue(METER, distanceInM))); diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/WithingsShim.java b/shim-server/src/main/java/org/openmhealth/shim/withings/WithingsShim.java index 2d1e54f6..30a613ea 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/WithingsShim.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/WithingsShim.java @@ -30,7 +30,6 @@ import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import javax.servlet.http.HttpServletRequest; @@ -43,7 +42,6 @@ import java.util.Map; import java.util.Objects; -import static java.util.Collections.singletonList; import static org.openmhealth.shim.withings.WithingsShim.WithingsDataType.*; @@ -56,10 +54,10 @@ public class WithingsShim extends OAuth1Shim { public static final String SHIM_KEY = "withings"; - private static final String DATA_URL = "http://wbsapi.withings.net"; - private static final String REQUEST_TOKEN_URL = "https://oauth.withings.com/account/request_token"; - private static final String USER_AUTHORIZATION_URL = "https://oauth.withings.com/account/authorize"; - private static final String ACCESS_TOKEN_URL = "https://oauth.withings.com/account/access_token"; + private static final String DATA_URL = "https://api.health.nokia.com"; + private static final String REQUEST_TOKEN_URL = "https://developer.health.nokia.com/account/request_token"; + private static final String USER_AUTHORIZATION_URL = "https://developer.health.nokia.com/account/authorize"; + private static final String ACCESS_TOKEN_URL = "https://developer.health.nokia.com/account/access_token"; private static final String INTRADAY_ACTIVITY_ENDPOINT = "getintradayactivity"; @Autowired @@ -105,12 +103,14 @@ protected OAuth1ClientSettings getClientSettings() { public ShimDataType[] getShimDataTypes() { return new ShimDataType[] { - BLOOD_PRESSURE, + BLOOD_PRESSURE, // order important for trigger call BODY_HEIGHT, + BODY_TEMPERATURE, BODY_WEIGHT, CALORIES_BURNED, HEART_RATE, SLEEP_DURATION, + SLEEP_EPISODE, STEP_COUNT }; } @@ -132,10 +132,12 @@ public enum WithingsDataType implements ShimDataType { BLOOD_PRESSURE("measure", "getmeas", true), BODY_HEIGHT("measure", "getmeas", true), + BODY_TEMPERATURE("measure", "getmeas", true), BODY_WEIGHT("measure", "getmeas", true), CALORIES_BURNED("v2/measure", "getactivity", false), HEART_RATE("measure", "getmeas", true), SLEEP_DURATION("v2/sleep", "getsummary", false), + SLEEP_EPISODE("v2/sleep", "getsummary", false), STEP_COUNT("v2/measure", "getactivity", false); private String endpoint; @@ -196,50 +198,14 @@ public ShimDataResponse getData(ShimDataRequest shimDataRequest) throws ShimExce HttpEntity responseEntity = response.getEntity(); if (shimDataRequest.getNormalize()) { - WithingsDataPointMapper mapper; - - switch (withingsDataType) { - - case BLOOD_PRESSURE: - mapper = new WithingsBloodPressureDataPointMapper(); - break; - case BODY_HEIGHT: - mapper = new WithingsBodyHeightDataPointMapper(); - break; - case BODY_WEIGHT: - mapper = new WithingsBodyWeightDataPointMapper(); - break; - case CALORIES_BURNED: - if (clientSettings.isIntradayDataAvailable()) { - mapper = new WithingsIntradayCaloriesBurnedDataPointMapper(); - } - else { - mapper = new WithingsDailyCaloriesBurnedDataPointMapper(); - } - break; - case HEART_RATE: - mapper = new WithingsHeartRateDataPointMapper(); - break; - case SLEEP_DURATION: - mapper = new WithingsSleepDurationDataPointMapper(); - break; - case STEP_COUNT: - if (clientSettings.isIntradayDataAvailable()) { - mapper = new WithingsIntradayStepCountDataPointMapper(); - } - else { - mapper = new WithingsDailyStepCountDataPointMapper(); - } - break; - default: - throw new UnsupportedOperationException(); - } + + WithingsDataPointMapper dataPointMapper = getDataPointMapper(withingsDataType); InputStream content = responseEntity.getContent(); JsonNode jsonNode = objectMapper.readValue(content, JsonNode.class); - List dataPoints = mapper.asDataPoints(singletonList(jsonNode)); - return ShimDataResponse.result(WithingsShim.SHIM_KEY, - dataPoints); + List> dataPoints = dataPointMapper.asDataPoints(jsonNode); + + return ShimDataResponse.result(WithingsShim.SHIM_KEY, dataPoints); } else { return ShimDataResponse @@ -254,6 +220,50 @@ public ShimDataResponse getData(ShimDataRequest shimDataRequest) throws ShimExce } } + private WithingsDataPointMapper getDataPointMapper(WithingsDataType withingsDataType) { + + switch (withingsDataType) { + + case BLOOD_PRESSURE: + return new WithingsBloodPressureDataPointMapper(); + + case BODY_HEIGHT: + return new WithingsBodyHeightDataPointMapper(); + + case BODY_TEMPERATURE: + return new WithingsBodyTemperatureDataPointMapper(); + + case BODY_WEIGHT: + return new WithingsBodyWeightDataPointMapper(); + + case CALORIES_BURNED: + if (clientSettings.isIntradayDataAvailable()) { + return new WithingsIntradayCaloriesBurnedDataPointMapper(); + } + + return new WithingsDailyCaloriesBurnedDataPointMapper(); + + case HEART_RATE: + return new WithingsHeartRateDataPointMapper(); + + case SLEEP_DURATION: + return new WithingsSleepDurationDataPointMapper(); + + case SLEEP_EPISODE: + return new WithingsSleepEpisodeDataPointMapper(); + + case STEP_COUNT: + if (clientSettings.isIntradayDataAvailable()) { + return new WithingsIntradayStepCountDataPointMapper(); + } + + return new WithingsDailyStepCountDataPointMapper(); + + default: + throw new UnsupportedOperationException(); + } + } + URI createWithingsRequestUri(ShimDataRequest shimDataRequest, String userid, WithingsDataType withingsDataType) { @@ -269,8 +279,10 @@ URI createWithingsRequestUri(ShimDataRequest shimDataRequest, String userid, dateTimeMap.add("enddateymd", shimDataRequest.getEndDateTime().toLocalDate().toString()); } - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(DATA_URL).pathSegment( - withingsDataType.getEndpoint()); + UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder + .fromUriString(DATA_URL) + .pathSegment(withingsDataType.getEndpoint()); + String measureParameter; if (isIntradayActivityMeasure(withingsDataType)) { // intraday data uses a different endpoint @@ -279,7 +291,9 @@ URI createWithingsRequestUri(ShimDataRequest shimDataRequest, String userid, else { measureParameter = withingsDataType.getMeasureParameter(); } - uriComponentsBuilder.queryParam("action", measureParameter).queryParam("userid", userid) + uriComponentsBuilder + .queryParam("action", measureParameter) + .queryParam("userid", userid) .queryParams(dateTimeMap); // if it's a body measure @@ -297,17 +311,16 @@ URI createWithingsRequestUri(ShimDataRequest shimDataRequest, String userid, uriComponentsBuilder.queryParam("meastype", measureType.getMagicNumber()); } - uriComponentsBuilder.queryParam("category", 1); //filter out goal datapoints + uriComponentsBuilder.queryParam("category", 1); // filter out goal data points } - UriComponents uriComponents = uriComponentsBuilder.build(); - return uriComponents.toUri(); + return uriComponentsBuilder.build().toUri(); } /** - * Determines whether the request is a Withings intraday request based on the configuration - * setup and the data type from the Shim API request. This case requires a different endpoint and different time - * parameters than the standard activity endpoint. + * Determines whether the request is a Withings intraday request based on the configuration setup and the data type + * from the Shim API request. This case requires a different endpoint and different time parameters than the + * standard activity endpoint. * * @param withingsDataType the withings data type retrieved from the Shim API request */ diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/domain/WithingsBodyMeasureType.java b/shim-server/src/main/java/org/openmhealth/shim/withings/domain/WithingsBodyMeasureType.java index 4379651b..2c945a8b 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/domain/WithingsBodyMeasureType.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/domain/WithingsBodyMeasureType.java @@ -30,13 +30,11 @@ public enum WithingsBodyMeasureType { BODY_WEIGHT(1), BODY_HEIGHT(4), - // FAT_FREE_MASS(5), // TODO confirm what this means - // FAT_RATIO(6), // TODO confirm what this means - // FAT_MASS_WEIGHT(8), // TODO confirm what this means DIASTOLIC_BLOOD_PRESSURE(9), SYSTOLIC_BLOOD_PRESSURE(10), HEART_RATE(11), - OXYGEN_SATURATION(54); + OXYGEN_SATURATION(54), + BODY_TEMPERATURE(71); private int magicNumber; diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapper.java index e2b62ed8..0db4d6cd 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapper.java @@ -24,7 +24,6 @@ import java.math.BigDecimal; import java.util.Optional; -import static java.util.Optional.empty; import static org.openmhealth.schema.domain.omh.LengthUnit.METER; import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.BODY_HEIGHT; @@ -41,10 +40,6 @@ public class WithingsBodyHeightDataPointMapper extends WithingsBodyMeasureDataPo Optional value = getValueForMeasureType(measuresNode, BODY_HEIGHT); - if (!value.isPresent()) { - return empty(); - } - - return Optional.of(new BodyHeight.Builder(new LengthUnitValue(METER, value.get()))); + return value.map(heightInM -> new BodyHeight.Builder(new LengthUnitValue(METER, heightInM))); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyMeasureDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyMeasureDataPointMapper.java index 854207a3..63641cdb 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyMeasureDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyMeasureDataPointMapper.java @@ -33,6 +33,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.String.format; import static java.time.ZoneOffset.UTC; @@ -57,7 +58,7 @@ public abstract class WithingsBodyMeasureDataPointMapper exte public List> asDataPoints(List responseNodes) { checkNotNull(responseNodes); - checkNotNull(responseNodes.size() == 1, "A single response node is allowed per call."); + checkArgument(responseNodes.size() == 1, "A single response node is allowed per call."); JsonNode responseNodeBody = asRequiredNode(responseNodes.get(0), BODY_NODE_PROPERTY); List> dataPoints = Lists.newArrayList(); @@ -88,14 +89,11 @@ public List> asDataPoints(List responseNodes) { measureBuilder.setEffectiveTimeFrame(OffsetDateTime.ofInstant(dateTimeInstant, UTC)); } - Optional userComment = asOptionalString(measureGroupNode, "comment"); - if (userComment.isPresent()) { - measureBuilder.setUserNotes(userComment.get()); - } + asOptionalString(measureGroupNode, "comment").ifPresent(measureBuilder::setUserNotes); T measure = measureBuilder.build(); - Optional externalId = asOptionalLong(measureGroupNode, "grpid"); + Optional externalId = asOptionalLong(measureGroupNode, "grpid").map(Object::toString); dataPoints.add(newDataPoint(measure, externalId.orElse(null), isSensed(measureGroupNode), null)); } @@ -185,7 +183,6 @@ protected BigDecimal getValue(JsonNode measureNode) { int scale = asRequiredInteger(measureNode, "unit"); return BigDecimal.valueOf(unscaledValue, -1 * scale); - } /** diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyTemperatureDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyTemperatureDataPointMapper.java new file mode 100644 index 00000000..1df5f78a --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyTemperatureDataPointMapper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.withings.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.BodyTemperature; +import org.openmhealth.schema.domain.omh.Measure; +import org.openmhealth.schema.domain.omh.TemperatureUnitValue; + +import java.math.BigDecimal; +import java.util.Optional; + +import static org.openmhealth.schema.domain.omh.TemperatureUnit.CELSIUS; +import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.BODY_TEMPERATURE; + + +/** + * @author Emerson Farrugia + * @see Body Measures API documentation + */ +public class WithingsBodyTemperatureDataPointMapper extends WithingsBodyMeasureDataPointMapper { + + // TODO since the documentation doesn't explicitly say the temperature is in C, this needs testing + @Override + public Optional> newMeasureBuilder(JsonNode measuresNode) { + + Optional value = getValueForMeasureType(measuresNode, BODY_TEMPERATURE); + + return value + .map(temperatureInC -> new BodyTemperature.Builder(new TemperatureUnitValue(CELSIUS, temperatureInC))); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapper.java index 1ac6123b..33c80ca7 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapper.java @@ -24,7 +24,6 @@ import java.math.BigDecimal; import java.util.Optional; -import static java.util.Optional.empty; import static org.openmhealth.schema.domain.omh.MassUnit.KILOGRAM; import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.BODY_WEIGHT; @@ -41,10 +40,6 @@ public class WithingsBodyWeightDataPointMapper extends WithingsBodyMeasureDataPo Optional value = getValueForMeasureType(measuresNode, BODY_WEIGHT); - if (!value.isPresent()) { - return empty(); - } - - return Optional.of(new BodyWeight.Builder(new MassUnitValue(KILOGRAM, value.get()))); + return value.map(weightInKg -> new BodyWeight.Builder(new MassUnitValue(KILOGRAM, weightInKg))); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapper.java index 313f6a5e..942a570b 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapper.java @@ -17,85 +17,48 @@ package org.openmhealth.shim.withings.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; +import org.openmhealth.schema.domain.omh.DataPoint; -import java.time.*; import java.util.Optional; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalString; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredLong; +import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; /** - * A mapper from Withings Activity Measures endpoint responses (/measure?action=getactivity) to {@link CaloriesBurned} - * objects - *

- *

NOTE: This only captures calories that are burned from activity that is captured by a Withings device or - * application, and - * may not be an accurate representation of all the calories burned from metabolic resting or activities not - * captured.

+ * A mapper from Withings Activity Measures endpoint responses (/measure?action=getactivity) to {@link CaloriesBurned2} + * objects. * * @author Chris Schaefbauer + * @author Emerson Farrugia * @see Activity Measures API documentation */ -public class WithingsDailyCaloriesBurnedDataPointMapper extends WithingsListDataPointMapper { +public class WithingsDailyCaloriesBurnedDataPointMapper extends WithingsListDataPointMapper { + + @Override + String getListNodeName() { + return "activities"; + } /** * Maps an individual list node from the array in the Withings activity measure endpoint response into a {@link - * CaloriesBurned} data point. - *

- *

Note: the start datetime and end datetime values for the mapped {@link CaloriesBurned} {@link DataPoint} - * assume that - * the start timezone and end time zone are the same, both equal to the "timezone" property in the Withings - * response - * datapoints. However, according to Withings, the property value they provide is specifically the end datetime - * timezone.

+ * CaloriesBurned2} data point. * * @param node activity node from the array "activites" contained in the "body" of the endpoint response - * @return a {@link DataPoint} object containing a {@link CaloriesBurned} measure with the appropriate values from - * the JSON node parameter, wrapped as an {@link Optional} */ @Override - Optional> asDataPoint(JsonNode node) { + public Optional> asDataPoint(JsonNode node) { long caloriesBurnedValue = asRequiredLong(node, "calories"); - CaloriesBurned.Builder caloriesBurnedBuilder = - new CaloriesBurned.Builder(new KcalUnitValue(KcalUnit.KILOCALORIE, caloriesBurnedValue)); - - Optional dateString = asOptionalString(node, "date"); - Optional timeZoneFullName = asOptionalString(node, "timezone"); - // We assume that timezone is the same for both the startdate and enddate timestamps, even though Withings only - // provides the enddate timezone as the "timezone" property. - // TODO: Revisit once Withings can provide start_timezone and end_timezone - if (dateString.isPresent() && timeZoneFullName.isPresent()) { - LocalDateTime localStartDateTime = LocalDate.parse(dateString.get()).atStartOfDay(); - ZoneId zoneId = ZoneId.of(timeZoneFullName.get()); - ZonedDateTime zonedDateTime = ZonedDateTime.of(localStartDateTime, zoneId); - ZoneOffset offset = zonedDateTime.getOffset(); - OffsetDateTime offsetStartDateTime = OffsetDateTime.of(localStartDateTime, offset); - LocalDateTime localEndDateTime = LocalDate.parse(dateString.get()).atStartOfDay().plusDays(1); - OffsetDateTime offsetEndDateTime = OffsetDateTime.of(localEndDateTime, offset); - caloriesBurnedBuilder.setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndEndDateTime(offsetStartDateTime, - offsetEndDateTime)); - } + CaloriesBurned2.Builder caloriesBurnedBuilder = + new CaloriesBurned2.Builder(KILOCALORIE.newUnitValue(caloriesBurnedValue), getTimeFrame(node)); - Optional userComment = asOptionalString(node, "comment"); - if (userComment.isPresent()) { - caloriesBurnedBuilder.setUserNotes(userComment.get()); - } + asOptionalString(node, "comment").ifPresent(caloriesBurnedBuilder::setUserNotes); - CaloriesBurned caloriesBurned = caloriesBurnedBuilder.build(); - DataPoint caloriesBurnedDataPoint = - newDataPoint(caloriesBurned, null, true, null); + CaloriesBurned2 caloriesBurned = caloriesBurnedBuilder.build(); - return Optional.of(caloriesBurnedDataPoint); - - } - - @Override - String getListNodeName() { - return "activities"; + return Optional.of(newDataPoint(caloriesBurned, asRequiredString(node, "date"), true, null)); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapper.java index 2f94ef66..d2385c51 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapper.java @@ -18,73 +18,45 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.StepCount1; -import org.openmhealth.schema.domain.omh.TimeInterval; +import org.openmhealth.schema.domain.omh.StepCount2; -import java.time.*; import java.util.Optional; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalString; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredLong; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; /** - * A mapper from Withings Activity Measures endpoint responses (/measure?action=getactivity) to {@link StepCount1} + * A mapper from Withings Activity Measures endpoint responses (/measure?action=getactivity) to {@link StepCount2} * objects. - *

- *

Note: the start datetime and end datetime values for the mapped {@link StepCount1} {@link DataPoint} assume that - * the start timezone and end time zone are the same, both equal to the "timezone" property in the Withings response - * datapoints. However, according to Withings, the property value they provide is specifically the end datetime - * timezone.

* * @author Chris Schaefbauer + * @author Emerson Farrugia * @see Activity Measures API documentation */ -public class WithingsDailyStepCountDataPointMapper extends WithingsListDataPointMapper { +public class WithingsDailyStepCountDataPointMapper extends WithingsListDataPointMapper { + + @Override + String getListNodeName() { + return "activities"; + } /** * Maps an individual list node from the array in the Withings activity measure endpoint response into a {@link - * StepCount1} data point. + * StepCount2} data point. * * @param node activity node from the array "activites" contained in the "body" of the endpoint response - * @return a {@link DataPoint} object containing a {@link StepCount1} measure with the appropriate values from - * the JSON node parameter, wrapped as an {@link Optional} */ @Override - Optional> asDataPoint(JsonNode node) { + Optional> asDataPoint(JsonNode node) { long stepValue = asRequiredLong(node, "steps"); - StepCount1.Builder stepCountBuilder = new StepCount1.Builder(stepValue); - Optional dateString = asOptionalString(node, "date"); - Optional timeZoneFullName = asOptionalString(node, "timezone"); - // We assume that timezone is the same for both the startdate and enddate timestamps, even though Withings only - // provides the enddate timezone as the "timezone" property. - // TODO: Revisit once Withings can provide start_timezone and end_timezone - if (dateString.isPresent() && timeZoneFullName.isPresent()) { - LocalDateTime localStartDateTime = LocalDate.parse(dateString.get()).atStartOfDay(); - ZoneId zoneId = ZoneId.of(timeZoneFullName.get()); - ZonedDateTime zonedDateTime = ZonedDateTime.of(localStartDateTime, zoneId); - ZoneOffset offset = zonedDateTime.getOffset(); - OffsetDateTime offsetStartDateTime = OffsetDateTime.of(localStartDateTime, offset); - LocalDateTime localEndDateTime = LocalDate.parse(dateString.get()).atStartOfDay().plusDays(1); - OffsetDateTime offsetEndDateTime = OffsetDateTime.of(localEndDateTime, offset); - stepCountBuilder.setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndEndDateTime(offsetStartDateTime, offsetEndDateTime)); - } - Optional userComment = asOptionalString(node, "comment"); - if (userComment.isPresent()) { - stepCountBuilder.setUserNotes(userComment.get()); - } + StepCount2.Builder stepCountBuilder = new StepCount2.Builder(stepValue, getTimeFrame(node)); - StepCount1 stepCount = stepCountBuilder.build(); - DataPoint stepCountDataPoint = newDataPoint(stepCount, null, true, null); + asOptionalString(node, "comment").ifPresent(stepCountBuilder::setUserNotes); - return Optional.of(stepCountDataPoint); - } + StepCount2 stepCount = stepCountBuilder.build(); - @Override - String getListNodeName() { - return "activities"; + return Optional.of(newDataPoint(stepCount, asRequiredString(node, "date"), true, null)); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDataPointMapper.java index 8bcf1851..4bb0bc17 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDataPointMapper.java @@ -16,9 +16,16 @@ package org.openmhealth.shim.withings.mapper; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DataPointAcquisitionProvenance; +import org.openmhealth.schema.domain.omh.DataPointHeader; +import org.openmhealth.schema.domain.omh.Measure; import org.openmhealth.shim.common.mapper.JsonNodeDataPointMapper; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; + import static java.util.UUID.randomUUID; import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; @@ -27,7 +34,7 @@ /** * @author Chris Schaefbauer */ -public abstract class WithingsDataPointMapper implements JsonNodeDataPointMapper { +public abstract class WithingsDataPointMapper implements JsonNodeDataPointMapper { public final static String RESOURCE_API_SOURCE_NAME = "Withings Resource API"; protected static final String BODY_NODE_PROPERTY = "body"; @@ -38,11 +45,11 @@ public abstract class WithingsDataPointMapper implement * @param measure a measure * @param externalId the Withings identifier of the measure, if known * @param sensed a boolean indicating whether the measure was sensed by a device, if known - * @param deviceName the name of the Withings device that generated the measure, if known + * @param device the Withings device that generated the measure, if known * @return the constructed data point */ - protected DataPoint newDataPoint(T measure, Long externalId, Boolean sensed, - String deviceName) { + protected DataPoint newDataPoint(T measure, String externalId, Boolean sensed, + WithingsDevice device) { DataPointAcquisitionProvenance.Builder provenanceBuilder = new DataPointAcquisitionProvenance.Builder(RESOURCE_API_SOURCE_NAME); @@ -54,8 +61,8 @@ protected DataPoint newDataPoint(T measure, Long external // additional properties are always subject to change DataPointAcquisitionProvenance acquisitionProvenance = provenanceBuilder.build(); - if (deviceName != null) { - acquisitionProvenance.setAdditionalProperty("device_name", deviceName); + if (device != null) { + acquisitionProvenance.setAdditionalProperty("device_name", device.getDisplayName()); } if (externalId != null) { @@ -69,4 +76,9 @@ protected DataPoint newDataPoint(T measure, Long external return new DataPoint<>(header, measure); } + + protected OffsetDateTime asOffsetDateTime(long epochSeconds, String timeZoneId) { + + return Instant.ofEpochSecond(epochSeconds).atZone(ZoneId.of(timeZoneId)).toOffsetDateTime(); + } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDevice.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDevice.java new file mode 100644 index 00000000..bcb40f33 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsDevice.java @@ -0,0 +1,61 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.withings.mapper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + + +/** + * @author Chris Schaefbauer + * @author Emerson Farrugia + */ +public enum WithingsDevice { + + ACTIVITY_TRACKER(16, "Activity tracker"), + AURA(32, "Aura"); + + private int magicNumber; + private String displayName; + + private static Map map = new HashMap<>(); + + static { + for (WithingsDevice constant : WithingsDevice.values()) { + map.put(constant.magicNumber, constant); + } + } + + WithingsDevice(int magicNumber, String displayName) { + this.magicNumber = magicNumber; + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } + + /** + * @param magicNumber a magic number + * @return the constant corresponding to the magic number + */ + public static Optional findByMagicNumber(Integer magicNumber) { + + return Optional.ofNullable(map.get(magicNumber)); + } +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapper.java index d08ede5f..c122d195 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapper.java @@ -23,7 +23,6 @@ import java.math.BigDecimal; import java.util.Optional; -import static java.util.Optional.empty; import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.HEART_RATE; @@ -39,10 +38,6 @@ public class WithingsHeartRateDataPointMapper extends WithingsBodyMeasureDataPoi Optional value = getValueForMeasureType(measuresNode, HEART_RATE); - if (!value.isPresent()) { - return empty(); - } - - return Optional.of(new HeartRate.Builder(value.get())); + return value.map(HeartRate.Builder::new); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapper.java index 03d53ae5..672561d4 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapper.java @@ -16,141 +16,34 @@ package org.openmhealth.shim.withings.mapper; -import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; +import org.openmhealth.schema.domain.omh.TimeFrame; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.util.*; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; +import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; /** - * A mapper from Withings Intraday Activity endpoint responses (/measure?action=getactivity) to {@link CaloriesBurned} + * A mapper from Withings Intraday Activity endpoint responses (/measure?action=getactivity) to {@link CaloriesBurned2} * objects. *

*

This mapper handles responses from an API request that requires special permissions from Withings. This special - * activation can be requested from their API - * Documentation website

+ * activation can be requested by filling the form linked from their API Documentation website

* + * @author Emerson Farrugia * @author Chris Schaefbauer - * @see Intrday Activity Measures API - * documentation + * @see Activity Measures API documentation */ -public class WithingsIntradayCaloriesBurnedDataPointMapper extends WithingsDataPointMapper { +public class WithingsIntradayCaloriesBurnedDataPointMapper extends WithingsIntradayDataPointMapper { - /** - * Maps JSON response nodes from the intraday activities endpoint (measure?action=getintradayactivity) in the - * Withings API into a list of {@link CaloriesBurned} {@link DataPoint} objects. - * - * @param responseNodes a list of a single JSON node containing the entire response from the intraday activities - * endpoint - * @return a list of DataPoint objects of type {@link CaloriesBurned} with the appropriate values mapped from the - * input - * JSON - */ @Override - public List> asDataPoints(List responseNodes) { - - checkNotNull(responseNodes); - checkNotNull(responseNodes.size() == 1, "A single response node is allowed per call."); - - JsonNode bodyNode = asRequiredNode(responseNodes.get(0), "body"); - JsonNode seriesNode = asRequiredNode(bodyNode, "series"); - - Iterator> fieldsIterator = seriesNode.fields(); - Map nodesWithCalories = getNodesWithCalories(fieldsIterator); - List startDateTimesInUnixEpochSeconds = Lists.newArrayList(nodesWithCalories.keySet()); - - //ensure the datapoints are in order of passing time (data points that are earlier in time come before data - // points that are later) - Collections.sort(startDateTimesInUnixEpochSeconds); - ArrayList> dataPoints = Lists.newArrayList(); - for (Long startDateTime : startDateTimesInUnixEpochSeconds) { - asDataPoint(nodesWithCalories.get(startDateTime), startDateTime).ifPresent(dataPoints::add); - } - - return dataPoints; - - } - - /** - * Maps an individual list node from the array in the Withings activity measure endpoint response into a {@link - * CaloriesBurned} data point. - * - * @param nodeWithCalorie activity node from the array "activites" contained in the "body" of the endpoint response - * that has a calories field - * @return a {@link DataPoint} object containing a {@link CaloriesBurned} measure with the appropriate values from - * the JSON node parameter, wrapped as an {@link Optional} - */ - private Optional> asDataPoint(JsonNode nodeWithCalorie, - Long startDateTimeInUnixEpochSeconds) { - - Long caloriesBurnedValue = asRequiredLong(nodeWithCalorie, "calories"); - CaloriesBurned.Builder caloriesBurnedBuilder = - new CaloriesBurned.Builder(new KcalUnitValue(KcalUnit.KILOCALORIE, caloriesBurnedValue)); - - Optional duration = asOptionalLong(nodeWithCalorie, "duration"); - if (duration.isPresent()) { - OffsetDateTime offsetDateTime = - OffsetDateTime.ofInstant(Instant.ofEpochSecond(startDateTimeInUnixEpochSeconds), ZoneId.of("Z")); - caloriesBurnedBuilder.setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndDuration(offsetDateTime, new DurationUnitValue( - DurationUnit.SECOND, duration.get()))); - } - - Optional userComment = asOptionalString(nodeWithCalorie, "comment"); - if (userComment.isPresent()) { - caloriesBurnedBuilder.setUserNotes(userComment.get()); - } - - CaloriesBurned calorieBurned = caloriesBurnedBuilder.build(); - return Optional.of(newDataPoint(calorieBurned, null, true, - null)); - + public String getMeasureValuePath() { + return "calories"; } - /** - * Creates a hashmap that contains only the entries from the intraday activities dictionary that have calories - * burned counts. - * - * @param fieldsIterator an iterator of map entries containing the key-value pairs related to each intraday - * activity event - * @return a hashmap with keys as the start datetime (in unix epoch seconds) of each activity event, and values as - * the information related to the activity event starting at the key datetime - */ - private Map getNodesWithCalories(Iterator> fieldsIterator) { - - HashMap nodesWithCalories = Maps.newHashMap(); - fieldsIterator.forEachRemaining(n -> addNodesIfHasCalories(nodesWithCalories, n)); - return nodesWithCalories; - - } - - /** - * Adds a key-value entry into the nodesWithCalories hashmap if it has a calories value. - * - * @param nodesWithCalories pass by reference hashmap to which the key-value pair should be added if a calories - * value exists - * @param intradayActivityEventEntry an entry from the intraday activity series dictionary, the key is a string - * representing the state datetime for the acivity period (in unix epoch seconds) and the value is the JSON object - * holding data related to that activity - */ - private void addNodesIfHasCalories(HashMap nodesWithCalories, - Map.Entry intradayActivityEventEntry) { - - if (intradayActivityEventEntry.getValue().has("calories")) { - nodesWithCalories - .put(Long.parseLong(intradayActivityEventEntry.getKey()), intradayActivityEventEntry.getValue()); - } + @Override + public CaloriesBurned2 newMeasure(Long measureValue, TimeFrame effectiveTimeFrame) { + return new CaloriesBurned2.Builder(KILOCALORIE.newUnitValue(measureValue), effectiveTimeFrame).build(); } - - } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayDataPointMapper.java new file mode 100644 index 00000000..5e8afd86 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayDataPointMapper.java @@ -0,0 +1,117 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.withings.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Streams; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.Measure; +import org.openmhealth.schema.domain.omh.TimeFrame; +import org.openmhealth.schema.domain.omh.TimeInterval; + +import java.time.Instant; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.time.ZoneOffset.UTC; +import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredLong; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredNode; + + +/** + * A mapper from Withings Intraday Activity endpoint responses (/measure?action=getactivity) to measure + * objects. + *

+ * This mapper handles responses from an API request that requires special permissions from Withings. This special + * activation can be requested from their API + * Documentation website

+ * + * @author Emerson Farrugia + * @author Chris Schaefbauer + * @see Intrday Activity Measures API + * documentation + */ +public abstract class WithingsIntradayDataPointMapper extends WithingsDataPointMapper { + + /** + * Maps JSON response nodes from the intraday activities endpoint (measure?action=getintradayactivity) in the + * Withings API into a list of data points. + * + * @param responseNodes a list of a single JSON node containing the entire response from the intraday activities + * endpoint + */ + @Override + public List> asDataPoints(List responseNodes) { + + checkNotNull(responseNodes); + checkArgument(responseNodes.size() == 1, "A single response node is allowed per call."); + + JsonNode bodyNode = asRequiredNode(responseNodes.get(0), "body"); + JsonNode seriesNode = asRequiredNode(bodyNode, "series"); + + return Streams.stream(seriesNode.fields()) + .filter((e) -> e.getValue().hasNonNull(getMeasureValuePath())) + .map(e -> { + MeasureTuple tuple = new MeasureTuple(); + + tuple.startDateTime = Instant.ofEpochSecond(Long.valueOf(e.getKey())).atOffset(UTC); + tuple.durationInSeconds = asRequiredLong(e.getValue(), "duration"); + tuple.measureValue = asRequiredLong(e.getValue(), getMeasureValuePath()); + + return tuple; + }) + .filter(p -> p.measureValue >= 0) + .sorted() + .map(p -> newMeasure(p.measureValue, p.getTimeFrame())) + .map(m -> { + String externalId = String.valueOf( + m.getEffectiveTimeFrame().getTimeInterval().getStartDateTime().toEpochSecond()); + + return newDataPoint(m, externalId, true, null); + }) + .collect(Collectors.toList()); + } + + /** + * @return the path of the node that contains the measure value + */ + public abstract String getMeasureValuePath(); + + + class MeasureTuple implements Comparable { + + public OffsetDateTime startDateTime; + public long durationInSeconds; + public long measureValue; + + public TimeFrame getTimeFrame() { + return new TimeFrame( + TimeInterval.ofStartDateTimeAndDuration(startDateTime, SECOND.newUnitValue(durationInSeconds))); + } + + @Override + public int compareTo(MeasureTuple o) { + return this.startDateTime.compareTo(o.startDateTime); + } + } + + public abstract T newMeasure(Long measureValue, TimeFrame effectiveTimeFrame); +} diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapper.java index bb6b8c6c..e4251f30 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapper.java @@ -16,128 +16,32 @@ package org.openmhealth.shim.withings.mapper; -import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.openmhealth.schema.domain.omh.*; - -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.util.*; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; +import org.openmhealth.schema.domain.omh.StepCount2; +import org.openmhealth.schema.domain.omh.TimeFrame; /** - * A mapper from Withings Intraday Activity endpoint responses (/measure?action=getactivity) to {@link StepCount1} + * A mapper from Withings Intraday Activity endpoint responses (/measure?action=getactivity) to {@link StepCount2} * objects. *

*

This mapper handles responses from an API request that requires special permissions from Withings. This special * activation can be requested by filling the form linked from their API Documentation website

* + * @author Emerson Farrugia * @author Chris Schaefbauer * @see Activity Measures API documentation */ -public class WithingsIntradayStepCountDataPointMapper extends WithingsDataPointMapper { +public class WithingsIntradayStepCountDataPointMapper extends WithingsIntradayDataPointMapper { - /** - * Maps JSON response nodes from the intraday activities endpoint (measure?action=getintradayactivity) in the - * Withings API into a list of {@link StepCount1} {@link DataPoint} objects. - * - * @param responseNodes a list of a single JSON node containing the entire response from the intraday activities - * endpoint - * @return a list of DataPoint objects of type {@link StepCount1} with the appropriate values mapped from the input - * JSON - */ @Override - public List> asDataPoints(List responseNodes) { - - checkNotNull(responseNodes); - checkNotNull(responseNodes.size() == 1, "A single response node is allowed per call."); - - JsonNode bodyNode = asRequiredNode(responseNodes.get(0), "body"); - JsonNode seriesNode = asRequiredNode(bodyNode, "series"); - - Iterator> fieldsIterator = seriesNode.fields(); - Map nodesWithSteps = getNodesWithSteps(fieldsIterator); - List startDateTimesInUnixEpochSeconds = Lists.newArrayList(nodesWithSteps.keySet()); - - //ensure the datapoints are in order of passing time (data points that are earlier in time come before data - // points that are later) - Collections.sort(startDateTimesInUnixEpochSeconds); - ArrayList> dataPoints = Lists.newArrayList(); - for (Long startDateTime : startDateTimesInUnixEpochSeconds) { - asDataPoint(nodesWithSteps.get(startDateTime), startDateTime).ifPresent(dataPoints::add); - } - - return dataPoints; - + public String getMeasureValuePath() { + return "steps"; } - /** - * Maps an individual list node from the array in the Withings activity measure endpoint response into a {@link - * StepCount1} data point. - * - * @param nodeWithSteps activity node from the array "activites" contained in the "body" of the endpoint response - * @return a {@link DataPoint} object containing a {@link StepCount1} measure with the appropriate values from - * the JSON node parameter, wrapped as an {@link Optional} - */ - private Optional> asDataPoint(JsonNode nodeWithSteps, Long startDateTimeInUnixEpochSeconds) { - Long stepCountValue = asRequiredLong(nodeWithSteps, "steps"); - StepCount1.Builder stepCountBuilder = new StepCount1.Builder(stepCountValue); - - Optional duration = asOptionalLong(nodeWithSteps, "duration"); - if (duration.isPresent()) { - OffsetDateTime offsetDateTime = - OffsetDateTime.ofInstant(Instant.ofEpochSecond(startDateTimeInUnixEpochSeconds), ZoneId.of("Z")); - stepCountBuilder.setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndDuration(offsetDateTime, new DurationUnitValue( - DurationUnit.SECOND, duration.get()))); - } - - Optional userComment = asOptionalString(nodeWithSteps, "comment"); - if (userComment.isPresent()) { - stepCountBuilder.setUserNotes(userComment.get()); - } - - StepCount1 stepCount = stepCountBuilder.build(); - return Optional.of(newDataPoint(stepCount, null, true, null)); - } - - /** - * Creates a map that contains only the entries from the intraday activities dictionary that have step counts. - * - * @param fieldsIterator an iterator of map entries containing the key-value pairs related to each intraday - * activity - * event - * @return a map with keys as the start datetime (in unix epoch seconds) of each activity event, and values as - * the information related to the activity event starting at the key datetime - */ - private Map getNodesWithSteps(Iterator> fieldsIterator) { - HashMap nodesWithSteps = Maps.newHashMap(); - fieldsIterator.forEachRemaining(n -> addNodeIfHasSteps(nodesWithSteps, n)); - return nodesWithSteps; - } + @Override + public StepCount2 newMeasure(Long measureValue, TimeFrame effectiveTimeFrame) { - /** - * Adds a key-value entry into the nodesWithStepValue hashmap if it has a steps value. - * - * @param nodesWithStepValue pass by reference hashmap to which the key-value pair should be added if a step count - * value exists - * @param intradayActivityEventEntry an entry from the intraday activity series dictionary, the key is a string - * representing the state datetime for the acivity period (in unix epoch seconds) and the value is the JSON object - * holding data related to that activity - */ - private void addNodeIfHasSteps(HashMap nodesWithStepValue, - Map.Entry intradayActivityEventEntry) { - if (intradayActivityEventEntry.getValue().has("steps")) { - nodesWithStepValue - .put(Long.parseLong(intradayActivityEventEntry.getKey()), intradayActivityEventEntry.getValue()); - } + return new StepCount2.Builder(measureValue, effectiveTimeFrame).build(); } - - } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsListDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsListDataPointMapper.java index dcb0c65f..c88805b6 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsListDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsListDataPointMapper.java @@ -21,12 +21,20 @@ import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.Measure; import org.openmhealth.schema.domain.omh.SchemaSupport; +import org.openmhealth.schema.domain.omh.TimeFrame; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.List; import java.util.Optional; import static com.google.common.base.Preconditions.checkNotNull; +import static java.time.LocalTime.MIDNIGHT; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredNode; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredString; /** @@ -36,7 +44,7 @@ * @author Chris Schaefbauer * @author Emerson Farrugia */ -public abstract class WithingsListDataPointMapper extends WithingsDataPointMapper { +public abstract class WithingsListDataPointMapper extends WithingsDataPointMapper { /** * Maps a JSON response with individual data points contained in a JSON array to a list of {@link DataPoint} @@ -81,4 +89,17 @@ public List> asDataPoints(List responseNodes) { * @return the name of the list node used by this mapper */ abstract String getListNodeName(); + + protected TimeFrame getTimeFrame(JsonNode node) { + + LocalDate localDate = LocalDate.parse(asRequiredString(node, "date")); + + // We assume that timezone is the same for both the startdate and enddate timestamps, even though Withings only + // provides the enddate timezone as the "timezone" property. + ZoneId timeZoneId = ZoneId.of(asRequiredString(node, "timezone")); + + OffsetDateTime startDateTime = ZonedDateTime.of(localDate, MIDNIGHT, timeZoneId).toOffsetDateTime(); + + return new TimeFrame(ofStartDateTimeAndEndDateTime(startDateTime, startDateTime.plusDays(1))); + } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapper.java index cdf9adb4..d98aa3b3 100644 --- a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapper.java +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapper.java @@ -17,128 +17,63 @@ package org.openmhealth.shim.withings.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.SleepDuration2; +import org.openmhealth.schema.domain.omh.SleepEpisode; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; -import static java.time.ZoneId.of; +import static java.util.Optional.empty; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalInteger; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalLong; -import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredLong; /** - * A mapper from Withings Sleep Summary endpoint responses (/sleep?action=getsummary) to {@link SleepDuration1} + * A mapper from Withings Sleep Summary endpoint responses (/sleep?action=getsummary) to {@link SleepDuration2} * objects. * * @author Chris Schaefbauer * @see Sleep Summary API documentation */ -public class WithingsSleepDurationDataPointMapper extends WithingsListDataPointMapper { +public class WithingsSleepDurationDataPointMapper extends WithingsListDataPointMapper { + + private WithingsSleepEpisodeDataPointMapper sleepEpisodeMapper = new WithingsSleepEpisodeDataPointMapper(); + + @Override + String getListNodeName() { + return "series"; + } /** * Maps an individual list node from the array in the Withings sleep summary endpoint response into a {@link - * SleepDuration1} data point. + * SleepDuration2} data point. * * @param node activity node from the array "series" contained in the "body" of the endpoint response - * @return a {@link DataPoint} object containing a {@link SleepDuration1} measure with the appropriate values from - * the JSON node parameter, wrapped as an {@link Optional} + * @return a {@link SleepDuration2} data point */ @Override - Optional> asDataPoint(JsonNode node) { - - Long lightSleepInSeconds = asRequiredLong(node, "data.lightsleepduration"); - Long deepSleepInSeconds = asRequiredLong(node, "data.deepsleepduration"); - Long remSleepInSeconds = asRequiredLong(node, "data.remsleepduration"); + Optional> asDataPoint(JsonNode node) { - Long totalSleepInSeconds = lightSleepInSeconds + deepSleepInSeconds + remSleepInSeconds; + Optional> sleepEpisodeDataPoint = sleepEpisodeMapper.asDataPoint(node); - SleepDuration1.Builder sleepDurationBuilder = - new SleepDuration1.Builder(new DurationUnitValue(DurationUnit.SECOND, totalSleepInSeconds)); - - - Optional startDateInEpochSeconds = asOptionalLong(node, "startdate"); - Optional endDateInEpochSeconds = asOptionalLong(node, "enddate"); - - if (startDateInEpochSeconds.isPresent() && endDateInEpochSeconds.isPresent()) { - OffsetDateTime offsetStartDateTime = - OffsetDateTime.ofInstant(Instant.ofEpochSecond(startDateInEpochSeconds.get()), of("Z")); - OffsetDateTime offsetEndDateTime = - OffsetDateTime.ofInstant(Instant.ofEpochSecond(endDateInEpochSeconds.get()), of("Z")); - sleepDurationBuilder.setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndEndDateTime(offsetStartDateTime, offsetEndDateTime)); + if (!sleepEpisodeDataPoint.isPresent()) { + return empty(); } - Optional externalId = asOptionalLong(node, "id"); - Optional modelId = asOptionalLong(node, "model"); - String modelName = null; + SleepEpisode sleepEpisode = sleepEpisodeDataPoint.get().getBody(); - if (modelId.isPresent()) { - modelName = SleepDeviceTypes.valueOf(modelId.get()); - } + SleepDuration2 sleepDuration = + new SleepDuration2.Builder(sleepEpisode.getTotalSleepTime(), sleepEpisode.getEffectiveTimeFrame()) + .build(); - SleepDuration1 sleepDuration = sleepDurationBuilder.build(); - Optional wakeupCount = asOptionalLong(node, "data.wakeupcount"); - if (wakeupCount.isPresent()) { - sleepDuration.setAdditionalProperty("wakeup_count", new Integer(wakeupCount.get().intValue())); - } + sleepEpisode.getAdditionalProperties().forEach(sleepDuration::setAdditionalProperty); - // These sleep phase values are Withings platform-specific, so we pass them through as additionalProperties to - // ensure we keep relevant platform specific values. Should be interpreted according to Withings API spec - sleepDuration.setAdditionalProperty("light_sleep_duration", - new DurationUnitValue(DurationUnit.SECOND, lightSleepInSeconds)); - sleepDuration.setAdditionalProperty("deep_sleep_duration", - new DurationUnitValue(DurationUnit.SECOND, deepSleepInSeconds)); - sleepDuration.setAdditionalProperty("rem_sleep_duration", - new DurationUnitValue(DurationUnit.SECOND, remSleepInSeconds)); - - // This is an additional piece of information captured by Withings devices around sleep and should be - // interpreted according to the Withings API specification. We do not capture durationtowakeup or - // wakeupduration properties from the Withings API because it is unclear the distinction between them and we - // aim to avoid creating more ambiguity through passing through these properties - Optional timeToSleepValue = asOptionalLong(node, "data.durationtosleep"); - if (timeToSleepValue.isPresent()) { - sleepDuration.setAdditionalProperty("duration_to_sleep", - new DurationUnitValue(DurationUnit.SECOND, timeToSleepValue.get())); - } + Optional externalId = asOptionalLong(node, "id").map(Object::toString); - return Optional.of(newDataPoint(sleepDuration, externalId.orElse(null), true, modelName)); - } + WithingsDevice device = asOptionalInteger(node, "model") + .flatMap(WithingsDevice::findByMagicNumber) + .orElse(null); - @Override - String getListNodeName() { - return "series"; - } - - // TODO clean this up - public enum SleepDeviceTypes { - Pulse(16), Aura(32); - - private long deviceId; - - private static Map map = new HashMap(); - - static { - for (SleepDeviceTypes sleepDeviceTypeName : SleepDeviceTypes.values()) { - map.put(sleepDeviceTypeName.deviceId, sleepDeviceTypeName.name()); - } - } - - SleepDeviceTypes(final long deviceId) { - this.deviceId = deviceId; - } - - /** - * Returns the string device name for a device ID - * - * @param deviceId the id number for the device contained within the Withings API response datapoint - * @return common name of the device (e.g., Pulse, Aura) - */ - public static String valueOf(long deviceId) { - return map.get(deviceId); - } + return Optional.of(newDataPoint(sleepDuration, externalId.orElse(null), true, device)); } } diff --git a/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepEpisodeDataPointMapper.java b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepEpisodeDataPointMapper.java new file mode 100644 index 00000000..90d3acb5 --- /dev/null +++ b/shim-server/src/main/java/org/openmhealth/shim/withings/mapper/WithingsSleepEpisodeDataPointMapper.java @@ -0,0 +1,101 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.withings.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DurationUnitValue; +import org.openmhealth.schema.domain.omh.SleepEpisode; + +import java.time.OffsetDateTime; +import java.util.Optional; + +import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; +import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; + + +/** + * A mapper from Withings Sleep Summary endpoint responses (/sleep?action=getsummary) to {@link SleepEpisode} objects. + * + * @author Emerson Farrugia + * @author Chris Schaefbauer + * @see Sleep Summary API documentation + */ +public class WithingsSleepEpisodeDataPointMapper extends WithingsListDataPointMapper { + + @Override + String getListNodeName() { + return "series"; + } + + /** + * Maps an individual list node from the array in the Withings sleep summary endpoint response into a {@link + * SleepEpisode} data point. + * + * @param node activity node from the array "series" contained in the "body" of the endpoint response + * @return a {@link SleepEpisode} data point + */ + @Override + Optional> asDataPoint(JsonNode node) { + + Long latencyToSleepOnsetInSeconds = asOptionalLong(node, "data.durationtosleep").orElse(0L); + Long latencyToArisingInSeconds = asOptionalLong(node, "data.durationtowakeup").orElse(0L); + String timeZoneId = asRequiredString(node, "timezone"); + + OffsetDateTime effectiveStartDateTime = asOffsetDateTime(asRequiredLong(node, "startdate"), timeZoneId) + .plusSeconds(latencyToSleepOnsetInSeconds); + + OffsetDateTime effectiveEndDateTime = asOffsetDateTime(asRequiredLong(node, "enddate"), timeZoneId) + .minusSeconds(latencyToArisingInSeconds); + + SleepEpisode.Builder sleepEpisodeBuilder = + new SleepEpisode.Builder(ofStartDateTimeAndEndDateTime(effectiveStartDateTime, effectiveEndDateTime)) + .setLatencyToSleepOnset(new DurationUnitValue(SECOND, latencyToSleepOnsetInSeconds)) + .setLatencyToArising(new DurationUnitValue(SECOND, latencyToArisingInSeconds)); + + Long lightSleepDurationInSeconds = asOptionalLong(node, "data.lightsleepduration").orElse(0L); + Long deepSleepDurationInSeconds = asOptionalLong(node, "data.deepsleepduration").orElse(0L); + Long remSleepDurationInSeconds = asOptionalLong(node, "data.remsleepduration").orElse(0L); + + Long totalSleepDurationInSeconds = + lightSleepDurationInSeconds + deepSleepDurationInSeconds + remSleepDurationInSeconds; + + sleepEpisodeBuilder.setTotalSleepTime(new DurationUnitValue(SECOND, totalSleepDurationInSeconds)); + + asOptionalInteger(node, "data.wakeupcount").ifPresent(sleepEpisodeBuilder::setNumberOfAwakenings); + + SleepEpisode sleepEpisode = sleepEpisodeBuilder.build(); + + // These sleep phase values are Withings platform-specific, so we pass them through as additionalProperties to + // ensure we keep relevant platform specific values. + sleepEpisode.setAdditionalProperty("light_sleep_duration", + new DurationUnitValue(SECOND, lightSleepDurationInSeconds)); + sleepEpisode.setAdditionalProperty("deep_sleep_duration", + new DurationUnitValue(SECOND, deepSleepDurationInSeconds)); + sleepEpisode.setAdditionalProperty("rem_sleep_duration", + new DurationUnitValue(SECOND, remSleepDurationInSeconds)); + + Optional externalId = asOptionalLong(node, "id").map(Object::toString); + + WithingsDevice device = asOptionalInteger(node, "model") + .flatMap(WithingsDevice::findByMagicNumber) + .orElse(null); + + return Optional.of(newDataPoint(sleepEpisode, externalId.orElse(null), true, device)); + } +} diff --git a/shim-server/src/main/resources/application.yaml b/shim-server/src/main/resources/application.yaml index 118a60e2..64b3a13f 100644 --- a/shim-server/src/main/resources/application.yaml +++ b/shim-server/src/main/resources/application.yaml @@ -56,6 +56,8 @@ openmhealth: # moves: # client-id: "set-value-here" # client-secret: "set-value-here" + # # whether to use app-based (moves://) or browser-based (http://) authorization flows + # authorization-initiated-from-browser: true # runkeeper: # client-id: "set-value-here" # client-secret: "set-value-here" diff --git a/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepDurationDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepDurationDataPointMapperUnitTests.java index aa121b8b..9a8e0c3e 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepDurationDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepDurationDataPointMapperUnitTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.fitbit.mapper; import org.openmhealth.schema.domain.omh.DataPoint; @@ -22,6 +38,7 @@ public class FitbitSleepDurationDataPointMapperUnitTests extends FitbitSleepMeas private final FitbitSleepDurationDataPointMapper mapper = new FitbitSleepDurationDataPointMapper(); + @Override public FitbitSleepDurationDataPointMapper getMapper() { return mapper; } diff --git a/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepEpisodeDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepEpisodeDataPointMapperUnitTests.java index 3d51275b..0c4e29e3 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepEpisodeDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepEpisodeDataPointMapperUnitTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.fitbit.mapper; import org.openmhealth.schema.domain.omh.*; @@ -20,6 +36,7 @@ public class FitbitSleepEpisodeDataPointMapperUnitTests extends FitbitSleepMeasu private final FitbitSleepEpisodeDataPointMapper mapper = new FitbitSleepEpisodeDataPointMapper(); + @Override public FitbitSleepEpisodeDataPointMapper getMapper() { return mapper; } diff --git a/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapperUnitTests.java index 58531f10..ff6a77fb 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/fitbit/mapper/FitbitSleepMeasureDataPointMapperUnitTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.fitbit.mapper; import com.fasterxml.jackson.databind.JsonNode; @@ -14,6 +30,9 @@ import static org.hamcrest.Matchers.empty; +/** + * @author Emerson Farrugia + */ public abstract class FitbitSleepMeasureDataPointMapperUnitTests extends DataPointMapperUnitTests { diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/common/GoogleFitTestProperties.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/common/GoogleFitTestProperties.java index 739ba8a8..f3b48502 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/common/GoogleFitTestProperties.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/common/GoogleFitTestProperties.java @@ -18,32 +18,45 @@ import org.openmhealth.schema.domain.omh.DataPointModality; import org.openmhealth.schema.domain.omh.SchemaId; +import org.openmhealth.schema.domain.omh.TimeFrame; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import static java.util.Optional.empty; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; + /** * @author Chris Schaefbauer + * @author Emerson Farrugia */ public class GoogleFitTestProperties { - private String startDateTime; - private String endDateTime; private String sourceOriginId; - private double fpValue; - private DataPointModality modality; - private String stringValue; - private long intValue; private SchemaId bodySchemaId; + private DataPointModality modality; + private List values = new ArrayList<>(); + private OffsetDateTime effectiveStartDateTime; + private OffsetDateTime effectiveEndDateTime; - public void addFloatingPointProperty(double fpVal) { + public String getSourceOriginId() { - fpValue = fpVal; + return sourceOriginId; } - public String getSourceOriginId() { + public void setSourceOriginId(String sourceOriginId) { + this.sourceOriginId = sourceOriginId; + } - return sourceOriginId; + public SchemaId getBodySchemaId() { + return bodySchemaId; + } + + public void setBodySchemaId(SchemaId bodySchemaId) { + this.bodySchemaId = bodySchemaId; } public Optional getModality() { @@ -55,53 +68,69 @@ public void setModality(DataPointModality modality) { this.modality = modality; } - public double getFpValue() { - return fpValue; + public String getStringValue() { + return getStringValue(0); } - public Optional getEndDateTime() { - - return Optional.ofNullable(endDateTime); + public String getStringValue(int index) { + return getValue(String.class, index); } - public Optional getStartDateTime() { + public T getValue(Class clazz, int index) { + return clazz.cast(values.get(index)); + } - return Optional.ofNullable(startDateTime); + public Long getIntValue() { + return getIntValue(0); } - public String getStringValue() { - return stringValue; + public Long getIntValue(int index) { + return getValue(Long.class, index); } - public void setStringValue(String stringValue) { - this.stringValue = stringValue; + public Double getFpValue() { + return getFpValue(0); } - public void setStartDateTime(String startDateTime) { - this.startDateTime = startDateTime; + public Double getFpValue(int index) { + return getValue(Double.class, index); } - public void setEndDateTime(String endDateTime) { - this.endDateTime = endDateTime; + public GoogleFitTestProperties addValue(Object value) { + this.values.add(value); + return this; } - public void setSourceOriginId(String sourceOriginId) { - this.sourceOriginId = sourceOriginId; + public Optional getEffectiveEndDateTime() { + + return Optional.ofNullable(effectiveEndDateTime); } - public void setIntValue(long integerValue) { - this.intValue = integerValue; + public void setEffectiveEndDateTime(String effectiveEndDateTime) { + this.effectiveEndDateTime = OffsetDateTime.parse(effectiveEndDateTime); } - public long getIntValue() { - return intValue; + public Optional getEffectiveStartDateTime() { + + return Optional.ofNullable(effectiveStartDateTime); } - public SchemaId getBodySchemaId() { - return bodySchemaId; + public void setEffectiveStartDateTime(String effectiveStartDateTime) { + this.effectiveStartDateTime = OffsetDateTime.parse(effectiveStartDateTime); } - public void setBodySchemaId(SchemaId bodySchemaId) { - this.bodySchemaId = bodySchemaId; + public Optional getEffectiveTimeFrame() { + + if (getEffectiveStartDateTime().isPresent() && getEffectiveEndDateTime().isPresent()) { + return Optional.of(new TimeFrame(ofStartDateTimeAndEndDateTime( + getEffectiveStartDateTime().get(), + getEffectiveEndDateTime().get()))); + } + else if (getEffectiveStartDateTime().isPresent()) { + return Optional.of(new TimeFrame(getEffectiveStartDateTime().get())); + } + else { + return empty(); + } } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapperUnitTests.java index 4263cf1b..eeed1b93 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyHeightDataPointMapperUnitTests.java @@ -70,11 +70,11 @@ public void asDataPointsShouldReturnSelfReportedAsModalityWhenDataSourceContains @Override public void assertThatMeasureMatches(BodyHeight testMeasure, GoogleFitTestProperties testProperties) { - BodyHeight.Builder bodyHeightBuilder = + BodyHeight.Builder expectedBodyHeightBuilder = new BodyHeight.Builder(new LengthUnitValue(METER, testProperties.getFpValue())); - setExpectedEffectiveTimeFrame(bodyHeightBuilder, testProperties); + testProperties.getEffectiveTimeFrame().ifPresent(expectedBodyHeightBuilder::setEffectiveTimeFrame); - assertThat(testMeasure, equalTo(bodyHeightBuilder.build())); + assertThat(testMeasure, equalTo(expectedBodyHeightBuilder.build())); } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapperUnitTests.java index 0ad12709..9a95167e 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitBodyWeightDataPointMapperUnitTests.java @@ -80,7 +80,7 @@ public void assertThatMeasureMatches(BodyWeight testMeasure, GoogleFitTestProper BodyWeight.Builder expectedBodyWeightBuilder = new BodyWeight.Builder(new MassUnitValue(KILOGRAM, testProperties.getFpValue())); - setExpectedEffectiveTimeFrame(expectedBodyWeightBuilder, testProperties); + testProperties.getEffectiveTimeFrame().ifPresent(expectedBodyWeightBuilder::setEffectiveTimeFrame); assertThat(testMeasure, equalTo(expectedBodyWeightBuilder.build())); } diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapperUnitTests.java index 7ba74e92..4aa19458 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitCaloriesBurnedDataPointMapperUnitTests.java @@ -1,8 +1,7 @@ package org.openmhealth.shim.googlefit.mapper; -import org.openmhealth.schema.domain.omh.CaloriesBurned; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.KcalUnitValue; import org.openmhealth.shim.googlefit.common.GoogleFitTestProperties; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -13,7 +12,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; -import static org.openmhealth.schema.domain.omh.CaloriesBurned.*; +import static org.openmhealth.schema.domain.omh.CaloriesBurned2.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; @@ -21,7 +20,8 @@ /** * @author Chris Schaefbauer */ -public class GoogleFitCaloriesBurnedDataPointMapperUnitTests extends GoogleFitDataPointMapperUnitTests { +public class GoogleFitCaloriesBurnedDataPointMapperUnitTests + extends GoogleFitDataPointMapperUnitTests { private final GoogleFitCaloriesBurnedDataPointMapper mapper = new GoogleFitCaloriesBurnedDataPointMapper(); @@ -41,7 +41,7 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); assertThatDataPointMatches(dataPoints.get(0), createFloatingPointTestProperties(200.0, "2015-07-07T13:30:00Z", "2015-07-07T14:00:00Z", @@ -67,17 +67,15 @@ public void asDataPointsShouldReturnNoModalityWhenDataSourceDoesNotContainUserIn nullValue()); } - /* Helper methods */ - @Override - public void assertThatMeasureMatches(CaloriesBurned testMeasure, GoogleFitTestProperties testProperties) { - - CaloriesBurned.Builder expectedCaloriesBurnedBuilder = - new CaloriesBurned.Builder( - new KcalUnitValue(KILOCALORIE, testProperties.getFpValue())); + public void assertThatMeasureMatches(CaloriesBurned2 testMeasure, GoogleFitTestProperties testProperties) { - setExpectedEffectiveTimeFrame(expectedCaloriesBurnedBuilder, testProperties); + CaloriesBurned2 expectedCaloriesBurned = new CaloriesBurned2.Builder( + KILOCALORIE.newUnitValue(testProperties.getFpValue()), + testProperties.getEffectiveTimeFrame().get() + ) + .build(); - assertThat(testMeasure, equalTo(expectedCaloriesBurnedBuilder.build())); + assertThat(testMeasure, equalTo(expectedCaloriesBurned)); } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapperUnitTests.java index 366e613f..dc0ff6ac 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitDataPointMapperUnitTests.java @@ -1,12 +1,15 @@ package org.openmhealth.shim.googlefit.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DataPointHeader; +import org.openmhealth.schema.domain.omh.Measure; +import org.openmhealth.schema.domain.omh.SchemaId; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.openmhealth.shim.googlefit.common.GoogleFitTestProperties; import java.io.IOException; -import java.time.OffsetDateTime; +import java.util.Arrays; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -53,7 +56,8 @@ public void assertThatDataPointMatches(DataPoint dataPoint, GoogleFitTestProp if (testProperties.getModality().isPresent()) { - assertThat(dataPointHeader.getAcquisitionProvenance().getModality(), + assertThat( + dataPointHeader.getAcquisitionProvenance().getModality(), equalTo(testProperties.getModality().get())); } else { @@ -65,61 +69,60 @@ public void assertThatDataPointMatches(DataPoint dataPoint, GoogleFitTestProp /** * Creates a test properties object used to generate an expected value data point to test google fit data points * that use floating point values in their response. + * + * @deprecated use varargs instead */ + @Deprecated public GoogleFitTestProperties createFloatingPointTestProperties(double fpValue, String startDateTime, String endDateTime, String sourceOriginId, SchemaId schemaId) { - GoogleFitTestProperties testProperties = - createTestProperties(startDateTime, endDateTime, sourceOriginId, schemaId); - - testProperties.addFloatingPointProperty(fpValue); - - return testProperties; + return createTestProperties(startDateTime, endDateTime, sourceOriginId, schemaId, fpValue); } /** * Creates a test properties object used to generate an expected value data point to test google fit data points * that use integer values in their response. + * + * @deprecated use varargs instead */ + @Deprecated public GoogleFitTestProperties createIntegerTestProperties(long intValue, String startDateTime, String endDateTime, String sourceOriginId, SchemaId schemaId) { - GoogleFitTestProperties testProperties = - createTestProperties(startDateTime, endDateTime, sourceOriginId, schemaId); - - testProperties.setIntValue(intValue); - - return testProperties; + return createTestProperties(startDateTime, endDateTime, sourceOriginId, schemaId, intValue); } /** * Creates a test properties object used to generate an expected value data point to test google fit data points * that use strings to represent values. + * + * @deprecated use varargs instead */ - public GoogleFitTestProperties createStringTestProperties(String stringValue, String startDateTime, + public GoogleFitTestProperties createStringTestProperties( + String stringValue, + String startDateTime, String endDateTime, String sourceOriginId, SchemaId schemaId) { - GoogleFitTestProperties testProperties = - createTestProperties(startDateTime, endDateTime, sourceOriginId, schemaId); - - testProperties.setStringValue(stringValue); - - return testProperties; + return createTestProperties(startDateTime, endDateTime, sourceOriginId, schemaId, stringValue); } - private GoogleFitTestProperties createTestProperties(String startDateTimeString, String endDateTimeString, - String sourceOriginId, SchemaId schemaId) { + public GoogleFitTestProperties createTestProperties( + String startDateTimeString, + String endDateTimeString, + String sourceOriginId, + SchemaId schemaId, + Object... values) { GoogleFitTestProperties testProperties = new GoogleFitTestProperties(); if (startDateTimeString != null) { - testProperties.setStartDateTime(startDateTimeString); + testProperties.setEffectiveStartDateTime(startDateTimeString); } if (endDateTimeString != null) { - testProperties.setEndDateTime(endDateTimeString); + testProperties.setEffectiveEndDateTime(endDateTimeString); } if (sourceOriginId != null) { @@ -133,22 +136,8 @@ private GoogleFitTestProperties createTestProperties(String startDateTimeString, testProperties.setBodySchemaId(schemaId); - return testProperties; - } - - /** - * Sets the effective time frame for a data point builder given a {@link GoogleFitTestProperties} object. - */ - public void setExpectedEffectiveTimeFrame(T.Builder builder, GoogleFitTestProperties testProperties) { - - if (testProperties.getEndDateTime().isPresent()) { + Arrays.stream(values).forEach(testProperties::addValue); - builder.setEffectiveTimeFrame(TimeInterval.ofStartDateTimeAndEndDateTime( - OffsetDateTime.parse(testProperties.getStartDateTime().get()), - OffsetDateTime.parse(testProperties.getEndDateTime().get()))); - } - else { - builder.setEffectiveTimeFrame(OffsetDateTime.parse(testProperties.getStartDateTime().get())); - } + return testProperties; } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitGeopositionDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitGeopositionDataPointMapperUnitTests.java new file mode 100644 index 00000000..7d694c93 --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitGeopositionDataPointMapperUnitTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.googlefit.mapper; + +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.Geoposition; +import org.openmhealth.schema.domain.omh.LengthUnit; +import org.openmhealth.shim.googlefit.common.GoogleFitTestProperties; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.openmhealth.schema.domain.omh.Geoposition.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.PlaneAngleUnit.DEGREE_OF_ARC; + + +/** + * @author Emerson Farrugia + */ +public class GoogleFitGeopositionDataPointMapperUnitTests extends GoogleFitDataPointMapperUnitTests { + + private final GoogleFitGeopositionDataPointMapper mapper = new GoogleFitGeopositionDataPointMapper(); + + @BeforeClass + @Override + public void initializeResponseNode() throws IOException { + + responseNode = asJsonNode("org/openmhealth/shim/googlefit/mapper/googlefit-merge-location-samples.json"); + } + + @Test + public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { + + assertThat(mapper.asDataPoints(responseNode).size(), equalTo(2)); + } + + @Test + public void asDataPointsShouldReturnCorrectDataPointsWithoutElevation() { + + List> dataPoints = mapper.asDataPoints(responseNode); + + assertThatDataPointMatches( + dataPoints.get(1), + createTestProperties( + "2017-10-04T13:45:54Z", + null, + "raw:com.google.location.sample:com.google.android.gms:LGE:Nexus 5:bar:live_location", + SCHEMA_ID, + 51.233600616455078, -0.57613241672515869, 43.0, 50.0)); + } + + @Override + public void assertThatMeasureMatches(Geoposition testMeasure, GoogleFitTestProperties testProperties) { + + Geoposition expectedGeoposition = + new Geoposition.Builder( + DEGREE_OF_ARC.newUnitValue(testProperties.getFpValue(0)), + DEGREE_OF_ARC.newUnitValue(testProperties.getFpValue(1)), + testProperties.getEffectiveTimeFrame().orElseThrow(IllegalArgumentException::new)) + .setElevation(LengthUnit.METER.newUnitValue(testProperties.getFpValue(3))) + .build(); + + assertThat(testMeasure, equalTo(expectedGeoposition)); + } +} diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapperUnitTests.java index 51092513..28d4eba7 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitHeartRateDataPointMapperUnitTests.java @@ -53,7 +53,7 @@ public void assertThatMeasureMatches(HeartRate testMeasure, GoogleFitTestPropert HeartRate.Builder expectedHeartRateBuilder = new HeartRate.Builder(testProperties.getFpValue()); - setExpectedEffectiveTimeFrame(expectedHeartRateBuilder, testProperties); + testProperties.getEffectiveTimeFrame().ifPresent(expectedHeartRateBuilder::setEffectiveTimeFrame); assertThat(testMeasure, equalTo(expectedHeartRateBuilder.build())); } diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapperUnitTests.java index 9837ac35..b5d093ca 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitPhysicalActivityDataPointMapperUnitTests.java @@ -91,7 +91,7 @@ public void assertThatMeasureMatches(PhysicalActivity testMeasure, GoogleFitTest PhysicalActivity.Builder physicalActivityBuilder = new PhysicalActivity.Builder(testProperties.getStringValue()); - setExpectedEffectiveTimeFrame(physicalActivityBuilder, testProperties); + testProperties.getEffectiveTimeFrame().ifPresent(physicalActivityBuilder::setEffectiveTimeFrame); assertThat(testMeasure, equalTo(physicalActivityBuilder.build())); } diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitSpeedDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitSpeedDataPointMapperUnitTests.java new file mode 100644 index 00000000..0ae0c7e6 --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitSpeedDataPointMapperUnitTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.googlefit.mapper; + +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.Speed; +import org.openmhealth.shim.googlefit.common.GoogleFitTestProperties; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.openmhealth.schema.domain.omh.Speed.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.SpeedUnit.METERS_PER_SECOND; + + +/** + * @author Emerson Farrugia + */ +public class GoogleFitSpeedDataPointMapperUnitTests extends GoogleFitDataPointMapperUnitTests { + + private final GoogleFitSpeedDataPointMapper mapper = new GoogleFitSpeedDataPointMapper(); + + @BeforeClass + @Override + public void initializeResponseNode() throws IOException { + + responseNode = asJsonNode("org/openmhealth/shim/googlefit/mapper/googlefit-merge-speed.json"); + } + + @Test + public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { + + assertThat(mapper.asDataPoints(responseNode).size(), equalTo(2)); + } + + @Test + public void asDataPointsShouldReturnCorrectDataPoints() { + + List> dataPoints = mapper.asDataPoints(responseNode); + + assertThatDataPointMatches(dataPoints.get(0), + createFloatingPointTestProperties(0.75, "2017-10-04T13:55:56Z", null, + "derived:com.google.speed:com.google.android.gms:LGE:Nexus 5:foo:live_gps_speed", SCHEMA_ID)); + } + + @Override + public void assertThatMeasureMatches(Speed testMeasure, GoogleFitTestProperties testProperties) { + + Speed expectedSpeed = + new Speed.Builder( + METERS_PER_SECOND.newUnitValue(testProperties.getFpValue()), + testProperties.getEffectiveTimeFrame().orElseThrow(IllegalArgumentException::new)) + .build(); + + assertThat(testMeasure, equalTo(expectedSpeed)); + } +} diff --git a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapperUnitTests.java index 7c5cbcd0..4531fff3 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/googlefit/mapper/GoogleFitStepCountDataPointMapperUnitTests.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.StepCount1; +import org.openmhealth.schema.domain.omh.StepCount2; import org.openmhealth.shim.googlefit.common.GoogleFitTestProperties; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -17,13 +17,13 @@ import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.collection.IsEmptyCollection.empty; import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; -import static org.openmhealth.schema.domain.omh.StepCount1.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.StepCount2.SCHEMA_ID; /** * @author Chris Schaefbauer */ -public class GoogleFitStepCountDataPointMapperUnitTests extends GoogleFitDataPointMapperUnitTests { +public class GoogleFitStepCountDataPointMapperUnitTests extends GoogleFitDataPointMapperUnitTests { private final GoogleFitStepCountDataPointMapper mapper = new GoogleFitStepCountDataPointMapper(); @@ -43,7 +43,7 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(singletonList(responseNode)); + List> dataPoints = mapper.asDataPoints(singletonList(responseNode)); assertThatDataPointMatches(dataPoints.get(0), createIntegerTestProperties(4146, "2015-02-02T22:49:39.811Z", "2015-02-02T23:25:20.811Z", @@ -58,13 +58,12 @@ public void asDataPointsShouldReturnCorrectDataPoints() { @Test public void asDataPointsShouldReturnSelfReportedAsModalityWhenDataSourceContainsUserInput() { - List> dataPoints = mapper.asDataPoints(singletonList(responseNode)); + List> dataPoints = mapper.asDataPoints(singletonList(responseNode)); assertThat(dataPoints.get(2).getHeader().getAcquisitionProvenance().getModality(), equalTo(SELF_REPORTED)); assertThat(dataPoints.get(0).getHeader().getAcquisitionProvenance().getModality(), nullValue()); - } @Test @@ -75,14 +74,13 @@ public void asDataPointsShouldReturnZeroDataPointsWithEmptyData() { assertThat(mapper.asDataPoints(emptyNode), is(empty())); } - /* Helper methods */ - @Override - public void assertThatMeasureMatches(StepCount1 testMeasure, GoogleFitTestProperties testProperties) { - - StepCount1.Builder expectedStepCountBuilder = new StepCount1.Builder(testProperties.getIntValue()); + public void assertThatMeasureMatches(StepCount2 testMeasure, GoogleFitTestProperties testProperties) { - setExpectedEffectiveTimeFrame(expectedStepCountBuilder, testProperties); + StepCount2.Builder expectedStepCountBuilder = new StepCount2.Builder( + testProperties.getIntValue(), + testProperties.getEffectiveTimeFrame().orElseThrow(IllegalArgumentException::new) + ); assertThat(testMeasure, equalTo(expectedStepCountBuilder.build())); } diff --git a/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapperUnitTests.java index 218d33b8..2a48470d 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthSleepDurationDataPointMapperUnitTests.java @@ -19,22 +19,22 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.SleepDuration1; +import org.openmhealth.schema.domain.omh.SleepDuration2; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import java.math.BigDecimal; import java.time.OffsetDateTime; import java.util.List; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; import static org.openmhealth.schema.domain.omh.DurationUnit.MINUTE; -import static org.openmhealth.schema.domain.omh.SleepDuration1.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.SleepDuration2.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; @@ -45,7 +45,7 @@ public class IHealthSleepDurationDataPointMapperUnitTests extends IHealthDataPoi private JsonNode responseNode; private IHealthSleepDurationDataPointMapper mapper = new IHealthSleepDurationDataPointMapper(); - private List> dataPoints; + private List> dataPoints; @BeforeClass public void initializeResponse() { @@ -68,9 +68,9 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPointsWhenSensed() { - SleepDuration1 expectedSleepDuration = new SleepDuration1.Builder(new DurationUnitValue( - MINUTE, 345)) - .setEffectiveTimeFrame(ofStartDateTimeAndEndDateTime( + SleepDuration2 expectedSleepDuration = new SleepDuration2.Builder( + new DurationUnitValue(MINUTE, 345), + ofStartDateTimeAndEndDateTime( OffsetDateTime.parse("2015-11-15T01:51:00-07:00"), OffsetDateTime.parse("2015-11-15T09:16:00-07:00"))) .build(); @@ -81,29 +81,18 @@ public void asDataPointsShouldReturnCorrectDataPointsWhenSensed() { "7eb7292b90d710ae7b7f61b75f9425cf", OffsetDateTime.parse("2015-11-15T16:19:10Z")); } - @Test - public void asDataPointsShouldMapAwakenAsAdditionalProperty() { - - assertThat(((BigDecimal) dataPoints.get(0).getBody().getAdditionalProperties().get("wakeup_count")).intValue(), - equalTo(13)); - - assertThat(((BigDecimal) dataPoints.get(2).getBody().getAdditionalProperties().get("wakeup_count")).intValue(), - equalTo(0)); - } - @Test public void asDataPointsShouldReturnDataPointWithUserNoteWhenNoteIsPresent() { - SleepDuration1 expectedSleepDuration = new SleepDuration1.Builder(new DurationUnitValue(MINUTE, 195)) - .setEffectiveTimeFrame(ofStartDateTimeAndEndDateTime( + SleepDuration2 expectedSleepDuration = new SleepDuration2.Builder( + new DurationUnitValue(MINUTE, 195), + ofStartDateTimeAndEndDateTime( OffsetDateTime.parse("2015-11-15T13:51:00+01:00"), OffsetDateTime.parse("2015-11-15T17:16:00+01:00"))) .setUserNotes("Best sleep ever") .build(); assertThat(dataPoints.get(1).getBody(), equalTo(expectedSleepDuration)); - - assertThat(dataPoints.get(0).getBody().getUserNotes(), nullValue()); assertThat(dataPoints.get(1).getBody().getUserNotes(), equalTo("Best sleep ever")); } diff --git a/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapperUnitTests.java index 8a3d263e..4f2b25ec 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/ihealth/mapper/IHealthStepCountDataPointMapperUnitTests.java @@ -17,18 +17,17 @@ package org.openmhealth.shim.ihealth.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.hamcrest.Matchers; import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.StepCount1; +import org.openmhealth.schema.domain.omh.StepCount2; import org.openmhealth.schema.domain.omh.TimeInterval; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import java.time.OffsetDateTime; import java.util.List; +import static java.time.OffsetDateTime.parse; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -37,7 +36,8 @@ import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; import static org.openmhealth.schema.domain.omh.DurationUnit.DAY; -import static org.openmhealth.schema.domain.omh.StepCount1.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.StepCount2.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; /** @@ -47,7 +47,7 @@ public class IHealthStepCountDataPointMapperUnitTests extends IHealthDataPointMa private JsonNode responseNode; private IHealthStepCountDataPointMapper mapper = new IHealthStepCountDataPointMapper(); - private List> dataPoints; + private List> dataPoints; @BeforeClass @@ -79,28 +79,31 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPointsWhenSensed() { - StepCount1 expectedStepCount = new StepCount1.Builder(21) - .setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndDuration(OffsetDateTime.parse("2015-11-16T00:00:00+05:00"), - new DurationUnitValue(DAY, 1))) + TimeInterval effectiveTimeInterval = + ofStartDateTimeAndDuration(parse("2015-11-16T00:00:00+05:00"), + new DurationUnitValue(DAY, 1)); + + StepCount2 expectedStepCount = new StepCount2.Builder(21, effectiveTimeInterval) .build(); assertThat(dataPoints.get(0).getBody(), equalTo(expectedStepCount)); testDataPointHeader(dataPoints.get(0).getHeader(), SCHEMA_ID, SENSED, - "ac67c4ccf64af669d92569af85d19f59", OffsetDateTime.parse("2015-11-17T19:23:21Z")); + "ac67c4ccf64af669d92569af85d19f59", parse("2015-11-17T19:23:21Z")); } @Test public void asDataPointsShouldReturnDataPointWithUserNoteWhenNoteIsPresent() { - StepCount1 expectedStepCount = new StepCount1.Builder(4398) - .setEffectiveTimeFrame( - TimeInterval.ofStartDateTimeAndDuration(OffsetDateTime.parse("2015-11-18T00:00:00Z"), - new DurationUnitValue(DAY, 1))).setUserNotes("Great steps") + TimeInterval effectiveTimeInterval = + ofStartDateTimeAndDuration(parse("2015-11-18T00:00:00Z"), + new DurationUnitValue(DAY, 1)); + + StepCount2 expectedStepCount = new StepCount2.Builder(4398, effectiveTimeInterval) + .setUserNotes("Great steps") .build(); - assertThat(dataPoints.get(1).getBody(), Matchers.equalTo(expectedStepCount)); + assertThat(dataPoints.get(1).getBody(), equalTo(expectedStepCount)); assertThat(dataPoints.get(0).getBody().getUserNotes(), nullValue()); assertThat(dataPoints.get(1).getBody().getUserNotes(), equalTo("Great steps")); diff --git a/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapperUnitTests.java index 49bfdf6f..2f751ca1 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepDurationDataPointMapperUnitTests.java @@ -1,20 +1,31 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmhealth.shim.misfit.mapper; -import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.*; -import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; -import org.openmhealth.shim.common.mapper.JsonNodeMappingException; -import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import java.io.IOException; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.List; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.greaterThan; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; @@ -24,102 +35,36 @@ /** * @author Emerson Farrugia */ -public class MisfitSleepDurationDataPointMapperUnitTests extends DataPointMapperUnitTests { +public class MisfitSleepDurationDataPointMapperUnitTests + extends MisfitSleepMeasureDataPointMapperUnitTests { private final MisfitSleepDurationDataPointMapper mapper = new MisfitSleepDurationDataPointMapper(); - private JsonNode responseNode; - - @BeforeTest - public void initializeResponseNode() throws IOException { - - responseNode = asJsonNode("org/openmhealth/shim/misfit/mapper/misfit-sleeps.json"); - } - - @Test(expectedExceptions = JsonNodeMappingException.class) - public void asDataPointsShouldThrowExceptionOnEmptySleepDetails() throws IOException { - - JsonNode node = objectMapper.readTree("{\n" + - " \"sleeps\": [\n" + - " {\n" + - " \"id\": \"54fa13a8440f705a7406845f\",\n" + - " \"autoDetected\": false,\n" + - " \"startTime\": \"2015-02-24T21:40:59-05:00\",\n" + - " \"duration\": 2580,\n" + - " \"sleepDetails\": []\n" + - " }\n" + - " ]\n" + - "}"); - - mapper.asDataPoints(node); - } - - @Test - public void asDataPointsShouldReturnEmptyListIfAwake() throws IOException { - - JsonNode node = objectMapper.readTree("{\n" + - " \"sleeps\": [\n" + - " {\n" + - " \"id\": \"54fa13a8440f705a7406845f\",\n" + - " \"autoDetected\": false,\n" + - " \"startTime\": \"2015-02-24T21:40:59-05:00\",\n" + - " \"duration\": 2580,\n" + - " \"sleepDetails\": [\n" + - " {\n" + - " \"datetime\": \"2015-02-24T21:40:59-05:00\",\n" + - " \"value\": 1\n" + - " }\n" + - " ]\n" + - " }\n" + - " ]\n" + - "}"); - - List> dataPoints = mapper.asDataPoints(node); - - assertThat(dataPoints, notNullValue()); - assertThat(dataPoints, empty()); - } - - @Test - public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { - - List> dataPoints = mapper.asDataPoints(responseNode); - - assertThat(dataPoints, notNullValue()); - assertThat(dataPoints.size(), equalTo(2)); - } - - @Test - public void asDataPointsShouldReturnEmptyListIfEmptyResponse() throws IOException { - - JsonNode emptyNode = objectMapper.readTree("{\n" + - " \"sleeps\": []\n" + - "}"); - - assertThat(mapper.asDataPoints(emptyNode), empty()); + @Override + protected MisfitSleepMeasureDataPointMapper getMapper() { + return mapper; } @Test public void asDataPointsShouldReturnCorrectDataPoints() { - - List> dataPoints = mapper.asDataPoints(responseNode); - - assertThat(dataPoints, notNullValue()); - assertThat(dataPoints.size(), greaterThan(0)); - - // the end time is the addition of the start time and the total duration + // the end time is the addition of the start time and the total duration when the last segment isn't awake TimeInterval effectiveTimeInterval = TimeInterval.ofStartDateTimeAndEndDateTime( OffsetDateTime.of(2015, 2, 23, 21, 40, 59, 0, ZoneOffset.ofHours(-5)), OffsetDateTime.of(2015, 2, 24, 0, 53, 59, 0, ZoneOffset.ofHours(-5))); // the sleep duration is the total duration minus the sum of the awake segment durations - SleepDuration1 sleepDuration = new SleepDuration1.Builder(new DurationUnitValue(SECOND, 10140)) - .setEffectiveTimeFrame(effectiveTimeInterval) - .build(); + SleepDuration2 expectedSleepDuration = + new SleepDuration2.Builder(new DurationUnitValue(SECOND, 10140), effectiveTimeInterval) + .build(); - DataPoint firstDataPoint = dataPoints.get(0); + List> dataPoints = mapper.asDataPoints(sleepsResponseNode); - assertThat(firstDataPoint.getBody(), equalTo(sleepDuration)); + assertThat(dataPoints, notNullValue()); + assertThat(dataPoints.size(), greaterThan(0)); + + DataPoint firstDataPoint = dataPoints.get(0); + + assertThat(firstDataPoint.getBody(), equalTo(expectedSleepDuration)); DataPointAcquisitionProvenance acquisitionProvenance = firstDataPoint.getHeader().getAcquisitionProvenance(); @@ -127,18 +72,4 @@ public void asDataPointsShouldReturnCorrectDataPoints() { assertThat(acquisitionProvenance.getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); assertThat(acquisitionProvenance.getModality(), equalTo(SENSED)); } - - @Test - public void asDataPointsShouldSetModalityAsSensedOnlyWhenAutodetectedIsTrue() throws IOException { - - JsonNode responseNodeForSleepSensing = - asJsonNode("org/openmhealth/shim/misfit/mapper/misfit-sleeps-detected-and-not.json"); - - List> dataPoints = mapper.asDataPoints(responseNodeForSleepSensing); - - assertThat(dataPoints.get(0).getHeader().getAcquisitionProvenance().getModality(), equalTo(SENSED)); - assertThat(dataPoints.get(1).getHeader().getAcquisitionProvenance().getModality(), nullValue()); - assertThat(dataPoints.get(2).getHeader().getAcquisitionProvenance().getModality(), nullValue()); - - } } \ No newline at end of file diff --git a/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepEpisodeDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepEpisodeDataPointMapperUnitTests.java new file mode 100644 index 00000000..fc242137 --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepEpisodeDataPointMapperUnitTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.misfit.mapper; + +import org.openmhealth.schema.domain.omh.*; +import org.testng.annotations.Test; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; +import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; +import static org.openmhealth.shim.misfit.mapper.MisfitDataPointMapper.RESOURCE_API_SOURCE_NAME; + + +/** + * @author Emerson Farrugia + */ +public class MisfitSleepEpisodeDataPointMapperUnitTests + extends MisfitSleepMeasureDataPointMapperUnitTests { + + private final MisfitSleepEpisodeDataPointMapper mapper = new MisfitSleepEpisodeDataPointMapper(); + + @Override + protected MisfitSleepMeasureDataPointMapper getMapper() { + return mapper; + } + + @Test + public void asDataPointsShouldReturnCorrectDataPoints() { + + // the end time is the addition of the start time and the total duration when the last segment isn't awake + TimeInterval effectiveTimeInterval = TimeInterval.ofStartDateTimeAndEndDateTime( + OffsetDateTime.of(2015, 2, 23, 21, 40, 59, 0, ZoneOffset.ofHours(-5)), + OffsetDateTime.of(2015, 2, 24, 0, 53, 59, 0, ZoneOffset.ofHours(-5))); + + SleepEpisode expectedSleepEpisode = new SleepEpisode.Builder(effectiveTimeInterval) + // the total sleep time is total duration minus the sum of the awake segment durations + .setTotalSleepTime(new DurationUnitValue(SECOND, 10140)) + .build(); + + List> dataPoints = mapper.asDataPoints(sleepsResponseNode); + + assertThat(dataPoints, notNullValue()); + assertThat(dataPoints.size(), greaterThan(0)); + + DataPoint firstDataPoint = dataPoints.get(0); + + assertThat(firstDataPoint.getBody(), equalTo(expectedSleepEpisode)); + + DataPointAcquisitionProvenance acquisitionProvenance = firstDataPoint.getHeader().getAcquisitionProvenance(); + + assertThat(acquisitionProvenance, notNullValue()); + assertThat(acquisitionProvenance.getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); + assertThat(acquisitionProvenance.getModality(), equalTo(SENSED)); + } +} diff --git a/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepMeasureDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepMeasureDataPointMapperUnitTests.java new file mode 100644 index 00000000..3e100b4a --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitSleepMeasureDataPointMapperUnitTests.java @@ -0,0 +1,189 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.misfit.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.SchemaSupport; +import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; +import org.openmhealth.shim.common.mapper.JsonNodeMappingException; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; + + +/** + * @author Emerson Farrugia + */ +public abstract class MisfitSleepMeasureDataPointMapperUnitTests + extends DataPointMapperUnitTests { + + protected JsonNode sleepsResponseNode; + + @BeforeMethod + public void initializeResponseNode() throws IOException { + + sleepsResponseNode + = asJsonNode("org/openmhealth/shim/misfit/mapper/misfit-sleeps.json"); + } + + protected abstract MisfitSleepMeasureDataPointMapper getMapper(); + + @Test(expectedExceptions = JsonNodeMappingException.class) + public void asDataPointsShouldThrowExceptionOnEmptySleepDetails() throws IOException { + + JsonNode node = objectMapper.readTree("{\n" + + " \"sleeps\": [\n" + + " {\n" + + " \"id\": \"54fa13a8440f705a7406845f\",\n" + + " \"autoDetected\": false,\n" + + " \"startTime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"duration\": 2580,\n" + + " \"sleepDetails\": []\n" + + " }\n" + + " ]\n" + + "}"); + + getMapper().asDataPoints(node); + } + + @Test + public void asDataPointsShouldReturnEmptyListIfOnlyAwake() throws IOException { + + JsonNode node = objectMapper.readTree("{\n" + + " \"sleeps\": [\n" + + " {\n" + + " \"id\": \"54fa13a8440f705a7406845f\",\n" + + " \"autoDetected\": false,\n" + + " \"startTime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"duration\": 2580,\n" + + " \"sleepDetails\": [\n" + + " {\n" + + " \"datetime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"value\": 1\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"); + + List> dataPoints = getMapper().asDataPoints(node); + + assertThat(dataPoints, notNullValue()); + assertThat(dataPoints, empty()); + } + + @Test + public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { + + List> dataPoints = getMapper().asDataPoints(sleepsResponseNode); + + assertThat(dataPoints, notNullValue()); + assertThat(dataPoints.size(), equalTo(2)); + } + + @Test + public void asDataPointsShouldReturnEmptyListIfEmptyResponse() throws IOException { + + JsonNode emptyNode = objectMapper.readTree("{\n" + + " \"sleeps\": []\n" + + "}"); + + assertThat(getMapper().asDataPoints(emptyNode), empty()); + } + + @Test + public void asDataPointsShouldSetModalityToSensedWhenAutoDetectedIsTrue() throws IOException { + + JsonNode node = objectMapper.readTree("{\n" + + " \"sleeps\": [\n" + + " {\n" + + " \"id\": \"54fa13a8440f705a7406845f\",\n" + + " \"autoDetected\": true,\n" + + " \"startTime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"duration\": 2580,\n" + + " \"sleepDetails\": [\n" + + " {\n" + + " \"datetime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"value\": 2\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"); + + List> dataPoints = getMapper().asDataPoints(node); + + assertThat(dataPoints.get(0).getHeader().getAcquisitionProvenance().getModality(), equalTo(SENSED)); + } + + @Test + public void asDataPointsShouldNotSetModalityWhenAutoDetectedIsFalse() throws IOException { + + JsonNode node = objectMapper.readTree("{\n" + + " \"sleeps\": [\n" + + " {\n" + + " \"id\": \"54fa13a8440f705a7406845f\",\n" + + " \"autoDetected\": false,\n" + + " \"startTime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"duration\": 2580,\n" + + " \"sleepDetails\": [\n" + + " {\n" + + " \"datetime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"value\": 2\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"); + + List> dataPoints = getMapper().asDataPoints(node); + + assertThat(dataPoints.get(0).getHeader().getAcquisitionProvenance().getModality(), nullValue()); + } + + @Test + public void asDataPointsShouldNotSetModalityWhenAutoDetectedIsMissing() throws IOException { + + JsonNode node = objectMapper.readTree("{\n" + + " \"sleeps\": [\n" + + " {\n" + + " \"id\": \"54fa13a8440f705a7406845f\",\n" + + " \"startTime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"duration\": 2580,\n" + + " \"sleepDetails\": [\n" + + " {\n" + + " \"datetime\": \"2015-02-24T21:40:59-05:00\",\n" + + " \"value\": 2\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"); + + List> dataPoints = getMapper().asDataPoints(node); + + assertThat(dataPoints.get(0).getHeader().getAcquisitionProvenance().getModality(), nullValue()); + } +} \ No newline at end of file diff --git a/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapperUnitTests.java index 16f5c367..d5a6344c 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/misfit/mapper/MisfitStepCountDataPointMapperUnitTests.java @@ -38,7 +38,7 @@ public void initializeResponseNode() throws IOException { @Test public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); assertThat(dataPoints, notNullValue()); assertThat(dataPoints.size(), equalTo(3)); @@ -47,7 +47,7 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); assertThat(dataPoints, notNullValue()); assertThat(dataPoints.size(), greaterThan(0)); @@ -57,11 +57,10 @@ public void asDataPointsShouldReturnCorrectDataPoints() { OffsetDateTime.of(2015, 4, 13, 0, 0, 0, 0, UTC), new DurationUnitValue(DAY, 1)); - StepCount1 stepCount = new StepCount1.Builder(26370) - .setEffectiveTimeFrame(effectiveTimeInterval) + StepCount2 stepCount = new StepCount2.Builder(26370, effectiveTimeInterval) .build(); - DataPoint firstDataPoint = dataPoints.get(0); + DataPoint firstDataPoint = dataPoints.get(0); assertThat(firstDataPoint.getBody(), equalTo(stepCount)); diff --git a/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapperUnitTests.java index 04763444..a572a803 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesPhysicalActivityDataPointMapperUnitTests.java @@ -1,99 +1,100 @@ package org.openmhealth.shim.moves.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.hamcrest.Matchers; import org.openmhealth.schema.domain.omh.*; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import static java.time.ZoneOffset.UTC; -import static org.hamcrest.MatcherAssert.assertThat; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.List; + import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.is; -import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; +import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; import static org.openmhealth.schema.domain.omh.LengthUnit.METER; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; +import static org.openmhealth.shim.moves.mapper.MovesDataPointMapper.RESOURCE_API_SOURCE_NAME; -import java.io.IOException; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.List; /** - * @author Jared Sieling. + * @author Jared Sieling + * @author Emerson Farrugia */ public class MovesPhysicalActivityDataPointMapperUnitTests extends DataPointMapperUnitTests { private final MovesPhysicalActivityDataPointMapper mapper = new MovesPhysicalActivityDataPointMapper(); - private JsonNode responseNode; + private JsonNode storylineResponseNode; + private JsonNode storylineNoSegmentsResponseNode; @BeforeTest - public void initializeResponseNode() { - responseNode = asJsonNode("org/openmhealth/shim/moves/mapper/moves-storyline.json"); + public void initializeResponseNode() throws IOException { + + storylineResponseNode = asJsonNode("/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily.json"); + storylineNoSegmentsResponseNode = + asJsonNode("/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-segments.json"); } @Test - public void asDataPointsShouldReturnEmptyListWhenResponseIsEmpty() throws IOException { - JsonNode emptyResponseNode = asJsonNode("org/openmhealth/shim/moves/mapper/moves-storyline-empty.json"); - assertThat(mapper.asDataPoints(emptyResponseNode), is(empty())); + public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { + + List> dataPoints = mapper.asDataPoints(storylineResponseNode); + + assertThat(dataPoints, notNullValue()); + assertThat(dataPoints.size(), equalTo(7)); // activities without time frames are ignored } @Test - public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { - assertThat(mapper.asDataPoints(responseNode).size(), equalTo(8)); + public void asDataPointsShouldReturnEmptyListIfResponseHasNoSegments() throws IOException { + + assertThat(mapper.asDataPoints(storylineNoSegmentsResponseNode), empty()); } @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); - assertThatDataPointMatches(dataPoints.get(0), "cycling", "2016-04-03", "17:52:00", 1500D, 309L); - assertThatDataPointMatches(dataPoints.get(2), "walking", "2016-04-04", "17:54:51", 5844D, 3820L); + List> dataPoints = mapper.asDataPoints(storylineResponseNode); + + assertThatDataPointMatches(dataPoints.get(0), + "walking", + OffsetDateTime.parse("2012-12-12T07:14:30+02:00"), + OffsetDateTime.parse("2012-12-12T07:27:32+02:00"), + 1251L, + 99L); } - public void assertThatDataPointMatches(DataPoint dataPoint, String expectedActivityName, - TimeFrame expectedTimeFrame, Double expectedDistance) { + public void assertThatDataPointMatches( + DataPoint dataPoint, + String expectedActivityName, + OffsetDateTime expectedEffectiveStartDateTime, + OffsetDateTime expectedEffectiveEndDateTime, + Long expectedDistanceInM, + Long expectedCaloriesInKCal) { PhysicalActivity.Builder expectedMeasureBuilder = new PhysicalActivity.Builder(expectedActivityName); - expectedMeasureBuilder.setEffectiveTimeFrame(expectedTimeFrame); + expectedMeasureBuilder.setEffectiveTimeFrame( + ofStartDateTimeAndEndDateTime(expectedEffectiveStartDateTime, expectedEffectiveEndDateTime)); - if (expectedDistance != null) { - expectedMeasureBuilder.setDistance(new LengthUnitValue(METER, expectedDistance)); + if (expectedDistanceInM != null) { + expectedMeasureBuilder.setDistance(new LengthUnitValue(METER, expectedDistanceInM)); } - PhysicalActivity expectedPhysicalActivity = expectedMeasureBuilder.build(); - - assertThat(dataPoint.getBody(), Matchers.equalTo(expectedPhysicalActivity)); - assertThat(dataPoint.getHeader().getBodySchemaId(), Matchers.equalTo(PhysicalActivity.SCHEMA_ID)); - - assertThat(dataPoint.getHeader().getAcquisitionProvenance().getSourceName(), - Matchers.equalTo(MovesDataPointMapper.RESOURCE_API_SOURCE_NAME)); - } - - public void assertThatDataPointMatches(DataPoint dataPoint, String expectedActivityName, - String expectedEffectiveDate, String expectedEffectiveStartTime, Double expectedDistance, - Long expectedDurationInMs) { - - OffsetDateTime expectedEffectiveStartDateTime = OffsetDateTime - .of(LocalDate.parse(expectedEffectiveDate), LocalTime.parse(expectedEffectiveStartTime), ZoneOffset.ofHours(-5)); + if (expectedCaloriesInKCal != null) { + expectedMeasureBuilder.setCaloriesBurned(new KcalUnitValue(KILOCALORIE, expectedCaloriesInKCal)); + } - TimeFrame expectedTimeFrame; + PhysicalActivity expectedPhysicalActivity = expectedMeasureBuilder.build(); - if (expectedDurationInMs != null) { - DurationUnitValue expectedDuration = new DurationUnitValue(SECOND, expectedDurationInMs); + assertThat(dataPoint.getHeader().getBodySchemaId(), equalTo(PhysicalActivity.SCHEMA_ID)); + assertThat(dataPoint.getBody(), equalTo(expectedPhysicalActivity)); - expectedTimeFrame = new TimeFrame( - TimeInterval.ofStartDateTimeAndDuration(expectedEffectiveStartDateTime, expectedDuration)); - } - else { - expectedTimeFrame = new TimeFrame(expectedEffectiveStartDateTime); - } + DataPointAcquisitionProvenance acquisitionProvenance = dataPoint.getHeader().getAcquisitionProvenance(); - assertThatDataPointMatches(dataPoint, expectedActivityName, expectedTimeFrame, expectedDistance); + assertThat(acquisitionProvenance, notNullValue()); + assertThat(acquisitionProvenance.getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); } - } diff --git a/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapperUnitTest.java b/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapperUnitTest.java deleted file mode 100644 index ae470b47..00000000 --- a/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapperUnitTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.openmhealth.shim.moves.mapper; - -import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; -import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.time.OffsetDateTime; -import java.util.List; - -import static java.time.ZoneOffset.UTC; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.greaterThan; -import static org.openmhealth.schema.domain.omh.DurationUnit.DAY; -import static org.openmhealth.shim.moves.mapper.MovesDataPointMapper.RESOURCE_API_SOURCE_NAME; - -/** - * @author Jared Sieling. - */ -public class MovesStepCountDataPointMapperUnitTest extends DataPointMapperUnitTests { - - private final MovesStepCountDataPointMapper mapper = new MovesStepCountDataPointMapper(); - private JsonNode responseNode; - - @BeforeTest - public void initializeResponseNode() throws IOException { - responseNode = asJsonNode("/org/openmhealth/shim/moves/mapper/moves-summary.json"); - } - - @Test - public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); - - assertThat(dataPoints, notNullValue()); - assertThat(dataPoints.size(), equalTo(3)); - } - - @Test - public void asDataPointsShouldReturnEmptyListIfEmptyResponse() throws IOException { - JsonNode emptyNode = objectMapper.readTree("[{\"summary\": [{\n" + - " \"activity\": \"transport\",\n" + - " \"group\": \"transport\",\n" + - " \"duration\": 1683.0,\n" + - " \"distance\": 37980.0\n" + - " }]}]"); - assertThat(mapper.asDataPoints(emptyNode), empty()); - } - - @Test - public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); - - assertThat(dataPoints, notNullValue()); - assertThat(dataPoints.size(), greaterThan(0)); - - TimeInterval effectiveTimeInterval = TimeInterval.ofStartDateTimeAndDuration( - OffsetDateTime.of(2016, 4, 3, 0, 0, 0, 0, UTC), - new DurationUnitValue(DAY, 1)); - - StepCount1 stepCount = new StepCount1.Builder(1741) - .setEffectiveTimeFrame(effectiveTimeInterval) - .build(); - - DataPoint firstDataPoint = dataPoints.get(0); - - assertThat(firstDataPoint.getBody(), equalTo(stepCount)); - - DataPointAcquisitionProvenance acquisitionProvenance = firstDataPoint.getHeader().getAcquisitionProvenance(); - - assertThat(acquisitionProvenance, notNullValue()); - assertThat(acquisitionProvenance.getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); - } - - -} diff --git a/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapperUnitTests.java new file mode 100644 index 00000000..2d491a6d --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/moves/mapper/MovesStepCountDataPointMapperUnitTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.moves.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DataPointAcquisitionProvenance; +import org.openmhealth.schema.domain.omh.StepCount2; +import org.openmhealth.schema.domain.omh.TimeInterval; +import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; +import static org.openmhealth.shim.moves.mapper.MovesDataPointMapper.RESOURCE_API_SOURCE_NAME; + + +/** + * @author Jared Sieling + * @author Emerson Farrugia + */ +public class MovesStepCountDataPointMapperUnitTests extends DataPointMapperUnitTests { + + private final MovesStepCountDataPointMapper mapper = new MovesStepCountDataPointMapper(); + private JsonNode storylineResponseNode; + private JsonNode storylineNoSegmentsResponseNode; + private JsonNode storylineNoStepsResponseNode; + + @BeforeTest + public void initializeResponseNode() throws IOException { + + storylineResponseNode = asJsonNode("/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily.json"); + storylineNoSegmentsResponseNode = asJsonNode("/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-segments.json"); + storylineNoStepsResponseNode = asJsonNode("/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-steps.json"); + } + + @Test + public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { + + List> dataPoints = mapper.asDataPoints(storylineResponseNode); + + assertThat(dataPoints, notNullValue()); + assertThat(dataPoints.size(), equalTo(5)); + } + + @Test + public void asDataPointsShouldReturnEmptyListIfResponseHasNoSegments() throws IOException { + + assertThat(mapper.asDataPoints(storylineNoSegmentsResponseNode), empty()); + } + + @Test + public void asDataPointsShouldReturnEmptyListIfResponseHasNoSteps() throws IOException { + + assertThat(mapper.asDataPoints(storylineNoStepsResponseNode), empty()); + } + + @Test + public void asDataPointsShouldReturnCorrectDataPoints() { + + List> dataPoints = mapper.asDataPoints(storylineResponseNode); + + assertThat(dataPoints, notNullValue()); + assertThat(dataPoints.size(), greaterThan(0)); + + TimeInterval effectiveTimeInterval = TimeInterval.ofStartDateTimeAndEndDateTime( + OffsetDateTime.of(2012, 12, 12, 7, 14, 30, 0, ZoneOffset.ofHours(2)), + OffsetDateTime.of(2012, 12, 12, 7, 27, 32, 0, ZoneOffset.ofHours(2))); + + StepCount2 stepCount = new StepCount2.Builder(1353, effectiveTimeInterval) + .build(); + + DataPoint firstDataPoint = dataPoints.get(0); + + assertThat(firstDataPoint.getBody(), equalTo(stepCount)); + + DataPointAcquisitionProvenance acquisitionProvenance = firstDataPoint.getHeader().getAcquisitionProvenance(); + + assertThat(acquisitionProvenance, notNullValue()); + assertThat(acquisitionProvenance.getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); + } +} diff --git a/shim-server/src/test/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapperUnitTests.java index ba0a4ee4..2c778c83 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/runkeeper/mapper/RunkeeperCaloriesBurnedDataPointMapperUnitTests.java @@ -1,7 +1,10 @@ package org.openmhealth.shim.runkeeper.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DataPointHeader; +import org.openmhealth.schema.domain.omh.TimeInterval; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -12,7 +15,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.openmhealth.schema.domain.omh.CaloriesBurned.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.CaloriesBurned2.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; @@ -43,14 +46,15 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPointBodies() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); - CaloriesBurned expectedCaloriesBurned = - new CaloriesBurned.Builder(new KcalUnitValue(KILOCALORIE, 210.796359954334)) + TimeInterval effectiveTimeInterval = ofStartDateTimeAndDuration( + OffsetDateTime.parse("2014-10-19T13:17:27+02:00"), + SECOND.newUnitValue(4364.74158141667)); + + CaloriesBurned2 expectedCaloriesBurned = + new CaloriesBurned2.Builder(KILOCALORIE.newUnitValue(210.796359954334), effectiveTimeInterval) .setActivityName("Cycling") - .setEffectiveTimeFrame(ofStartDateTimeAndDuration( - OffsetDateTime.parse("2014-10-19T13:17:27+02:00"), - new DurationUnitValue(SECOND, 4364.74158141667))) .build(); assertThat(dataPoints.get(0).getBody(), equalTo(expectedCaloriesBurned)); @@ -70,6 +74,5 @@ public void asDataPointsShouldReturnCorrectDataPointHeaders() { assertThat(firstTestHeader.getAcquisitionProvenance().getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); - } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBloodPressureDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBloodPressureDataPointMapperUnitTests.java index d1fca89f..751c6837 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBloodPressureDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBloodPressureDataPointMapperUnitTests.java @@ -16,45 +16,46 @@ package org.openmhealth.shim.withings.mapper; -import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.*; -import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; -import org.testng.annotations.BeforeTest; +import org.openmhealth.shim.withings.domain.WithingsBodyMeasureType; import org.testng.annotations.Test; -import java.io.IOException; import java.time.OffsetDateTime; import java.util.List; import static java.util.Collections.singletonList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.openmhealth.schema.domain.omh.BloodPressure.*; +import static org.openmhealth.schema.domain.omh.BloodPressure.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.BloodPressureUnit.MM_OF_MERCURY; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; +import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.DIASTOLIC_BLOOD_PRESSURE; import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; /** * @author Chris Schaefbauer + * @author Emerson Farrugia */ -public class WithingsBloodPressureDataPointMapperUnitTests extends DataPointMapperUnitTests { +public class WithingsBloodPressureDataPointMapperUnitTests + extends WithingsBodyMeasureDataPointMapperUnitTests { private WithingsBloodPressureDataPointMapper mapper = new WithingsBloodPressureDataPointMapper(); - private JsonNode responseNode; + @Override + protected WithingsBodyMeasureDataPointMapper getMapper() { + return mapper; + } - @BeforeTest - public void initializeResponseNode() throws IOException { - - responseNode = asJsonNode("org/openmhealth/shim/withings/mapper/withings-body-measures.json"); + @Override + protected WithingsBodyMeasureType getBodyMeasureType() { + return DIASTOLIC_BLOOD_PRESSURE; } @Test public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); - assertThat(dataPoints.size(), equalTo(1)); + assertThat(mapper.asDataPoints(responseNode).size(), equalTo(1)); } @Test @@ -75,6 +76,6 @@ public void asDataPointsShouldReturnCorrectDataPoints() { assertThat(actualDataPointHeader.getAcquisitionProvenance().getModality(), equalTo(SENSED)); assertThat(actualDataPointHeader.getAcquisitionProvenance().getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); assertThat(actualDataPointHeader.getAcquisitionProvenance().getAdditionalProperties().get("external_id"), - equalTo(366956482L)); + equalTo("366956482")); } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapperUnitTests.java index c271a769..878e0ce6 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyHeightDataPointMapperUnitTests.java @@ -16,23 +16,21 @@ package org.openmhealth.shim.withings.mapper; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.openmhealth.schema.domain.omh.*; -import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; -import org.openmhealth.shim.common.mapper.JsonNodeMappingException; -import org.testng.annotations.BeforeTest; +import org.openmhealth.schema.domain.omh.BodyHeight; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DataPointHeader; +import org.openmhealth.schema.domain.omh.LengthUnitValue; +import org.openmhealth.shim.withings.domain.WithingsBodyMeasureType; import org.testng.annotations.Test; -import java.io.IOException; import java.time.OffsetDateTime; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.openmhealth.schema.domain.omh.BodyHeight.*; +import static org.openmhealth.schema.domain.omh.BodyHeight.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; -import static org.openmhealth.schema.domain.omh.LengthUnit.*; +import static org.openmhealth.schema.domain.omh.LengthUnit.METER; import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.BODY_HEIGHT; import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; @@ -41,37 +39,19 @@ * @author Chris Schaefbauer * @author Emerson Farrugia */ -public class WithingsBodyHeightDataPointMapperUnitTests extends DataPointMapperUnitTests { +public class WithingsBodyHeightDataPointMapperUnitTests + extends WithingsBodyMeasureDataPointMapperUnitTests { - private static final ObjectMapper objectMapper = new ObjectMapper(); private WithingsBodyHeightDataPointMapper mapper = new WithingsBodyHeightDataPointMapper(); - private JsonNode responseNode; - - @BeforeTest - public void initializeResponseNode() throws IOException { - - responseNode = asJsonNode("org/openmhealth/shim/withings/mapper/withings-body-measures.json"); + @Override + protected WithingsBodyMeasureDataPointMapper getMapper() { + return mapper; } - // this is included in only one mapper for brevity, can be rinsed and repeated in others if necessary - @Test(expectedExceptions = JsonNodeMappingException.class) - public void getValueForMeasureTypeShouldThrowExceptionOnDuplicateMeasureTypes() throws Exception { - - JsonNode measuresNode = objectMapper.readTree("[\n" + - " {\n" + - " \"type\": 4,\n" + // WithingsBodyMeasureType.BODY_HEIGHT - " \"unit\": 0,\n" + - " \"value\": 68\n" + - " },\n" + - " {\n" + - " \"type\": 4,\n" + - " \"unit\": 0,\n" + - " \"value\": 104\n" + - " }\n" + - "]"); - - mapper.getValueForMeasureType(measuresNode, BODY_HEIGHT); + @Override + protected WithingsBodyMeasureType getBodyMeasureType() { + return BODY_HEIGHT; } @Test @@ -96,6 +76,6 @@ public void asDataPointsShouldReturnCorrectDataPoints() { assertThat(actualDataPointHeader.getAcquisitionProvenance().getModality(), equalTo(SELF_REPORTED)); assertThat(actualDataPointHeader.getAcquisitionProvenance().getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); assertThat(actualDataPointHeader.getAcquisitionProvenance().getAdditionalProperties().get("external_id"), - equalTo(320419189L)); + equalTo("320419189")); } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyMeasureDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyMeasureDataPointMapperUnitTests.java new file mode 100644 index 00000000..ab250fcf --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyMeasureDataPointMapperUnitTests.java @@ -0,0 +1,68 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.withings.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.openmhealth.schema.domain.omh.Measure; +import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; +import org.openmhealth.shim.common.mapper.JsonNodeMappingException; +import org.openmhealth.shim.withings.domain.WithingsBodyMeasureType; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.IOException; + + +/** + * @author Chris Schaefbauer + * @author Emerson Farrugia + */ +public abstract class WithingsBodyMeasureDataPointMapperUnitTests extends DataPointMapperUnitTests { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + protected JsonNode responseNode; + + protected abstract WithingsBodyMeasureDataPointMapper getMapper(); + + protected abstract WithingsBodyMeasureType getBodyMeasureType(); + + @BeforeMethod + public void initializeResponseNode() throws IOException { + + responseNode = asJsonNode("org/openmhealth/shim/withings/mapper/withings-body-measures.json"); + } + + @Test(expectedExceptions = JsonNodeMappingException.class) + public void getValueForMeasureTypeShouldThrowExceptionOnDuplicateMeasureTypes() throws Exception { + + JsonNode measuresNode = objectMapper.readTree("[\n" + + " {\n" + + " \"type\": " + getBodyMeasureType().getMagicNumber() + ",\n" + + " \"unit\": 0,\n" + + " \"value\": 68\n" + + " },\n" + + " {\n" + + " \"type\": " + getBodyMeasureType().getMagicNumber() + ",\n" + + " \"unit\": 0,\n" + + " \"value\": 104\n" + + " }\n" + + "]"); + + getMapper().getValueForMeasureType(measuresNode, getBodyMeasureType()); + } +} diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyTemperatureDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyTemperatureDataPointMapperUnitTests.java new file mode 100644 index 00000000..0de8921f --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyTemperatureDataPointMapperUnitTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.withings.mapper; + +import org.openmhealth.schema.domain.omh.BodyTemperature; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DataPointHeader; +import org.openmhealth.schema.domain.omh.TemperatureUnitValue; +import org.openmhealth.shim.withings.domain.WithingsBodyMeasureType; +import org.testng.annotations.Test; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; +import static org.openmhealth.schema.domain.omh.TemperatureUnit.CELSIUS; +import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.BODY_TEMPERATURE; +import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; + + +/** + * @author Emerson Farrugia + */ +public class WithingsBodyTemperatureDataPointMapperUnitTests + extends WithingsBodyMeasureDataPointMapperUnitTests { + + private WithingsBodyTemperatureDataPointMapper mapper = new WithingsBodyTemperatureDataPointMapper(); + + @Override + protected WithingsBodyMeasureDataPointMapper getMapper() { + return mapper; + } + + @Override + protected WithingsBodyMeasureType getBodyMeasureType() { + return BODY_TEMPERATURE; + } + + @Test + public void asDataPointsShouldReturnCorrectDataPoints() { + + List> actualDataPoints = mapper.asDataPoints(responseNode); + + BodyTemperature expectedBodyTemperature = new BodyTemperature.Builder(new TemperatureUnitValue(CELSIUS, 37L)) + .setEffectiveTimeFrame(OffsetDateTime.parse("2015-02-23T19:24:49Z")) + .build(); + + assertThat(actualDataPoints.get(0).getBody(), equalTo(expectedBodyTemperature)); + + DataPointHeader actualDataPointHeader = actualDataPoints.get(0).getHeader(); + assertThat(actualDataPointHeader.getBodySchemaId(), equalTo(expectedBodyTemperature.getSchemaId())); + assertThat(actualDataPointHeader.getAcquisitionProvenance().getModality(), equalTo(SELF_REPORTED)); + assertThat(actualDataPointHeader.getAcquisitionProvenance().getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); + assertThat(actualDataPointHeader.getAcquisitionProvenance().getAdditionalProperties().get("external_id"), + equalTo("320419189")); + } +} diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapperUnitTests.java index e2ebb661..bece4b87 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsBodyWeightDataPointMapperUnitTests.java @@ -17,8 +17,11 @@ package org.openmhealth.shim.withings.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; -import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; +import org.openmhealth.schema.domain.omh.BodyWeight; +import org.openmhealth.schema.domain.omh.DataPoint; +import org.openmhealth.schema.domain.omh.DataPointAcquisitionProvenance; +import org.openmhealth.schema.domain.omh.MassUnitValue; +import org.openmhealth.shim.withings.domain.WithingsBodyMeasureType; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -28,24 +31,35 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.openmhealth.schema.domain.omh.BodyWeight.*; +import static org.openmhealth.schema.domain.omh.BodyWeight.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; -import static org.openmhealth.schema.domain.omh.MassUnit.*; +import static org.openmhealth.schema.domain.omh.MassUnit.KILOGRAM; +import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.BODY_WEIGHT; import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; /** * @author Chris Schaefbauer */ -public class WithingsBodyWeightDataPointMapperUnitTests extends DataPointMapperUnitTests { +public class WithingsBodyWeightDataPointMapperUnitTests + extends WithingsBodyMeasureDataPointMapperUnitTests { - WithingsBodyWeightDataPointMapper mapper = new WithingsBodyWeightDataPointMapper(); - JsonNode responseNode, responseNodeWithGoal; + private WithingsBodyWeightDataPointMapper mapper = new WithingsBodyWeightDataPointMapper(); + private JsonNode responseNodeWithGoal; + + @Override + protected WithingsBodyMeasureDataPointMapper getMapper() { + return mapper; + } + + @Override + protected WithingsBodyMeasureType getBodyMeasureType() { + return BODY_WEIGHT; + } @BeforeTest - public void initializeResponseNode() throws IOException { + public void initializeAdditionalResponseNodes() throws IOException { - responseNode = asJsonNode("org/openmhealth/shim/withings/mapper/withings-body-measures.json"); responseNodeWithGoal = asJsonNode("org/openmhealth/shim/withings/mapper/withings-body-measures-only-goal.json"); } @@ -60,8 +74,8 @@ public void asDataPointsShouldReturnCorrectDataPoints() { List> dataPointList = mapper.asDataPoints(responseNode); - testDataPoint(dataPointList.get(0), 74.126, "2015-05-31T06:06:23Z", 366956482L); - testDataPoint(dataPointList.get(1), 74.128, "2015-04-20T17:13:56Z", 347186704L); + testDataPoint(dataPointList.get(0), 74.126, "2015-05-31T06:06:23Z", "366956482"); + testDataPoint(dataPointList.get(1), 74.128, "2015-04-20T17:13:56Z", "347186704"); } @Test @@ -70,10 +84,10 @@ public void asDataPointsShouldIgnoreGoalsForBodyMeasures() { assertThat(mapper.asDataPoints(responseNodeWithGoal).size(), equalTo(0)); } - //TODO: Refactor this out with an "expectedProperties" dictionary for all the inputs and then one for all + // TODO: Refactor this out with an "expectedProperties" dictionary for all the inputs and then one for all // Withings points public void testDataPoint(DataPoint testDataPoint, double massValue, String offsetTimeString, - long externalId) { + String externalId) { BodyWeight bodyWeightExpected = new BodyWeight.Builder(new MassUnitValue(KILOGRAM, massValue)) .setEffectiveTimeFrame(OffsetDateTime.parse(offsetTimeString)) @@ -86,10 +100,8 @@ public void testDataPoint(DataPoint testDataPoint, double massValue, assertThat(testProvenance.getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); assertThat(testProvenance.getModality(), equalTo(SENSED)); - Long expectedExternalId = (Long) testDataPoint.getHeader().getAcquisitionProvenance().getAdditionalProperties() - .get("external_id"); - - assertThat(expectedExternalId, equalTo(externalId)); + assertThat(testDataPoint.getHeader().getAcquisitionProvenance().getAdditionalProperties().get("external_id"), + equalTo(externalId)); assertThat(testDataPoint.getHeader().getBodySchemaId(), equalTo(SCHEMA_ID)); } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapperUnitTests.java index 94d32d63..b3ff2d56 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyCaloriesBurnedDataPointMapperUnitTests.java @@ -17,7 +17,8 @@ package org.openmhealth.shim.withings.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; +import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -30,8 +31,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; -import static org.openmhealth.schema.domain.omh.KcalUnit.*; -import static org.openmhealth.schema.domain.omh.TimeInterval.*; +import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; @@ -58,7 +59,7 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); testDailyCaloriesBurnedDataPoint(dataPoints.get(0), 139, "2015-06-18T00:00:00-07:00", "2015-06-19T00:00:00-07:00"); @@ -70,22 +71,21 @@ public void asDataPointsShouldReturnCorrectDataPoints() { "2015-02-22T00:00:00-08:00"); } - public void testDailyCaloriesBurnedDataPoint(DataPoint caloriesBurnedDataPoint, - long expectedCaloriesBurnedValue, String expectedDateString, String expectedEndDateString) { + public void testDailyCaloriesBurnedDataPoint(DataPoint caloriesBurnedDataPoint, + long expectedCaloriesBurnedValue, String expectedStartDateString, String expectedEndDateString) { - CaloriesBurned expectedCaloriesBurned = - new CaloriesBurned.Builder(new KcalUnitValue(KILOCALORIE, expectedCaloriesBurnedValue)) - .setEffectiveTimeFrame(ofStartDateTimeAndEndDateTime(OffsetDateTime.parse(expectedDateString), + CaloriesBurned2 expectedCaloriesBurned = + new CaloriesBurned2.Builder( + KILOCALORIE.newUnitValue(expectedCaloriesBurnedValue), + ofStartDateTimeAndEndDateTime( + OffsetDateTime.parse(expectedStartDateString), OffsetDateTime.parse(expectedEndDateString))) .build(); assertThat(caloriesBurnedDataPoint.getBody(), equalTo(expectedCaloriesBurned)); assertThat(caloriesBurnedDataPoint.getHeader().getAcquisitionProvenance().getModality(), equalTo(SENSED)); - assertThat(caloriesBurnedDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), equalTo( - RESOURCE_API_SOURCE_NAME)); - assertThat(caloriesBurnedDataPoint.getHeader().getBodySchemaId(), equalTo(CaloriesBurned.SCHEMA_ID)); - + assertThat(caloriesBurnedDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), + equalTo(RESOURCE_API_SOURCE_NAME)); + assertThat(caloriesBurnedDataPoint.getHeader().getBodySchemaId(), equalTo(CaloriesBurned2.SCHEMA_ID)); } - - } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapperUnitTests.java index 4b5d20ec..7c4ee6d8 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsDailyStepCountDataPointMapperUnitTests.java @@ -18,8 +18,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.StepCount1; -import org.openmhealth.schema.domain.omh.TimeInterval; +import org.openmhealth.schema.domain.omh.StepCount2; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -32,16 +31,15 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; - /** * @author Chris Schaefbauer */ public class WithingsDailyStepCountDataPointMapperUnitTests extends DataPointMapperUnitTests { - private JsonNode responseNode; private WithingsDailyStepCountDataPointMapper mapper = new WithingsDailyStepCountDataPointMapper(); @@ -60,7 +58,7 @@ public void asDataPointsReturnsCorrectNumberOfDataPoints() { @Test public void asDataPointsReturnsCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); testDailyStepCountDataPoint(dataPoints.get(0), 2934, "2015-06-18T00:00:00-07:00", "2015-06-19T00:00:00-07:00"); testDailyStepCountDataPoint(dataPoints.get(1), 2600, "2015-06-19T00:00:00-07:00", "2015-06-20T00:00:00-07:00"); @@ -68,22 +66,22 @@ public void asDataPointsReturnsCorrectDataPoints() { testDailyStepCountDataPoint(dataPoints.get(3), 1798, "2015-02-21T00:00:00-08:00", "2015-02-22T00:00:00-08:00"); } - public void testDailyStepCountDataPoint(DataPoint stepCountDataPoint, long expectedStepCountValue, - String expectedDateString, String expectedEndDateString) { + public void testDailyStepCountDataPoint(DataPoint stepCountDataPoint, long expectedStepCountValue, + String expectedStartDateString, String expectedEndDateString) { - StepCount1 expectedStepCount = new StepCount1.Builder(expectedStepCountValue) - .setEffectiveTimeFrame(TimeInterval - .ofStartDateTimeAndEndDateTime(OffsetDateTime.parse(expectedDateString), - OffsetDateTime.parse(expectedEndDateString))) + StepCount2 expectedStepCount = new StepCount2.Builder( + expectedStepCountValue, + ofStartDateTimeAndEndDateTime( + OffsetDateTime.parse(expectedStartDateString), + OffsetDateTime.parse(expectedEndDateString))) .build(); - StepCount1 testStepCount = stepCountDataPoint.getBody(); + StepCount2 stepCount = stepCountDataPoint.getBody(); - assertThat(testStepCount, equalTo(expectedStepCount)); + assertThat(stepCount, equalTo(expectedStepCount)); assertThat(stepCountDataPoint.getHeader().getAcquisitionProvenance().getModality(), equalTo(SENSED)); - assertThat(stepCountDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), equalTo( - RESOURCE_API_SOURCE_NAME)); - assertThat(stepCountDataPoint.getHeader().getBodySchemaId(), equalTo(StepCount1.SCHEMA_ID)); - + assertThat(stepCountDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), + equalTo(RESOURCE_API_SOURCE_NAME)); + assertThat(stepCountDataPoint.getHeader().getBodySchemaId(), equalTo(StepCount2.SCHEMA_ID)); } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapperUnitTests.java index d783ae7f..d465d2bd 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsHeartRateDataPointMapperUnitTests.java @@ -16,24 +16,21 @@ package org.openmhealth.shim.withings.mapper; -import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DataPointAcquisitionProvenance; import org.openmhealth.schema.domain.omh.DataPointModality; import org.openmhealth.schema.domain.omh.HeartRate; -import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; -import org.testng.annotations.BeforeTest; +import org.openmhealth.shim.withings.domain.WithingsBodyMeasureType; import org.testng.annotations.Test; -import java.io.IOException; import java.time.OffsetDateTime; -import static java.util.Collections.singletonList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.openmhealth.schema.domain.omh.DataPointModality.SELF_REPORTED; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; -import static org.openmhealth.schema.domain.omh.HeartRate.*; +import static org.openmhealth.schema.domain.omh.HeartRate.SCHEMA_ID; +import static org.openmhealth.shim.withings.domain.WithingsBodyMeasureType.HEART_RATE; import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; @@ -41,40 +38,46 @@ * @author Chris Schaefbauer * @author Emerson Farrugia */ -public class WithingsHeartRateDataPointMapperUnitTests extends DataPointMapperUnitTests { +public class WithingsHeartRateDataPointMapperUnitTests extends WithingsBodyMeasureDataPointMapperUnitTests { private WithingsHeartRateDataPointMapper mapper = new WithingsHeartRateDataPointMapper(); - protected JsonNode responseNode; + @Override + protected WithingsBodyMeasureDataPointMapper getMapper() { + return mapper; + } - @BeforeTest - public void initializeDataPoints() throws IOException { - - responseNode = asJsonNode("/org/openmhealth/shim/withings/mapper/withings-body-measures.json"); + @Override + protected WithingsBodyMeasureType getBodyMeasureType() { + return HEART_RATE; } @Test public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { - assertThat(mapper.asDataPoints(singletonList(responseNode)).size(), equalTo(3)); + assertThat(mapper.asDataPoints(responseNode).size(), equalTo(3)); } @Test public void asDataPointsShouldReturnCorrectSensedDataPoint() { - assertThatDataPointMatches(mapper.asDataPoints(responseNode).get(0), 41.0, "2015-05-31T06:06:23Z", 366956482L, + assertThatDataPointMatches(mapper.asDataPoints(responseNode).get(0), 41.0, "2015-05-31T06:06:23Z", "366956482", null, SENSED); } @Test public void asDataPointsShouldReturnCorrectSelfReportedDataPoint() { - assertThatDataPointMatches(mapper.asDataPoints(responseNode).get(2), 47.0, "2015-02-26T21:57:17Z", 321858727L, + assertThatDataPointMatches(mapper.asDataPoints(responseNode).get(2), 47.0, "2015-02-26T21:57:17Z", "321858727", "a few minutes after a walk", SELF_REPORTED); } - private void assertThatDataPointMatches(DataPoint actualDataPoint, double expectedHeartRateValue, - String expectedDateTimeAsString, long expectedExternalId, String expectedComment, + private void assertThatDataPointMatches( + DataPoint actualDataPoint, + double expectedHeartRateValue, + String expectedDateTimeAsString, + String expectedExternalId, + String expectedComment, DataPointModality expectedModality) { HeartRate.Builder expectedHeartRateBuilder = new HeartRate.Builder(expectedHeartRateValue) diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapperUnitTests.java index 92ad8a7c..4529e505 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayCaloriesBurnedDataPointMapperUnitTests.java @@ -17,10 +17,8 @@ package org.openmhealth.shim.withings.mapper; import com.fasterxml.jackson.databind.JsonNode; -import org.openmhealth.schema.domain.omh.CaloriesBurned; +import org.openmhealth.schema.domain.omh.CaloriesBurned2; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.KcalUnitValue; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -31,7 +29,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.openmhealth.schema.domain.omh.CaloriesBurned.SCHEMA_ID; +import static org.openmhealth.schema.domain.omh.CaloriesBurned2.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; @@ -62,7 +60,7 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); testIntradayCaloriesBurnedDataPoint(dataPoints.get(0), 1, "2015-06-20T00:04:00Z", 60L); testIntradayCaloriesBurnedDataPoint(dataPoints.get(1), 2, "2015-06-20T00:29:00Z", 60L); @@ -70,21 +68,23 @@ public void asDataPointsShouldReturnCorrectDataPoints() { testIntradayCaloriesBurnedDataPoint(dataPoints.get(3), 7, "2015-06-20T00:41:00Z", 60L); } - public void testIntradayCaloriesBurnedDataPoint(DataPoint caloriesBurnedDataPoint, + public void testIntradayCaloriesBurnedDataPoint( + DataPoint caloriesBurnedDataPoint, long expectedCaloriesBurnedValue, - String expectedDateString, Long expectedDuration) { + String expectedStartDateTimeString, Long expectedDuration) { - CaloriesBurned expectedCaloriesBurned = - new CaloriesBurned.Builder(new KcalUnitValue(KILOCALORIE, expectedCaloriesBurnedValue)) - .setEffectiveTimeFrame( - ofStartDateTimeAndDuration(OffsetDateTime.parse(expectedDateString), - new DurationUnitValue(SECOND, expectedDuration))).build(); + CaloriesBurned2 expectedCaloriesBurned = + new CaloriesBurned2.Builder( + KILOCALORIE.newUnitValue(expectedCaloriesBurnedValue), + ofStartDateTimeAndDuration( + OffsetDateTime.parse(expectedStartDateTimeString), + SECOND.newUnitValue(expectedDuration))) + .build(); assertThat(caloriesBurnedDataPoint.getBody(), equalTo(expectedCaloriesBurned)); - assertThat(caloriesBurnedDataPoint.getHeader().getAcquisitionProvenance().getModality(), equalTo(SENSED)); - assertThat(caloriesBurnedDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), equalTo( - RESOURCE_API_SOURCE_NAME)); assertThat(caloriesBurnedDataPoint.getHeader().getBodySchemaId(), equalTo(SCHEMA_ID)); + assertThat(caloriesBurnedDataPoint.getHeader().getAcquisitionProvenance().getModality(), equalTo(SENSED)); + assertThat(caloriesBurnedDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), + equalTo(RESOURCE_API_SOURCE_NAME)); } - } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapperUnitTests.java index 52a94549..53bf4f1e 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsIntradayStepCountDataPointMapperUnitTests.java @@ -18,8 +18,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; -import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.StepCount1; +import org.openmhealth.schema.domain.omh.StepCount2; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -33,7 +32,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; -import static org.openmhealth.schema.domain.omh.StepCount1.SCHEMA_ID; import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; import static org.openmhealth.shim.withings.mapper.WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME; @@ -61,7 +59,7 @@ public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); testIntradayStepCountDataPoint(dataPoints.get(0), 21, "2015-06-20T00:04:00Z", 60L); testIntradayStepCountDataPoint(dataPoints.get(1), 47, "2015-06-20T00:29:00Z", 60L); @@ -69,23 +67,23 @@ public void asDataPointsShouldReturnCorrectDataPoints() { testIntradayStepCountDataPoint(dataPoints.get(3), 74, "2015-06-20T00:41:00Z", 60L); } - public void testIntradayStepCountDataPoint(DataPoint stepCountDataPoint, long expectedStepCountValue, - String expectedDateString, Long expectedDuration) { - - StepCount1 expectedStepCount = new StepCount1.Builder(expectedStepCountValue).setEffectiveTimeFrame( - ofStartDateTimeAndDuration(OffsetDateTime.parse(expectedDateString), - new DurationUnitValue(SECOND, expectedDuration))) - .build(); - - StepCount1 testStepCount = stepCountDataPoint.getBody(); - - assertThat(testStepCount, equalTo(expectedStepCount)); + public void testIntradayStepCountDataPoint( + DataPoint stepCountDataPoint, + long expectedStepCountValue, + String expectedStartDateTimeString, Long expectedDuration) { + + StepCount2 expectedStepCount = + new StepCount2.Builder( + expectedStepCountValue, + ofStartDateTimeAndDuration( + OffsetDateTime.parse(expectedStartDateTimeString), + SECOND.newUnitValue(expectedDuration))) + .build(); + + assertThat(stepCountDataPoint.getBody(), equalTo(expectedStepCount)); + assertThat(stepCountDataPoint.getHeader().getBodySchemaId(), equalTo(StepCount2.SCHEMA_ID)); assertThat(stepCountDataPoint.getHeader().getAcquisitionProvenance().getModality(), equalTo(SENSED)); - assertThat(stepCountDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), equalTo( - RESOURCE_API_SOURCE_NAME)); - assertThat(stepCountDataPoint.getHeader().getBodySchemaId(), equalTo(SCHEMA_ID)); - + assertThat(stepCountDataPoint.getHeader().getAcquisitionProvenance().getSourceName(), + equalTo(RESOURCE_API_SOURCE_NAME)); } - - } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapperUnitTests.java index 9085b960..71798dfe 100644 --- a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapperUnitTests.java +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsSleepDurationDataPointMapperUnitTests.java @@ -20,15 +20,15 @@ import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DataPointAcquisitionProvenance; import org.openmhealth.schema.domain.omh.DurationUnitValue; -import org.openmhealth.schema.domain.omh.SleepDuration1; +import org.openmhealth.schema.domain.omh.SleepDuration2; import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.IOException; -import java.time.OffsetDateTime; import java.util.List; +import static java.time.OffsetDateTime.parse; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; @@ -53,36 +53,28 @@ public void initializeResponseNode() throws IOException { @Test public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { - assertThat(mapper.asDataPoints(responseNode).size(), equalTo(1)); + assertThat(mapper.asDataPoints(responseNode).size(), equalTo(2)); } @Test public void asDataPointsShouldReturnCorrectDataPoints() { - List> dataPoints = mapper.asDataPoints(responseNode); + List> dataPoints = mapper.asDataPoints(responseNode); - SleepDuration1 expectedSleepDuration = new SleepDuration1.Builder(new DurationUnitValue(SECOND, 37460)) - .setEffectiveTimeFrame(ofStartDateTimeAndEndDateTime(OffsetDateTime.parse("2014-09-12T11:34:19Z"), - OffsetDateTime.parse("2014-09-12T17:22:57Z"))) + SleepDuration2 expectedSleepDuration = new SleepDuration2.Builder( + new DurationUnitValue(SECOND, 11160), + ofStartDateTimeAndEndDateTime(parse("2017-09-02T00:19:00+01:00"), parse("2017-09-02T03:25:00+01:00")) + ) .build(); - expectedSleepDuration.setAdditionalProperty("wakeup_count", 3); - expectedSleepDuration.setAdditionalProperty("light_sleep_duration", new DurationUnitValue(SECOND, 18540)); - expectedSleepDuration.setAdditionalProperty("deep_sleep_duration", new DurationUnitValue(SECOND, 8460)); - expectedSleepDuration.setAdditionalProperty("rem_sleep_duration", new DurationUnitValue(SECOND, 10460)); - expectedSleepDuration.setAdditionalProperty("duration_to_sleep", new DurationUnitValue(SECOND, 420)); - + assertThat(dataPoints.get(0).getHeader().getBodySchemaId(), equalTo(SleepDuration2.SCHEMA_ID)); assertThat(dataPoints.get(0).getBody(), equalTo(expectedSleepDuration)); - assertThat(dataPoints.get(0).getBody().getAdditionalProperties(), - equalTo(expectedSleepDuration.getAdditionalProperties())); - DataPointAcquisitionProvenance acquisitionProvenance = dataPoints.get(0).getHeader().getAcquisitionProvenance(); assertThat(acquisitionProvenance.getSourceName(), equalTo(WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME)); assertThat(acquisitionProvenance.getModality(), equalTo(SENSED)); - assertThat(acquisitionProvenance.getAdditionalProperties().get("external_id"), equalTo(16616514L)); - assertThat(acquisitionProvenance.getAdditionalProperties().get("device_name"), equalTo("Aura")); - assertThat(dataPoints.get(0).getHeader().getBodySchemaId(), equalTo(SleepDuration1.SCHEMA_ID)); + assertThat(acquisitionProvenance.getAdditionalProperties().get("external_id"), equalTo("123")); + assertThat(acquisitionProvenance.getAdditionalProperties().get("device_name"), equalTo("Activity tracker")); } } diff --git a/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsSleepEpisodeDataPointMapperUnitTests.java b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsSleepEpisodeDataPointMapperUnitTests.java new file mode 100644 index 00000000..53deff11 --- /dev/null +++ b/shim-server/src/test/java/org/openmhealth/shim/withings/mapper/WithingsSleepEpisodeDataPointMapperUnitTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2017 Open mHealth + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmhealth.shim.withings.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.openmhealth.schema.domain.omh.*; +import org.openmhealth.shim.common.mapper.DataPointMapperUnitTests; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.List; + +import static java.time.OffsetDateTime.parse; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; +import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; +import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; + + +/** + * @author Emerson Farrugia + */ +public class WithingsSleepEpisodeDataPointMapperUnitTests extends DataPointMapperUnitTests { + + private JsonNode responseNode; + private WithingsSleepEpisodeDataPointMapper mapper = new WithingsSleepEpisodeDataPointMapper(); + + @BeforeTest + public void initializeResponseNode() throws IOException { + + responseNode = asJsonNode("org/openmhealth/shim/withings/mapper/withings-sleep-summary.json"); + } + + @Test + public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { + + assertThat(mapper.asDataPoints(responseNode).size(), equalTo(2)); + } + + @Test + public void asDataPointsShouldReturnCorrectDataPoints() { + + List> dataPoints = mapper.asDataPoints(responseNode); + + SleepEpisode expectedSleepEpisode = new SleepEpisode.Builder( + ofStartDateTimeAndEndDateTime(parse("2017-09-02T00:19:00+01:00"), parse("2017-09-02T03:25:00+01:00")) + ) + .setLatencyToSleepOnset(new DurationUnitValue(SECOND, 240)) + .setLatencyToArising(new DurationUnitValue(SECOND, 0)) + .setTotalSleepTime(new DurationUnitValue(SECOND, 11160)) + .setNumberOfAwakenings(4) + .build(); + + assertThat(dataPoints.get(0).getHeader().getBodySchemaId(), equalTo(SleepEpisode.SCHEMA_ID)); + assertThat(dataPoints.get(0).getBody(), equalTo(expectedSleepEpisode)); + + DataPointAcquisitionProvenance acquisitionProvenance = dataPoints.get(0).getHeader().getAcquisitionProvenance(); + + assertThat(acquisitionProvenance.getSourceName(), equalTo(WithingsDataPointMapper.RESOURCE_API_SOURCE_NAME)); + assertThat(acquisitionProvenance.getModality(), equalTo(SENSED)); + assertThat(acquisitionProvenance.getAdditionalProperties().get("external_id"), equalTo("123")); + assertThat(acquisitionProvenance.getAdditionalProperties().get("device_name"), equalTo("Activity tracker")); + } +} diff --git a/shim-server/src/test/resources/fitbit-activity-catalog.json b/shim-server/src/test/resources/fitbit-activity-catalog.json deleted file mode 100644 index 7f8a2866..00000000 --- a/shim-server/src/test/resources/fitbit-activity-catalog.json +++ /dev/null @@ -1,9314 +0,0 @@ -{"shim": null, "timeStamp": 1408638117, "body": [ - { - "result": { - "date": "2014-08-19", - "content": { - "categories": [ - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 3016, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "6 - 8 inch step" - }, - { - "id": 3017, - "maxSpeedMPH": -1, - "mets": 10, - "minSpeedMPH": -1, - "name": "10 - 12 inch step" - } - ], - "hasSpeed": false, - "id": 90004, - "name": "Aerobic step" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3015, - "mets": 6.5, - "name": "Aerobic, general" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 3020, - "maxSpeedMPH": -1, - "mets": 5, - "minSpeedMPH": -1, - "name": "low impact" - }, - { - "id": 3021, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "high impact" - } - ], - "hasSpeed": false, - "id": 90005, - "name": "Aerobics" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3050, - "mets": 5.5, - "name": "Anishinaabe Jingle Dancing or other traditional American Indian dancing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3010, - "mets": 4.800000190734863, - "name": "Ballet or modem, twist, jazz, tap, jitterbug" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3031, - "mets": 5, - "name": "Ballroom, fast (disco, folk, square), line dancing, Irish step dancing, polka, contra, country" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3040, - "mets": 3, - "name": "Ballroom, slow (e.g. waltz, foxtrot, slow dancing), samba, tango, 19th C, mambo, chacha" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3025, - "mets": 4.5, - "name": "General, Greek, Middle Eastern, hula, flamenco, belly, swing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 56001, - "mets": 5.5, - "name": "Zumba" - } - ], - "id": 0, - "name": "Dancing" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4010, - "mets": 4, - "name": "Digging worms, with shovel" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4030, - "mets": 2.5, - "name": "Fishing from boat, sitting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4020, - "mets": 4, - "name": "Fishing from river bank and walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4040, - "mets": 3.5, - "name": "Fishing from river bank, standing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4050, - "mets": 6, - "name": "Fishing in stream, in waders" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4001, - "mets": 3, - "name": "Fishing, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4060, - "mets": 2, - "name": "Fishing, ice, sitting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4070, - "mets": 2.5, - "name": "Hunting, bow and arrow or crossbow" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4080, - "mets": 6, - "name": "Hunting, deer, elk, large game" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4090, - "mets": 2.5, - "name": "Hunting, duck, wading" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4100, - "mets": 5, - "name": "Hunting, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4110, - "mets": 6, - "name": "Hunting, pheasants or grouse" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4120, - "mets": 5, - "name": "Hunting, rabbit, squirrel, prairie chick, raccoon, small game" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4130, - "mets": 2.5, - "name": "Pistol shooting or trap shooting, standing" - } - ], - "id": 1, - "name": "Fishing and Hunting" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5149, - "mets": 2.5, - "name": "Building a fire inside" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5010, - "mets": 3.299999952316284, - "name": "Carpet sweeping, sweeping floors" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5056, - "mets": 7.5, - "name": "Carrying groceries upstairs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5181, - "mets": 3, - "name": "Carrying small children" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5161, - "mets": 2, - "name": "Change light bulb" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5185, - "mets": 2.5, - "name": "Child care: sitting/kneeling - dressing, bathing, grooming, feeding, occasional lifting of child-light effort, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5186, - "mets": 3, - "name": "Child care: standing - dressing, bathing, grooming, feeding, occasional lifting of child-light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5020, - "mets": 3, - "name": "Cleaning, heavy or major (e.g. wash car, wash windows, clean garage), vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5030, - "mets": 3, - "name": "Cleaning, house or cabin, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5040, - "mets": 2.5, - "name": "Cleaning, light (dusting, straightening up, changing linen, carrying out trash)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5166, - "mets": 3, - "name": "Closing windows and locking doors" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5057, - "mets": 3, - "name": "Cooking Indian bread on an outside stove" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5052, - "mets": 2.5, - "name": "Cooking or food preparation" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5187, - "mets": 4, - "name": "Elder care, disabled adult, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5053, - "mets": 2.5, - "name": "Feeding animals" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5060, - "mets": 2.299999952316284, - "name": "Food shopping with or without a grocery cart, standing or walking" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 5025, - "maxSpeedMPH": -1, - "mets": 2.5, - "minSpeedMPH": -1, - "name": "light effort" - }, - { - "id": 5026, - "maxSpeedMPH": -1, - "mets": 3.5, - "minSpeedMPH": -1, - "name": "moderate effort" - }, - { - "id": 5027, - "maxSpeedMPH": -1, - "mets": 4, - "minSpeedMPH": -1, - "name": "vigorous effort" - } - ], - "hasSpeed": false, - "id": 90006, - "name": "Household Chores" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5070, - "mets": 2.299999952316284, - "name": "Ironing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5080, - "mets": 1.5, - "name": "Knitting, sewing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5090, - "mets": 2, - "name": "Laundry, fold or hang clothes, put clothes in washer or dryer, packing suitcase" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17031, - "mets": 3, - "name": "Loading /unloading a car" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5100, - "mets": 2, - "name": "Making a bed" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5110, - "mets": 5, - "name": "Maple syruping/sugar bushing (including carrying buckets, carrying wood)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5021, - "mets": 3.5, - "name": "Mopping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5120, - "mets": 6, - "name": "Moving furniture, household items, carrying boxes" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5150, - "mets": 9, - "name": "Moving household items upstairs, carrying boxes or furniture" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5160, - "mets": 2, - "name": "Pumping gas, change light bulb, etc.)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5095, - "mets": 2.299999952316284, - "name": "Putting away clothes, gathering clothes to pack, putting away laundry" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5055, - "mets": 2.5, - "name": "Putting away groceries (e.g. carrying groceries, shopping without a grocery cart), carrying packages" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5147, - "mets": 3, - "name": "Putting away household items - moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5165, - "mets": 3, - "name": "Readying to leave" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5130, - "mets": 3.799999952316284, - "name": "Scrubbing floors, on hands and knees, scrubbing bathroom, bathtub" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5051, - "mets": 2.5, - "name": "Serving food, setting table" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5065, - "mets": 2.299999952316284, - "name": "Shopping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5190, - "mets": 2.5, - "name": "Sit, playing with animals, light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5170, - "mets": 2.5, - "name": "Sitting - playing with child(ren) - light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5191, - "mets": 2.799999952316284, - "name": "Stand, playing with animals, light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5195, - "mets": 3.5, - "name": "Standing - bathing dog" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5146, - "mets": 3.5, - "name": "Standing - packing/unpacking boxes, occasional lifting of household items light - moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5171, - "mets": 2.799999952316284, - "name": "Standing - playing with child(ren) - light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5140, - "mets": 4, - "name": "Sweeping garage, sidewalk or outside of house" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5043, - "mets": 3.5, - "name": "Vacuuming" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5175, - "mets": 4, - "name": "Walk/run - playing with child(ren) - moderate, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5180, - "mets": 5, - "name": "Walk/run - playing with child(ren) - vigorous, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5192, - "mets": 2.799999952316284, - "name": "Walk/run, playing with animals, light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5193, - "mets": 4, - "name": "Walk/run, playing with animals, moderate, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5194, - "mets": 5, - "name": "Walk/run, playing with animals, vigorous, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5041, - "mets": 2.299999952316284, - "name": "Washing dishes - standing or in general (not broken into stand/walk components)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5042, - "mets": 2.5, - "name": "Washing dishes; clearing dishes from table - walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5148, - "mets": 2.5, - "name": "Watering plants" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5081, - "mets": 1.5, - "name": "Wrapping presents" - } - ], - "id": 2, - "name": "Home activities" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6010, - "mets": 3, - "name": "Airplane repair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6020, - "mets": 4, - "name": "Automobile body work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6030, - "mets": 3, - "name": "Automobile repair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6060, - "mets": 4.5, - "name": "Carpentry, finishing or refinishing cabinets or furniture" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6040, - "mets": 3, - "name": "Carpentry, general, workshop" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6050, - "mets": 6, - "name": "Carpentry, outside house, installing rain gutters, building a fence" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6070, - "mets": 7.5, - "name": "Carpentry, sawing hardwood" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6080, - "mets": 5, - "name": "Caulking, chinking log cabin" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6090, - "mets": 4.5, - "name": "Caulking, except log cabin" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6100, - "mets": 5, - "name": "Cleaning gutters" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6110, - "mets": 5, - "name": "Excavating garage" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6120, - "mets": 5, - "name": "Hanging storm windows" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6130, - "mets": 4.5, - "name": "Laying or removing carpet" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6140, - "mets": 4.5, - "name": "Laying tile or linoleum, repairing appliances" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6165, - "mets": 4.5, - "name": "Painting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6150, - "mets": 5, - "name": "Painting, outside home" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6160, - "mets": 3, - "name": "Painting, papering, plastering, scraping, inside house, hanging sheet rock, remodeling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6170, - "mets": 3, - "name": "Put on and removal of tarp - sailboat" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6180, - "mets": 6, - "name": "Roofing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6190, - "mets": 4.5, - "name": "Sanding floors with a power sander" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6200, - "mets": 4.5, - "name": "Scraping and painting sailboat or powerboat" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6210, - "mets": 5, - "name": "Spreading dirt with a shovel" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6220, - "mets": 4.5, - "name": "Washing and waxing hull of sailboat, car, powerboat, airplane" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6230, - "mets": 4.5, - "name": "Washing fence, painting fence" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6240, - "mets": 3, - "name": "Wiring, plumbing" - } - ], - "id": 3, - "name": "Home repair" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8010, - "mets": 5, - "name": "Carrying, loading or stacking wood, loading/unloading or carrying lumber" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8020, - "mets": 6, - "name": "Chopping wood, splitting logs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8030, - "mets": 5, - "name": "Clearing land, hauling branches, wheelbarrow chores" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8040, - "mets": 5, - "name": "Digging sandbox" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8050, - "mets": 5, - "name": "Digging, spading, filling garden, composting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8060, - "mets": 6, - "name": "Gardening with heavy power tools, tilling a garden, chain saw" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8245, - "mets": 4, - "name": "Gardening, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8250, - "mets": 3, - "name": "Implied walking/standing -picking up yard, light, picking flowers or vegetables" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8080, - "mets": 5, - "name": "Laying crushed rock" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8090, - "mets": 5, - "name": "Laying sod" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8095, - "mets": 5.5, - "name": "Mowing lawn, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8100, - "mets": 2.5, - "name": "Mowing lawn, riding mower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8110, - "mets": 6, - "name": "Mowing lawn, walk, hand mower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8120, - "mets": 5.5, - "name": "Mowing lawn, walk, power mower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8130, - "mets": 4.5, - "name": "Operating snow blower, walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8246, - "mets": 3, - "name": "Picking fruit off trees, picking fruits/vegetables, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8140, - "mets": 4.5, - "name": "Planting seedlings, shrubs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8150, - "mets": 4.5, - "name": "Planting trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8160, - "mets": 4.300000190734863, - "name": "Raking lawn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8170, - "mets": 4, - "name": "Raking roof with snow rake" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8180, - "mets": 3, - "name": "Riding snow blower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8190, - "mets": 4, - "name": "Sacking grass, leaves" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8200, - "mets": 6, - "name": "Shoveling snow, by hand" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8210, - "mets": 4.5, - "name": "Trimming shrubs or trees, manual cutter" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8215, - "mets": 3.5, - "name": "Trimming shrubs or trees, power cutter, using leaf blower, edger" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8220, - "mets": 2.5, - "name": "Walking, applying fertilizer or seeding a lawn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8251, - "mets": 3, - "name": "Walking, gathering gardening tools" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8230, - "mets": 1.5, - "name": "Watering lawn or garden, standing or walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8240, - "mets": 4.5, - "name": "Weeding, cultivating garden" - } - ], - "id": 5, - "name": "Lawn and garden" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7010, - "mets": 1, - "name": "Lying quietly and watching television" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7011, - "mets": 1, - "name": "Lying quietly, doing nothing, lying in bed awake, listening to music (not talking or reading)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7075, - "mets": 1, - "name": "Meditating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7070, - "mets": 1, - "name": "Reclining - reading" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7060, - "mets": 1, - "name": "Reclining - talking or talking on phone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7050, - "mets": 1, - "name": "Reclining - writing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7020, - "mets": 1, - "name": "Sitting quietly and watching television" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7021, - "mets": 1, - "name": "Sitting quietly, sitting smoking, listening to music (not talking or reading), watching a movie in a theater" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7030, - "mets": 0.8999999761581421, - "name": "Sleeping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7040, - "mets": 1.2000000476837158, - "name": "Standing quietly (standing in a line)" - } - ], - "id": 4, - "name": "Leisure and inactivity" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9110, - "mets": 2.5, - "name": "Camping involving standing, walking, sitting, light-to-moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9100, - "mets": 1.5, - "name": "Retreat/family reunion activities involving sitting, relaxing, talking, eating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9075, - "mets": 1.5, - "name": "Sitting - arts and crafts, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9065, - "mets": 1.7999999523162842, - "name": "Sitting - in class, general, including note-taking or class discussion" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9030, - "mets": 1.2999999523162842, - "name": "Sitting - reading, book, newspaper, etc." - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9060, - "mets": 1.7999999523162842, - "name": "Sitting - studying, general, including reading and/or writing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9055, - "mets": 1.5, - "name": "Sitting - talking or talking on the phone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9040, - "mets": 1.7999999523162842, - "name": "Sitting - writing, desk work, typing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9080, - "mets": 2, - "name": "Sitting -arts and crafts, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9010, - "mets": 1.5, - "name": "Sitting -card playing, playing board games" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9115, - "mets": 1.5, - "name": "Sitting at a sporting event, spectator" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9085, - "mets": 1.7999999523162842, - "name": "Standing - arts and crafts, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9090, - "mets": 3, - "name": "Standing - arts and crafts, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9095, - "mets": 3.5, - "name": "Standing - arts and crafts, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9071, - "mets": 2, - "name": "Standing - miscellaneous" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9070, - "mets": 1.7999999523162842, - "name": "Standing - reading" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9050, - "mets": 1.7999999523162842, - "name": "Standing - talking or talking on the phone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9020, - "mets": 2.299999952316284, - "name": "Standing -drawing (writing), casino gambling, duplicating machine" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9105, - "mets": 2, - "name": "Touring/traveling/vacation involving walking and riding" - } - ], - "id": 6, - "name": "Miscellaneous" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10010, - "mets": 1.7999999523162842, - "name": "Accordion" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10020, - "mets": 2, - "name": "Cello" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10030, - "mets": 2.5, - "name": "Conducting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10040, - "mets": 4, - "name": "Drums" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10050, - "mets": 2, - "name": "Flute (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10120, - "mets": 2, - "name": "Guitar, classical, folk (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10125, - "mets": 3, - "name": "Guitar, rock and roll band (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10060, - "mets": 2, - "name": "Horn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10135, - "mets": 3.5, - "name": "Marching band, drum major (walking)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10130, - "mets": 4, - "name": "Marching band, playing an instrument, baton twirling (walking)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10070, - "mets": 2.5, - "name": "Piano or organ" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10080, - "mets": 3.5, - "name": "Trombone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10090, - "mets": 2.5, - "name": "Trumpet" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10100, - "mets": 2.5, - "name": "Violin" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10110, - "mets": 2, - "name": "Woodwind" - } - ], - "id": 7, - "name": "Music playing" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11010, - "mets": 4, - "name": "Bakery, general, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11015, - "mets": 2.5, - "name": "Bakery, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11020, - "mets": 2.299999952316284, - "name": "Bookbinding" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11030, - "mets": 6, - "name": "Building road (including hauling debris, driving heavy machinery)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11035, - "mets": 2, - "name": "Building road, directing traffic (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11040, - "mets": 3.5, - "name": "Carpentry, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11050, - "mets": 8, - "name": "Carrying heavy loads, such as bricks" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11060, - "mets": 8, - "name": "Carrying moderate loads up stairs, moving boxes (16-40 pounds)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11070, - "mets": 2.5, - "name": "Chambermaid, making bed (nursing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11120, - "mets": 5.5, - "name": "Construction, outside, remodeling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11130, - "mets": 3.5, - "name": "Electrical work, plumbing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11370, - "mets": 4.5, - "name": "Furriery" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11380, - "mets": 6, - "name": "Horse grooming" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11615, - "mets": 4, - "name": "Lifting items continuously, 10 - 20 lbs, with limited walking or resting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11420, - "mets": 3.5, - "name": "Locksmith" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11480, - "mets": 7, - "name": "Masonry concrete" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11485, - "mets": 4, - "name": "Massage therapist (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11490, - "mets": 7.5, - "name": "Moving, pushing heavy objects, 75 lbs or more (desks, moving van work)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11500, - "mets": 2.5, - "name": "Operating heavy duty equipment/automated, not driving" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11510, - "mets": 4.5, - "name": "Orange grove work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11520, - "mets": 2.299999952316284, - "name": "Printing (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11530, - "mets": 2.5, - "name": "Shoe repair, general" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 11540, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "Digging ditches" - }, - { - "id": 11550, - "maxSpeedMPH": -1, - "mets": 9, - "minSpeedMPH": -1, - "name": "Heavy lifting (more than 16 pounds/minute)" - }, - { - "id": 11560, - "maxSpeedMPH": -1, - "mets": 6, - "minSpeedMPH": -1, - "name": "Light lifting (less than 10 pounds/minute)" - }, - { - "id": 11570, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "Moderate lifting (10 to 15 pounds/minute)" - } - ], - "hasSpeed": false, - "id": 90007, - "name": "Shoveling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11580, - "mets": 1.5, - "name": "Sitting - light office work, general (chemistry lab work, light use of hand tools, watch repair or micro-assembly, light assembly/repair), sitting, reading, driving at work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11585, - "mets": 1.5, - "name": "Sitting - meetings, general, and/or with talking involved, eating at a business meeting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11590, - "mets": 2.5, - "name": "Sitting; moderate (heavy levers, riding mower/forklift, crane operation), teaching stretching or yoga" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11495, - "mets": 12, - "name": "Skindiving or SCUBA diving as a frogman (Navy Seal)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11600, - "mets": 2.299999952316284, - "name": "Standing; light (bartending, store clerk, assembling, filing, duplicating, putting up a Christmas tree), standing and talking at work, changing clothes when teaching physical education" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11610, - "mets": 3, - "name": "Standing; light/moderate (assemble/repair heavy parts, welding, stocking, auto repair, pack boxes for moving, etc.), patient care (as in nursing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11620, - "mets": 3.5, - "name": "Standing; moderate (assembling at fast rate, intermittent, lifting 50 lbs, hitch/twisting ropes)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11630, - "mets": 4, - "name": "Standing; moderate/heavy (lifting more than 50 lbs, masonry, painting, paper hanging)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11875, - "mets": 4, - "name": "Teach physical education, exercise, sports classes (non-sport play)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11876, - "mets": 6.5, - "name": "Teach physical education, exercise, sports classes (participate in the class)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11766, - "mets": 6.5, - "name": "Truck driving, loading and unloading truck (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11770, - "mets": 1.5, - "name": "Typing, electric, manual or computer" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11780, - "mets": 6, - "name": "Using heavy power tools such as pneumatic tools (jackhammers, drills. etc.)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11790, - "mets": 8, - "name": "Using heavy tools (not power) such as shovel, pick, tunnel bar, spade" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 11810, - "maxSpeedMPH": -1, - "mets": 4.5, - "minSpeedMPH": -1, - "name": "Carrying under 25 pounds" - }, - { - "id": 11820, - "maxSpeedMPH": -1, - "mets": 5, - "minSpeedMPH": -1, - "name": "Carrying 25 to 49 pounds" - }, - { - "id": 11830, - "maxSpeedMPH": -1, - "mets": 6.5, - "minSpeedMPH": -1, - "name": "Carrying 50 to 74 pounds" - }, - { - "id": 11840, - "maxSpeedMPH": -1, - "mets": 7.5, - "minSpeedMPH": -1, - "name": "Carrying 75 to 99 pounds" - }, - { - "id": 11850, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "Carrying over 100 pounds" - } - ], - "hasSpeed": false, - "id": 90008, - "name": "Walking while carrying things" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11870, - "mets": 3, - "name": "Working in scene shop, theater actor, backstage employee" - } - ], - "id": 8, - "name": "Occupations", - "subCategories": [ - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11090, - "mets": 6.5, - "name": "Coal mining, erecting supports" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11100, - "mets": 6, - "name": "Coal mining, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11080, - "mets": 6.5, - "name": "Drilling coal, rock" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11110, - "mets": 7, - "name": "Shoveling coal" - } - ], - "id": 9, - "name": "Coal Mining" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11121, - "mets": 3, - "name": "Buffing the floor with electric buffer" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11122, - "mets": 2.5, - "name": "Cleaning sink and toilet, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11123, - "mets": 2.5, - "name": "Dusting, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11124, - "mets": 4, - "name": "Feathering arena floor, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11125, - "mets": 3.5, - "name": "General cleaning, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11126, - "mets": 3.5, - "name": "Mopping, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11127, - "mets": 3, - "name": "Taking out the trash, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11128, - "mets": 2.5, - "name": "Vacuuming, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11129, - "mets": 3, - "name": "Vacuuming, moderate effort" - } - ], - "id": 10, - "name": "Custodial Work" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11140, - "mets": 8, - "name": "Baling hay, cleaning barn, poultry work, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11151, - "mets": 4, - "name": "Chasing cattle or other livestock on horseback, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11152, - "mets": 2, - "name": "Chasing cattle or other livestock, driving, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11150, - "mets": 3.5, - "name": "Chasing cattle, non-strenuous (walking), moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11160, - "mets": 2.5, - "name": "Driving a harvester, cutting hay, irrigation work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11170, - "mets": 2.5, - "name": "Driving a tractor" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11190, - "mets": 4.5, - "name": "Feeding cattle, horses" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11180, - "mets": 4, - "name": "Feeding small animals" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11200, - "mets": 8, - "name": "Forking straw bales, cleaning corral or bars, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11191, - "mets": 4.5, - "name": "Hauling water for animals, general hauling water" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11210, - "mets": 3, - "name": "Milking by hand, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11220, - "mets": 1.5, - "name": "Milking by machine, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11230, - "mets": 5.5, - "name": "Shoveling grain, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11192, - "mets": 6, - "name": "Taking care of animals (grooming, brushing, shearing sheep, assisting with birthing, medical care, branding)" - } - ], - "id": 11, - "name": "Farming" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11245, - "mets": 11, - "name": "Fire fighter, climbing ladder with full gear" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11240, - "mets": 12, - "name": "Fire fighter, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11246, - "mets": 8, - "name": "Fire fighter, hauling hoses on ground" - } - ], - "id": 12, - "name": "Fire Fighter" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11250, - "mets": 17, - "name": "Ax chopping, fast" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11260, - "mets": 5, - "name": "Ax chopping, slow" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11270, - "mets": 7, - "name": "Barking trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11280, - "mets": 11, - "name": "Carrying logs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11290, - "mets": 8, - "name": "Felling trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11300, - "mets": 8, - "name": "Forestry, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11310, - "mets": 5, - "name": "Hoeing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11320, - "mets": 6, - "name": "Planting by hand" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11330, - "mets": 7, - "name": "Sawing by hand" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11340, - "mets": 4.5, - "name": "Sawing with a chainsaw" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11350, - "mets": 9, - "name": "Trimming trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11360, - "mets": 4, - "name": "Weeding" - } - ], - "id": 13, - "name": "Forestry" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11390, - "mets": 8, - "name": "Horseback riding - galloping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11400, - "mets": 6.5, - "name": "Horseback riding - trotting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11410, - "mets": 2.5999999046325684, - "name": "Horseback riding - walking" - } - ], - "id": 14, - "name": "Horse Trainer" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11430, - "mets": 2.5, - "name": "Machine tooling, machining, working sheet metal" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11440, - "mets": 3, - "name": "Operating a lathe" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11450, - "mets": 5, - "name": "Operating a punch press" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11460, - "mets": 4, - "name": "Tapping and drilling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11470, - "mets": 3, - "name": "Welding" - } - ], - "id": 15, - "name": "Machine Shop" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11525, - "mets": 2.5, - "name": "Directing traffic (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11526, - "mets": 2, - "name": "Driving a squad car (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11528, - "mets": 4, - "name": "Making an arrest (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11527, - "mets": 1.2999999523162842, - "name": "Riding in a squad car (sitting)" - } - ], - "id": 16, - "name": "Police" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11640, - "mets": 5, - "name": "Fettling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11650, - "mets": 5.5, - "name": "Forging" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11660, - "mets": 8, - "name": "Hand rolling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11670, - "mets": 8, - "name": "Merchant mill rolling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11680, - "mets": 11, - "name": "Removing slag" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11690, - "mets": 7.5, - "name": "Tending furnace" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11700, - "mets": 5.5, - "name": "Tipping molds" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11710, - "mets": 8, - "name": "Working in general" - } - ], - "id": 17, - "name": "Steel Mill" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11740, - "mets": 2, - "name": "Hand sewing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11750, - "mets": 2.5, - "name": "Machine sewing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11760, - "mets": 4, - "name": "Pressing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11720, - "mets": 2.5, - "name": "Tailoring, cutting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11730, - "mets": 2.5, - "name": "Tailoring, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11765, - "mets": 3.5, - "name": "Weaving" - } - ], - "id": 18, - "name": "Tailoring" - } - ] - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 53003, - "mets": 5.300000190734863, - "name": "Pilates, Advanced" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 53001, - "mets": 3.0999999046325684, - "name": "Pilates, Beginner" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 53002, - "mets": 4.300000190734863, - "name": "Pilates, Intermediate" - } - ], - "id": 32, - "name": "Pilates" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13030, - "mets": 1.5, - "name": "Eating (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13040, - "mets": 2, - "name": "Grooming (washing, shaving, brushing teeth, washing hands, putting on make-up), sitting or standing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13045, - "mets": 2.5, - "name": "Hairstyling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13046, - "mets": 1, - "name": "Having hair or nails done by someone else, sitting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13000, - "mets": 2, - "name": "Standing - getting ready for bed, in general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13036, - "mets": 1, - "name": "Taking medication, sitting or standing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13035, - "mets": 2, - "name": "Talking and eating or eating only (standing)" - } - ], - "id": 19, - "name": "Self care" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15010, - "mets": 3.5, - "name": "Archery (non-hunting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15020, - "mets": 7, - "name": "Badminton, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15030, - "mets": 4.5, - "name": "Badminton, social singles and doubles, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15040, - "mets": 8, - "name": "Basketball, game" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15050, - "mets": 6, - "name": "Basketball, non-game, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15060, - "mets": 7, - "name": "Basketball, officiating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15070, - "mets": 4.5, - "name": "Basketball, shooting baskets" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15075, - "mets": 6.5, - "name": "Basketball, wheelchair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15080, - "mets": 2.5, - "name": "Billiards" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 55003, - "mets": 8.899999618530273, - "name": "Bootcamp" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15090, - "mets": 3, - "name": "Bowling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15100, - "mets": 12, - "name": "Boxing, in ring, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15110, - "mets": 6, - "name": "Boxing, punching bag" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15120, - "mets": 9, - "name": "Boxing, sparring" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15130, - "mets": 7, - "name": "Broombail" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15135, - "mets": 5, - "name": "Children's games (hopscotch, 4-square, dodge bail, playground apparatus, t-ball, tetherbai;, marbles, jacks, acrace games)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15140, - "mets": 4, - "name": "Coaching: football, soccer, basketball, baseball, swimming, etc." - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15150, - "mets": 5, - "name": "Cricket (batting, bowling)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15160, - "mets": 2.5, - "name": "Croquet" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15170, - "mets": 4, - "name": "Curling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15180, - "mets": 2.5, - "name": "Darts, wail or lawn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15190, - "mets": 6, - "name": "Drag racing, pushing or driving a car" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15200, - "mets": 6, - "name": "Fencing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15235, - "mets": 2.5, - "name": "Football or baseball, playing catch" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15210, - "mets": 9, - "name": "Football, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15230, - "mets": 8, - "name": "Football, touch, flag, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15240, - "mets": 3, - "name": "Frisbee playing, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15250, - "mets": 8, - "name": "Frisbee, ultimate" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15255, - "mets": 4.5, - "name": "Golf" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15270, - "mets": 3, - "name": "Golf, miniature, driving range" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15290, - "mets": 3.5, - "name": "Golf, using power cart" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15265, - "mets": 4.5, - "name": "Golf, walking and carrying clubs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15285, - "mets": 4.300000190734863, - "name": "Golf, walking and pulling clubs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15300, - "mets": 4, - "name": "Gymnastics, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15310, - "mets": 4, - "name": "Hacky sack" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15340, - "mets": 3.5, - "name": "Hand gliding" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15320, - "mets": 12, - "name": "Handball, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15330, - "mets": 8, - "name": "Handball, team" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15350, - "mets": 8, - "name": "Hockey, field" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15360, - "mets": 8, - "name": "Hockey, ice" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11390, - "mets": 8, - "name": "Horseback riding - galloping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11400, - "mets": 6.5, - "name": "Horseback riding - trotting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11410, - "mets": 2.5999999046325684, - "name": "Horseback riding - walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15370, - "mets": 4, - "name": "Horseback riding, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15380, - "mets": 3.5, - "name": "Horseback riding, saddling horse, grooming horse" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15410, - "mets": 3, - "name": "Horseshoe pitching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15420, - "mets": 12, - "name": "Jai alai" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15440, - "mets": 4, - "name": "Juggling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15450, - "mets": 7, - "name": "Kickball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 55002, - "mets": 9.399999618530273, - "name": "Kickboxing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15460, - "mets": 8, - "name": "Lacrosse" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15430, - "mets": 10, - "name": "Martial Arts" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15470, - "mets": 4, - "name": "Motor-cross" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15480, - "mets": 9, - "name": "Orienteering" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15500, - "mets": 6, - "name": "Paddleball, casual, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15490, - "mets": 10, - "name": "Paddleball, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15510, - "mets": 8, - "name": "Polo" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15530, - "mets": 7, - "name": "Racquetball, casual, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15520, - "mets": 10, - "name": "Racquetball, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15535, - "mets": 11, - "name": "Rock climbing, ascending rock" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15540, - "mets": 8, - "name": "Rock climbing, rappelling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15591, - "mets": 12.5, - "name": "Roller blading (in-line skating)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15550, - "mets": 12, - "name": "Rope jumping, fast" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15551, - "mets": 10, - "name": "Rope jumping, moderate, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15552, - "mets": 8, - "name": "Rope jumping, slow" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15560, - "mets": 10, - "name": "Rugby" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15570, - "mets": 3, - "name": "Shuffleboard, lawn bowling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15580, - "mets": 5, - "name": "Skateboarding" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15590, - "mets": 7, - "name": "Skating, roller" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15600, - "mets": 3.5, - "name": "Sky diving" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15610, - "mets": 7, - "name": "Soccer, casual, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15605, - "mets": 10, - "name": "Soccer, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15620, - "mets": 5, - "name": "Softball or baseball, fast or slow pitch, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15630, - "mets": 4, - "name": "Softball, officiating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15640, - "mets": 6, - "name": "Softball, pitching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 55001, - "mets": 6.5, - "name": "Spinning" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15650, - "mets": 12, - "name": "Squash" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15660, - "mets": 4, - "name": "Table tennis, ping pong" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15670, - "mets": 4, - "name": "Tai chi" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15675, - "mets": 7, - "name": "Tennis" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15680, - "mets": 6, - "name": "Tennis, doubles" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15690, - "mets": 8, - "name": "Tennis, singles" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15733, - "mets": 6, - "name": "Track and field (high jump, long jump, triple jump, javelin, pole vault)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15732, - "mets": 4, - "name": "Track and field (shot, discus, hammer throw)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15734, - "mets": 10, - "name": "Track and field (steeplechase, hurdles)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15700, - "mets": 3.5, - "name": "Trampoline" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15710, - "mets": 4, - "name": "Volleyball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15725, - "mets": 8, - "name": "Volleyball, beach" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15711, - "mets": 8, - "name": "Volleyball, competitive, in gymnasium" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15720, - "mets": 3, - "name": "Volleyball, non-competitive, 6 - 9 member team, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15731, - "mets": 7, - "name": "Wallyball, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15730, - "mets": 6, - "name": "Wrestling (one match = 5 minutes)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 56001, - "mets": 5.5, - "name": "Zumba" - } - ], - "id": 20, - "name": "Sports and Workouts", - "subCategories": [ - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 1010, - "maxSpeedMPH": 9.9, - "mets": 4, - "minSpeedMPH": -1, - "name": "under 10 mph" - }, - { - "id": 1020, - "maxSpeedMPH": 11.9, - "mets": 6, - "minSpeedMPH": 10, - "name": "10-11.9 mph" - }, - { - "id": 1030, - "maxSpeedMPH": 13.9, - "mets": 8, - "minSpeedMPH": 12, - "name": "12 to 13.9mph" - }, - { - "id": 1009, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "Mountain Bike" - }, - { - "id": 1040, - "maxSpeedMPH": 15.9, - "mets": 10, - "minSpeedMPH": 14, - "name": "14 to 15.9 mph" - }, - { - "id": 1050, - "maxSpeedMPH": 19, - "mets": 12, - "minSpeedMPH": 16, - "name": "16 to 19mph" - }, - { - "id": 1060, - "maxSpeedMPH": -1, - "mets": 16, - "minSpeedMPH": 20, - "name": "over 20mph" - } - ], - "hasSpeed": true, - "id": 90001, - "name": "Bike" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 1070, - "mets": 5, - "name": "Unicycling" - } - ], - "id": 21, - "name": "Bicycling" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2020, - "mets": 8, - "name": "Calisthenics (e.g. pushups, situps, pullups, jumping jacks), heavy, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2030, - "mets": 3.5, - "name": "Calisthenics, home exercise, light or moderate effort, general (example: back exercises), going up and down from floor" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2040, - "mets": 8, - "name": "Circuit training" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2060, - "mets": 5.5, - "name": "Health club exercise, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2101, - "mets": 2.5, - "name": "Mild stretching" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 2071, - "maxSpeedMPH": -1, - "mets": 3.5, - "minSpeedMPH": -1, - "name": "light effort (50 watts)" - }, - { - "id": 2072, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "moderate effort (100 watts)" - }, - { - "id": 2073, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "vigorous effort (150 watts)" - }, - { - "id": 2074, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": -1, - "name": "very vigorous effort (200 watts)" - } - ], - "hasSpeed": false, - "id": 90003, - "name": "Rowing Machine" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2080, - "mets": 7, - "name": "Ski machine, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2090, - "mets": 6, - "name": "Slimnastics, jazzercise" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2065, - "mets": 9, - "name": "Stair Climber" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 2011, - "maxSpeedMPH": -1, - "mets": 3, - "minSpeedMPH": -1, - "name": "very light effort (50 watts)" - }, - { - "id": 2012, - "maxSpeedMPH": -1, - "mets": 5.5, - "minSpeedMPH": -1, - "name": "light effort (100 watts)" - }, - { - "id": 2013, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "moderate effort (150 watts)" - }, - { - "id": 2014, - "maxSpeedMPH": -1, - "mets": 10.5, - "minSpeedMPH": -1, - "name": "vigorous effort (200 watts)" - }, - { - "id": 2015, - "maxSpeedMPH": -1, - "mets": 12.5, - "minSpeedMPH": -1, - "name": "very vigorous effort (250 watts)" - } - ], - "hasSpeed": false, - "id": 90002, - "name": "Stationary Bike" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2100, - "mets": 2.5, - "name": "Stretching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2110, - "mets": 6, - "name": "Teaching aerobic exercise class" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2120, - "mets": 4, - "name": "Water aerobics, water calisthenics" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2050, - "mets": 6, - "name": "Weight lifting (free weight, nautilus or universal-type), power lifting or body building, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2130, - "mets": 3, - "name": "Weight lifting (free, nautilus or universal-type), light or moderate effort, light workout, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2135, - "mets": 1, - "name": "Whirlpool, sitting" - } - ], - "id": 22, - "name": "Conditioning exercises" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 50201, - "maxSpeedMPH": -1, - "mets": 10.399999618530273, - "minSpeedMPH": -1, - "name": "Low Speed" - }, - { - "id": 50202, - "maxSpeedMPH": -1, - "mets": 13.199999809265137, - "minSpeedMPH": -1, - "name": "Medium Speed" - }, - { - "id": 50203, - "maxSpeedMPH": -1, - "mets": 16.399999618530273, - "minSpeedMPH": -1, - "name": "High Speed" - } - ], - "hasSpeed": false, - "id": 90018, - "name": "Elliptical, High Resistance" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 50001, - "maxSpeedMPH": -1, - "mets": 2, - "minSpeedMPH": -1, - "name": "Low Speed" - }, - { - "id": 50002, - "maxSpeedMPH": -1, - "mets": 4.699999809265137, - "minSpeedMPH": -1, - "name": "Medium Speed" - }, - { - "id": 50003, - "maxSpeedMPH": -1, - "mets": 8, - "minSpeedMPH": -1, - "name": "High Speed" - } - ], - "hasSpeed": false, - "id": 90016, - "name": "Elliptical, Low Resistance" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 50101, - "maxSpeedMPH": -1, - "mets": 6, - "minSpeedMPH": -1, - "name": "Low Speed" - }, - { - "id": 50102, - "maxSpeedMPH": -1, - "mets": 8.699999809265137, - "minSpeedMPH": -1, - "name": "Medium Speed" - }, - { - "id": 50103, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": -1, - "name": "High Speed" - } - ], - "hasSpeed": false, - "id": 90017, - "name": "Elliptical, Medium Resistance" - } - ], - "id": 29, - "name": "Elliptical" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12010, - "mets": 6, - "name": "Jog/walk combination (jogging component of less than 10 minutes)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12027, - "mets": 4.5, - "name": "Jogging on a mini-tramp" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12020, - "mets": 7, - "name": "Jogging, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12025, - "mets": 8, - "name": "Jogging, in place" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 12030, - "maxSpeedMPH": 5, - "mets": 8, - "minSpeedMPH": -1, - "name": "5 mph (12 min/mile)" - }, - { - "id": 12040, - "maxSpeedMPH": 5.2, - "mets": 9, - "minSpeedMPH": -1, - "name": "5.2 mph (11.5 min/mile)" - }, - { - "id": 12050, - "maxSpeedMPH": 6, - "mets": 10, - "minSpeedMPH": -1, - "name": "6 mph (10 min/mile)" - }, - { - "id": 12060, - "maxSpeedMPH": 6.7, - "mets": 11, - "minSpeedMPH": -1, - "name": "6.7 mph (9 min/mile)" - }, - { - "id": 12070, - "maxSpeedMPH": 7, - "mets": 11.5, - "minSpeedMPH": -1, - "name": "7 mph (8.5 min/mile)" - }, - { - "id": 12080, - "maxSpeedMPH": 7.5, - "mets": 12.5, - "minSpeedMPH": -1, - "name": "7.5 mph (8 min/mile)" - }, - { - "id": 12090, - "maxSpeedMPH": 8, - "mets": 13.5, - "minSpeedMPH": -1, - "name": "8 mph (7.5 min/mile)" - }, - { - "id": 12100, - "maxSpeedMPH": 8.6, - "mets": 14, - "minSpeedMPH": -1, - "name": "8.6 mph (7 min/mile)" - }, - { - "id": 12110, - "maxSpeedMPH": 9, - "mets": 15, - "minSpeedMPH": -1, - "name": "9 mph (6.5 min/mile)" - }, - { - "id": 12120, - "maxSpeedMPH": 10, - "mets": 16, - "minSpeedMPH": -1, - "name": "10 mph (6 min/mile)" - }, - { - "id": 12130, - "maxSpeedMPH": 10.9, - "mets": 18, - "minSpeedMPH": -1, - "name": "10.9 mph (5.5 min/mile)" - } - ], - "hasSpeed": true, - "id": 90009, - "name": "Run" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12190, - "mets": 8, - "name": "Running while pushing a stroller or wheelchair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12140, - "mets": 9, - "name": "Running, cross country" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12180, - "mets": 10, - "name": "Running, on a track, team practice" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12170, - "mets": 15, - "name": "Running, stairs, up" - } - ], - "id": 23, - "name": "Running" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51002, - "maxSpeedMPH": 2.4, - "mets": 3, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51003, - "maxSpeedMPH": 3.4, - "mets": 3.9000000953674316, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51004, - "maxSpeedMPH": 4.4, - "mets": 4.800000190734863, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51005, - "maxSpeedMPH": 5.4, - "mets": 10.399999618530273, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51006, - "maxSpeedMPH": 6.4, - "mets": 12.199999809265137, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51007, - "maxSpeedMPH": 7.4, - "mets": 14, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51008, - "maxSpeedMPH": 8.4, - "mets": 16, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51009, - "maxSpeedMPH": 9.4, - "mets": 17.799999237060547, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51010, - "maxSpeedMPH": 10.4, - "mets": 19.600000381469727, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90019, - "name": "Treadmill, 0% Incline" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51202, - "maxSpeedMPH": 2.4, - "mets": 6.300000190734863, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51203, - "maxSpeedMPH": 3.4, - "mets": 8.800000190734863, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51204, - "maxSpeedMPH": 4.4, - "mets": 11.5, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51205, - "maxSpeedMPH": 5.4, - "mets": 14.5, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51206, - "maxSpeedMPH": 6.4, - "mets": 17.200000762939453, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51207, - "maxSpeedMPH": 7.4, - "mets": 19.899999618530273, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51208, - "maxSpeedMPH": 8.4, - "mets": 22.5, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51209, - "maxSpeedMPH": 9.4, - "mets": 25.200000762939453, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51210, - "maxSpeedMPH": 10.4, - "mets": 28, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90021, - "name": "Treadmill, 10% Incline" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51302, - "maxSpeedMPH": 2.4, - "mets": 8, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51303, - "maxSpeedMPH": 3.4, - "mets": 11.399999618530273, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51304, - "maxSpeedMPH": 4.4, - "mets": 14.800000190734863, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51305, - "maxSpeedMPH": 5.4, - "mets": 16.600000381469727, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51306, - "maxSpeedMPH": 6.4, - "mets": 19.700000762939453, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51307, - "maxSpeedMPH": 7.4, - "mets": 22.799999237060547, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51308, - "maxSpeedMPH": 8.4, - "mets": 25.899999618530273, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51309, - "maxSpeedMPH": 9.4, - "mets": 28.899999618530273, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51310, - "maxSpeedMPH": 10.4, - "mets": 32, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90022, - "name": "Treadmill, 15% Incline" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51102, - "maxSpeedMPH": 2.4, - "mets": 4.599999904632568, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51103, - "maxSpeedMPH": 3.4, - "mets": 6.400000095367432, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51104, - "maxSpeedMPH": 4.4, - "mets": 8.100000381469727, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51105, - "maxSpeedMPH": 5.4, - "mets": 12.5, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51106, - "maxSpeedMPH": 6.4, - "mets": 14.800000190734863, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51107, - "maxSpeedMPH": 7.4, - "mets": 16.899999618530273, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51108, - "maxSpeedMPH": 8.4, - "mets": 19.200000762939453, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51109, - "maxSpeedMPH": 9.4, - "mets": 21.5, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51110, - "maxSpeedMPH": 10.4, - "mets": 23.700000762939453, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90020, - "name": "Treadmill, 5% Incline" - } - ], - "id": 30, - "name": "Treadmill" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18010, - "mets": 2.5, - "name": "Boating, power" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 18040, - "maxSpeedMPH": 3.9, - "mets": 3, - "minSpeedMPH": -1, - "name": "2.0 - 3.9 mph, light effort" - }, - { - "id": 18050, - "maxSpeedMPH": 5.9, - "mets": 7, - "minSpeedMPH": 4, - "name": "4.0 - 5.9 mph, moderate effort" - }, - { - "id": 18060, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": 6, - "name": "Faster than 6 mph, vigorous effort" - } - ], - "hasSpeed": true, - "id": 90014, - "name": "Canoeing or Rowing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18025, - "mets": 3.299999952316284, - "name": "Canoeing, harvesting wild rice, knocking rice off the stalks" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18020, - "mets": 4, - "name": "Canoeing, on camping trip" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18030, - "mets": 7, - "name": "Canoeing, portaging" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18080, - "mets": 12, - "name": "Canoeing, rowing, in competition, or crew or sculling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18090, - "mets": 3, - "name": "Diving, springboard or platform" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18100, - "mets": 5, - "name": "Kayaking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18110, - "mets": 4, - "name": "Paddle boat" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18140, - "mets": 3, - "name": "Sailing, Sunfish/Laser/Hobby Cat, Keel boats, ocean sailing, yachting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18120, - "mets": 3, - "name": "Sailing, boat and board sailing, windsurfing, ice sailing, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18130, - "mets": 5, - "name": "Sailing, in competition" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18150, - "mets": 6, - "name": "Skiing, water" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18160, - "mets": 7, - "name": "Skimobiling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18180, - "mets": 16, - "name": "Skindiving, fast" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18190, - "mets": 12.5, - "name": "Skindiving, moderate" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18200, - "mets": 7, - "name": "Skindiving, scuba diving, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18210, - "mets": 5, - "name": "Snorkeling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18220, - "mets": 3, - "name": "Surfing, body or board" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 58001, - "maxSpeedMPH": 0.9, - "mets": 4, - "minSpeedMPH": -1, - "name": "less than 25 yards/min" - }, - { - "id": 18240, - "maxSpeedMPH": 1.7, - "mets": 8, - "minSpeedMPH": 1, - "name": "25-50 yards/min" - }, - { - "id": 58002, - "maxSpeedMPH": 2.6, - "mets": 11, - "minSpeedMPH": 1.8, - "name": "50-75 yards/min" - }, - { - "id": 58003, - "maxSpeedMPH": 3.4, - "mets": 14, - "minSpeedMPH": 2.7, - "name": "75-100 yards/min" - } - ], - "hasSpeed": true, - "id": 90024, - "name": "Swimming" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18230, - "mets": 10, - "name": "Swimming laps, freestyle, fast, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18250, - "mets": 7, - "name": "Swimming, backstroke, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18260, - "mets": 10, - "name": "Swimming, breaststroke, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18270, - "mets": 11, - "name": "Swimming, butterfly, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18280, - "mets": 11, - "name": "Swimming, crawl, fast (75 yards/minute), vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18290, - "mets": 8, - "name": "Swimming, crawl, slow (50 yards/minute), moderate or light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18300, - "mets": 6, - "name": "Swimming, lake, ocean, river" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18310, - "mets": 6, - "name": "Swimming, leisurely, not lap swimming, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18320, - "mets": 8, - "name": "Swimming, sidestroke, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18330, - "mets": 8, - "name": "Swimming, synchronized" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18340, - "mets": 10, - "name": "Swimming, treading water, fast vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18350, - "mets": 4, - "name": "Swimming, treading water, moderate effort, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18355, - "mets": 4, - "name": "Water aerobics, water calisthenics" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18366, - "mets": 8, - "name": "Water jogging" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18360, - "mets": 10, - "name": "Water polo" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18365, - "mets": 3, - "name": "Water volleyball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18370, - "mets": 5, - "name": "Whitewater rafting, kayaking, or canoeing" - } - ], - "id": 25, - "name": "Water Activities" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 19080, - "maxSpeedMPH": 2.5, - "mets": 7, - "minSpeedMPH": -1, - "name": "2.5 mph, slow or light effort, ski walking" - }, - { - "id": 19090, - "maxSpeedMPH": 4.9, - "mets": 8, - "minSpeedMPH": 4, - "name": "4.0-4.9 mph, moderate speed and effort, general" - }, - { - "id": 19100, - "maxSpeedMPH": 7.9, - "mets": 9, - "minSpeedMPH": 5, - "name": "5.0-7.9 mph, brisk speed, vigorous effort" - }, - { - "id": 19110, - "maxSpeedMPH": -1, - "mets": 14, - "minSpeedMPH": 8, - "name": "faster than 8.0 mph, racing" - } - ], - "hasSpeed": true, - "id": 90015, - "name": "Cross Country Skiing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19010, - "mets": 6, - "name": "Moving ice house (set up/drill holes, etc.)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19020, - "mets": 5.5, - "name": "Skating, ice, 9 mph or less" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19030, - "mets": 7, - "name": "Skating, ice, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19040, - "mets": 9, - "name": "Skating, ice, rapidly, more than 9 mph" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19050, - "mets": 15, - "name": "Skating, speed, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19060, - "mets": 7, - "name": "Ski jumping (climb up carrying skis)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19130, - "mets": 16.5, - "name": "Skiing, cross country, hard snow, uphill, maximum, snow mountaineering" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19150, - "mets": 5, - "name": "Skiing, downhill, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19160, - "mets": 6, - "name": "Skiing, downhill, moderate effort, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19170, - "mets": 8, - "name": "Skiing, downhill, vigorous effort, racing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19180, - "mets": 7, - "name": "Sledding, tobogganing, bobsledding, luge" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19190, - "mets": 8, - "name": "Snow shoeing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19200, - "mets": 3.5, - "name": "Snowmobiling" - } - ], - "id": 24, - "name": "Winter activities" - } - ] - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16010, - "mets": 2, - "name": "Automobile or light truck (not a semi) driving" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16050, - "mets": 3, - "name": "Driving heavy truck, tractor, bus" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16020, - "mets": 2, - "name": "Flying airplane" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16030, - "mets": 2.5, - "name": "Motor scooter, motorcycle" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16040, - "mets": 6, - "name": "Pushing plane in and out of hangar" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16016, - "mets": 1, - "name": "Riding in a bus" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16015, - "mets": 1, - "name": "Riding in a car or truck" - } - ], - "id": 26, - "name": "Transportation" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17010, - "mets": 7, - "name": "Backpacking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17085, - "mets": 2.5, - "name": "Bird watching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17020, - "mets": 3.5, - "name": "Carrying infant or 15 pound load (e.g. suitcase)" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 17026, - "maxSpeedMPH": -1, - "mets": 5, - "minSpeedMPH": -1, - "name": "1 to 15 lb load" - }, - { - "id": 17027, - "maxSpeedMPH": -1, - "mets": 6, - "minSpeedMPH": -1, - "name": "16 to 24 lb load" - }, - { - "id": 17028, - "maxSpeedMPH": -1, - "mets": 8, - "minSpeedMPH": -1, - "name": "25 to 49 lb load" - }, - { - "id": 17029, - "maxSpeedMPH": -1, - "mets": 10, - "minSpeedMPH": -1, - "name": "50 to 74 lb load" - }, - { - "id": 17030, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": -1, - "name": "74+ lb load" - } - ], - "hasSpeed": false, - "id": 90011, - "name": "Carrying load upstairs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17130, - "mets": 8, - "name": "Going up stairs, using or climbing up ladder" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 17035, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "With 0 to 9 pound pack" - }, - { - "id": 17040, - "maxSpeedMPH": -1, - "mets": 7.5, - "minSpeedMPH": -1, - "name": "With 10 to 20 pound pack" - }, - { - "id": 17050, - "maxSpeedMPH": -1, - "mets": 8, - "minSpeedMPH": -1, - "name": "With 21 to 42 pound pack" - }, - { - "id": 17060, - "maxSpeedMPH": -1, - "mets": 9, - "minSpeedMPH": -1, - "name": "With 42+ pound pack" - } - ], - "hasSpeed": true, - "id": 90012, - "name": "Hike" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17080, - "mets": 6, - "name": "Hiking, cross country" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17090, - "mets": 6.5, - "name": "Marching, rapidly, military" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17105, - "mets": 4, - "name": "Pushing a wheelchair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17100, - "mets": 2.5, - "name": "Pushing or pulling stroller with child or walking with children" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17110, - "mets": 6.5, - "name": "Race walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17120, - "mets": 8, - "name": "Rock or mountain climbing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17140, - "mets": 5, - "name": "Using crutches" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 17151, - "maxSpeedMPH": 1.5, - "mets": 2, - "minSpeedMPH": -1, - "name": "less than 2 mph, strolling very slowly" - }, - { - "id": 17152, - "maxSpeedMPH": 2, - "mets": 2.5, - "minSpeedMPH": -1, - "name": "2.0 mph, slow pace" - }, - { - "id": 17170, - "maxSpeedMPH": 2.5, - "mets": 3, - "minSpeedMPH": -1, - "name": "2.5 mph" - }, - { - "id": 17190, - "maxSpeedMPH": 3, - "mets": 3.299999952316284, - "minSpeedMPH": -1, - "name": "3.0 mph, moderate pace" - }, - { - "id": 17200, - "maxSpeedMPH": 3.5, - "mets": 3.799999952316284, - "minSpeedMPH": -1, - "name": "3.5 mph, walking for exercise" - }, - { - "id": 17220, - "maxSpeedMPH": 4, - "mets": 5, - "minSpeedMPH": -1, - "name": "4.0 mph, very brisk pace" - }, - { - "id": 17230, - "maxSpeedMPH": 4.5, - "mets": 6.300000190734863, - "minSpeedMPH": -1, - "name": "4.5 mph, very, very brisk" - }, - { - "id": 17231, - "maxSpeedMPH": 5, - "mets": 8, - "minSpeedMPH": -1, - "name": "5.0 mph, speed walking" - }, - { - "id": 17165, - "maxSpeedMPH": -1, - "mets": 3, - "minSpeedMPH": -1, - "name": "Walking the dog" - } - ], - "hasSpeed": true, - "id": 90013, - "name": "Walk" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17160, - "mets": 3.5, - "name": "Walking for pleasure" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17260, - "mets": 5, - "name": "Walking, grass track" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17150, - "mets": 2, - "name": "Walking, household" - } - ], - "id": 27, - "name": "Walking" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54011, - "mets": 3.200000047683716, - "name": "Wii Baseball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54003, - "mets": 2.799999952316284, - "name": "Wii Bowling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54004, - "mets": 4.800000190734863, - "name": "Wii Boxing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54001, - "mets": 2.5, - "name": "Wii Fit Advanced Step" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54006, - "mets": 3.700000047683716, - "name": "Wii Fit Free Run" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54007, - "mets": 2.299999952316284, - "name": "Wii Fit Free step" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54009, - "mets": 3.700000047683716, - "name": "Wii Fit Island Run" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54005, - "mets": 2.799999952316284, - "name": "Wii Fit Rhythm Boxing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54002, - "mets": 2.5, - "name": "Wii Fit Super Hula Hoop" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54010, - "mets": 2.299999952316284, - "name": "Wii Golf" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54008, - "mets": 3.700000047683716, - "name": "Wii Tennis" - } - ], - "id": 33, - "name": "Wii Games" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57001, - "mets": 3.200000047683716, - "name": "XBOX Sports - Baseball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57002, - "mets": 2.299999952316284, - "name": "XBOX Sports - Darts" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57004, - "mets": 3.700000047683716, - "name": "XBOX Sports - Football" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57003, - "mets": 2.299999952316284, - "name": "XBOX Sports - Golf" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57007, - "mets": 2.799999952316284, - "name": "XBOX Sports - Just Dance 3" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57005, - "mets": 3.700000047683716, - "name": "XBOX Sports - Skiing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57006, - "mets": 3.700000047683716, - "name": "XBOX Sports - Tennis" - } - ], - "id": 35, - "name": "XBOX Games" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52001, - "mets": 4.300000190734863, - "name": "Yoga, Ashtanga" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52002, - "mets": 5.900000095367432, - "name": "Yoga, Bikram" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52003, - "mets": 2.299999952316284, - "name": "Yoga, Hatha" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52004, - "mets": 4.300000190734863, - "name": "Yoga, Power" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52005, - "mets": 7.300000190734863, - "name": "Yoga, Vinyasa" - } - ], - "id": 31, - "name": "Yoga" - } - ] - } - } - }, - { - "result": { - "date": "2014-08-20", - "content": { - "categories": [ - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 3016, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "6 - 8 inch step" - }, - { - "id": 3017, - "maxSpeedMPH": -1, - "mets": 10, - "minSpeedMPH": -1, - "name": "10 - 12 inch step" - } - ], - "hasSpeed": false, - "id": 90004, - "name": "Aerobic step" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3015, - "mets": 6.5, - "name": "Aerobic, general" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 3020, - "maxSpeedMPH": -1, - "mets": 5, - "minSpeedMPH": -1, - "name": "low impact" - }, - { - "id": 3021, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "high impact" - } - ], - "hasSpeed": false, - "id": 90005, - "name": "Aerobics" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3050, - "mets": 5.5, - "name": "Anishinaabe Jingle Dancing or other traditional American Indian dancing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3010, - "mets": 4.800000190734863, - "name": "Ballet or modem, twist, jazz, tap, jitterbug" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3031, - "mets": 5, - "name": "Ballroom, fast (disco, folk, square), line dancing, Irish step dancing, polka, contra, country" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3040, - "mets": 3, - "name": "Ballroom, slow (e.g. waltz, foxtrot, slow dancing), samba, tango, 19th C, mambo, chacha" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 3025, - "mets": 4.5, - "name": "General, Greek, Middle Eastern, hula, flamenco, belly, swing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 56001, - "mets": 5.5, - "name": "Zumba" - } - ], - "id": 0, - "name": "Dancing" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4010, - "mets": 4, - "name": "Digging worms, with shovel" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4030, - "mets": 2.5, - "name": "Fishing from boat, sitting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4020, - "mets": 4, - "name": "Fishing from river bank and walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4040, - "mets": 3.5, - "name": "Fishing from river bank, standing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4050, - "mets": 6, - "name": "Fishing in stream, in waders" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4001, - "mets": 3, - "name": "Fishing, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4060, - "mets": 2, - "name": "Fishing, ice, sitting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4070, - "mets": 2.5, - "name": "Hunting, bow and arrow or crossbow" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4080, - "mets": 6, - "name": "Hunting, deer, elk, large game" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4090, - "mets": 2.5, - "name": "Hunting, duck, wading" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4100, - "mets": 5, - "name": "Hunting, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4110, - "mets": 6, - "name": "Hunting, pheasants or grouse" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4120, - "mets": 5, - "name": "Hunting, rabbit, squirrel, prairie chick, raccoon, small game" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 4130, - "mets": 2.5, - "name": "Pistol shooting or trap shooting, standing" - } - ], - "id": 1, - "name": "Fishing and Hunting" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5149, - "mets": 2.5, - "name": "Building a fire inside" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5010, - "mets": 3.299999952316284, - "name": "Carpet sweeping, sweeping floors" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5056, - "mets": 7.5, - "name": "Carrying groceries upstairs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5181, - "mets": 3, - "name": "Carrying small children" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5161, - "mets": 2, - "name": "Change light bulb" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5185, - "mets": 2.5, - "name": "Child care: sitting/kneeling - dressing, bathing, grooming, feeding, occasional lifting of child-light effort, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5186, - "mets": 3, - "name": "Child care: standing - dressing, bathing, grooming, feeding, occasional lifting of child-light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5020, - "mets": 3, - "name": "Cleaning, heavy or major (e.g. wash car, wash windows, clean garage), vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5030, - "mets": 3, - "name": "Cleaning, house or cabin, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5040, - "mets": 2.5, - "name": "Cleaning, light (dusting, straightening up, changing linen, carrying out trash)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5166, - "mets": 3, - "name": "Closing windows and locking doors" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5057, - "mets": 3, - "name": "Cooking Indian bread on an outside stove" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5052, - "mets": 2.5, - "name": "Cooking or food preparation" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5187, - "mets": 4, - "name": "Elder care, disabled adult, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5053, - "mets": 2.5, - "name": "Feeding animals" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5060, - "mets": 2.299999952316284, - "name": "Food shopping with or without a grocery cart, standing or walking" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 5025, - "maxSpeedMPH": -1, - "mets": 2.5, - "minSpeedMPH": -1, - "name": "light effort" - }, - { - "id": 5026, - "maxSpeedMPH": -1, - "mets": 3.5, - "minSpeedMPH": -1, - "name": "moderate effort" - }, - { - "id": 5027, - "maxSpeedMPH": -1, - "mets": 4, - "minSpeedMPH": -1, - "name": "vigorous effort" - } - ], - "hasSpeed": false, - "id": 90006, - "name": "Household Chores" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5070, - "mets": 2.299999952316284, - "name": "Ironing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5080, - "mets": 1.5, - "name": "Knitting, sewing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5090, - "mets": 2, - "name": "Laundry, fold or hang clothes, put clothes in washer or dryer, packing suitcase" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17031, - "mets": 3, - "name": "Loading /unloading a car" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5100, - "mets": 2, - "name": "Making a bed" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5110, - "mets": 5, - "name": "Maple syruping/sugar bushing (including carrying buckets, carrying wood)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5021, - "mets": 3.5, - "name": "Mopping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5120, - "mets": 6, - "name": "Moving furniture, household items, carrying boxes" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5150, - "mets": 9, - "name": "Moving household items upstairs, carrying boxes or furniture" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5160, - "mets": 2, - "name": "Pumping gas, change light bulb, etc.)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5095, - "mets": 2.299999952316284, - "name": "Putting away clothes, gathering clothes to pack, putting away laundry" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5055, - "mets": 2.5, - "name": "Putting away groceries (e.g. carrying groceries, shopping without a grocery cart), carrying packages" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5147, - "mets": 3, - "name": "Putting away household items - moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5165, - "mets": 3, - "name": "Readying to leave" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5130, - "mets": 3.799999952316284, - "name": "Scrubbing floors, on hands and knees, scrubbing bathroom, bathtub" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5051, - "mets": 2.5, - "name": "Serving food, setting table" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5065, - "mets": 2.299999952316284, - "name": "Shopping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5190, - "mets": 2.5, - "name": "Sit, playing with animals, light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5170, - "mets": 2.5, - "name": "Sitting - playing with child(ren) - light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5191, - "mets": 2.799999952316284, - "name": "Stand, playing with animals, light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5195, - "mets": 3.5, - "name": "Standing - bathing dog" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5146, - "mets": 3.5, - "name": "Standing - packing/unpacking boxes, occasional lifting of household items light - moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5171, - "mets": 2.799999952316284, - "name": "Standing - playing with child(ren) - light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5140, - "mets": 4, - "name": "Sweeping garage, sidewalk or outside of house" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5043, - "mets": 3.5, - "name": "Vacuuming" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5175, - "mets": 4, - "name": "Walk/run - playing with child(ren) - moderate, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5180, - "mets": 5, - "name": "Walk/run - playing with child(ren) - vigorous, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5192, - "mets": 2.799999952316284, - "name": "Walk/run, playing with animals, light, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5193, - "mets": 4, - "name": "Walk/run, playing with animals, moderate, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5194, - "mets": 5, - "name": "Walk/run, playing with animals, vigorous, only active periods" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5041, - "mets": 2.299999952316284, - "name": "Washing dishes - standing or in general (not broken into stand/walk components)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5042, - "mets": 2.5, - "name": "Washing dishes; clearing dishes from table - walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5148, - "mets": 2.5, - "name": "Watering plants" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 5081, - "mets": 1.5, - "name": "Wrapping presents" - } - ], - "id": 2, - "name": "Home activities" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6010, - "mets": 3, - "name": "Airplane repair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6020, - "mets": 4, - "name": "Automobile body work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6030, - "mets": 3, - "name": "Automobile repair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6060, - "mets": 4.5, - "name": "Carpentry, finishing or refinishing cabinets or furniture" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6040, - "mets": 3, - "name": "Carpentry, general, workshop" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6050, - "mets": 6, - "name": "Carpentry, outside house, installing rain gutters, building a fence" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6070, - "mets": 7.5, - "name": "Carpentry, sawing hardwood" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6080, - "mets": 5, - "name": "Caulking, chinking log cabin" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6090, - "mets": 4.5, - "name": "Caulking, except log cabin" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6100, - "mets": 5, - "name": "Cleaning gutters" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6110, - "mets": 5, - "name": "Excavating garage" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6120, - "mets": 5, - "name": "Hanging storm windows" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6130, - "mets": 4.5, - "name": "Laying or removing carpet" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6140, - "mets": 4.5, - "name": "Laying tile or linoleum, repairing appliances" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6165, - "mets": 4.5, - "name": "Painting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6150, - "mets": 5, - "name": "Painting, outside home" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6160, - "mets": 3, - "name": "Painting, papering, plastering, scraping, inside house, hanging sheet rock, remodeling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6170, - "mets": 3, - "name": "Put on and removal of tarp - sailboat" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6180, - "mets": 6, - "name": "Roofing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6190, - "mets": 4.5, - "name": "Sanding floors with a power sander" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6200, - "mets": 4.5, - "name": "Scraping and painting sailboat or powerboat" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6210, - "mets": 5, - "name": "Spreading dirt with a shovel" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6220, - "mets": 4.5, - "name": "Washing and waxing hull of sailboat, car, powerboat, airplane" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6230, - "mets": 4.5, - "name": "Washing fence, painting fence" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 6240, - "mets": 3, - "name": "Wiring, plumbing" - } - ], - "id": 3, - "name": "Home repair" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8010, - "mets": 5, - "name": "Carrying, loading or stacking wood, loading/unloading or carrying lumber" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8020, - "mets": 6, - "name": "Chopping wood, splitting logs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8030, - "mets": 5, - "name": "Clearing land, hauling branches, wheelbarrow chores" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8040, - "mets": 5, - "name": "Digging sandbox" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8050, - "mets": 5, - "name": "Digging, spading, filling garden, composting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8060, - "mets": 6, - "name": "Gardening with heavy power tools, tilling a garden, chain saw" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8245, - "mets": 4, - "name": "Gardening, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8250, - "mets": 3, - "name": "Implied walking/standing -picking up yard, light, picking flowers or vegetables" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8080, - "mets": 5, - "name": "Laying crushed rock" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8090, - "mets": 5, - "name": "Laying sod" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8095, - "mets": 5.5, - "name": "Mowing lawn, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8100, - "mets": 2.5, - "name": "Mowing lawn, riding mower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8110, - "mets": 6, - "name": "Mowing lawn, walk, hand mower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8120, - "mets": 5.5, - "name": "Mowing lawn, walk, power mower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8130, - "mets": 4.5, - "name": "Operating snow blower, walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8246, - "mets": 3, - "name": "Picking fruit off trees, picking fruits/vegetables, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8140, - "mets": 4.5, - "name": "Planting seedlings, shrubs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8150, - "mets": 4.5, - "name": "Planting trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8160, - "mets": 4.300000190734863, - "name": "Raking lawn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8170, - "mets": 4, - "name": "Raking roof with snow rake" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8180, - "mets": 3, - "name": "Riding snow blower" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8190, - "mets": 4, - "name": "Sacking grass, leaves" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8200, - "mets": 6, - "name": "Shoveling snow, by hand" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8210, - "mets": 4.5, - "name": "Trimming shrubs or trees, manual cutter" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8215, - "mets": 3.5, - "name": "Trimming shrubs or trees, power cutter, using leaf blower, edger" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8220, - "mets": 2.5, - "name": "Walking, applying fertilizer or seeding a lawn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8251, - "mets": 3, - "name": "Walking, gathering gardening tools" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8230, - "mets": 1.5, - "name": "Watering lawn or garden, standing or walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 8240, - "mets": 4.5, - "name": "Weeding, cultivating garden" - } - ], - "id": 5, - "name": "Lawn and garden" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7010, - "mets": 1, - "name": "Lying quietly and watching television" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7011, - "mets": 1, - "name": "Lying quietly, doing nothing, lying in bed awake, listening to music (not talking or reading)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7075, - "mets": 1, - "name": "Meditating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7070, - "mets": 1, - "name": "Reclining - reading" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7060, - "mets": 1, - "name": "Reclining - talking or talking on phone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7050, - "mets": 1, - "name": "Reclining - writing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7020, - "mets": 1, - "name": "Sitting quietly and watching television" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7021, - "mets": 1, - "name": "Sitting quietly, sitting smoking, listening to music (not talking or reading), watching a movie in a theater" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7030, - "mets": 0.8999999761581421, - "name": "Sleeping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 7040, - "mets": 1.2000000476837158, - "name": "Standing quietly (standing in a line)" - } - ], - "id": 4, - "name": "Leisure and inactivity" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9110, - "mets": 2.5, - "name": "Camping involving standing, walking, sitting, light-to-moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9100, - "mets": 1.5, - "name": "Retreat/family reunion activities involving sitting, relaxing, talking, eating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9075, - "mets": 1.5, - "name": "Sitting - arts and crafts, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9065, - "mets": 1.7999999523162842, - "name": "Sitting - in class, general, including note-taking or class discussion" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9030, - "mets": 1.2999999523162842, - "name": "Sitting - reading, book, newspaper, etc." - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9060, - "mets": 1.7999999523162842, - "name": "Sitting - studying, general, including reading and/or writing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9055, - "mets": 1.5, - "name": "Sitting - talking or talking on the phone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9040, - "mets": 1.7999999523162842, - "name": "Sitting - writing, desk work, typing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9080, - "mets": 2, - "name": "Sitting -arts and crafts, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9010, - "mets": 1.5, - "name": "Sitting -card playing, playing board games" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9115, - "mets": 1.5, - "name": "Sitting at a sporting event, spectator" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9085, - "mets": 1.7999999523162842, - "name": "Standing - arts and crafts, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9090, - "mets": 3, - "name": "Standing - arts and crafts, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9095, - "mets": 3.5, - "name": "Standing - arts and crafts, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9071, - "mets": 2, - "name": "Standing - miscellaneous" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9070, - "mets": 1.7999999523162842, - "name": "Standing - reading" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9050, - "mets": 1.7999999523162842, - "name": "Standing - talking or talking on the phone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9020, - "mets": 2.299999952316284, - "name": "Standing -drawing (writing), casino gambling, duplicating machine" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 9105, - "mets": 2, - "name": "Touring/traveling/vacation involving walking and riding" - } - ], - "id": 6, - "name": "Miscellaneous" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10010, - "mets": 1.7999999523162842, - "name": "Accordion" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10020, - "mets": 2, - "name": "Cello" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10030, - "mets": 2.5, - "name": "Conducting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10040, - "mets": 4, - "name": "Drums" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10050, - "mets": 2, - "name": "Flute (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10120, - "mets": 2, - "name": "Guitar, classical, folk (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10125, - "mets": 3, - "name": "Guitar, rock and roll band (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10060, - "mets": 2, - "name": "Horn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10135, - "mets": 3.5, - "name": "Marching band, drum major (walking)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10130, - "mets": 4, - "name": "Marching band, playing an instrument, baton twirling (walking)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10070, - "mets": 2.5, - "name": "Piano or organ" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10080, - "mets": 3.5, - "name": "Trombone" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10090, - "mets": 2.5, - "name": "Trumpet" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10100, - "mets": 2.5, - "name": "Violin" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 10110, - "mets": 2, - "name": "Woodwind" - } - ], - "id": 7, - "name": "Music playing" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11010, - "mets": 4, - "name": "Bakery, general, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11015, - "mets": 2.5, - "name": "Bakery, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11020, - "mets": 2.299999952316284, - "name": "Bookbinding" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11030, - "mets": 6, - "name": "Building road (including hauling debris, driving heavy machinery)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11035, - "mets": 2, - "name": "Building road, directing traffic (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11040, - "mets": 3.5, - "name": "Carpentry, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11050, - "mets": 8, - "name": "Carrying heavy loads, such as bricks" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11060, - "mets": 8, - "name": "Carrying moderate loads up stairs, moving boxes (16-40 pounds)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11070, - "mets": 2.5, - "name": "Chambermaid, making bed (nursing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11120, - "mets": 5.5, - "name": "Construction, outside, remodeling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11130, - "mets": 3.5, - "name": "Electrical work, plumbing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11370, - "mets": 4.5, - "name": "Furriery" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11380, - "mets": 6, - "name": "Horse grooming" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11615, - "mets": 4, - "name": "Lifting items continuously, 10 - 20 lbs, with limited walking or resting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11420, - "mets": 3.5, - "name": "Locksmith" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11480, - "mets": 7, - "name": "Masonry concrete" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11485, - "mets": 4, - "name": "Massage therapist (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11490, - "mets": 7.5, - "name": "Moving, pushing heavy objects, 75 lbs or more (desks, moving van work)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11500, - "mets": 2.5, - "name": "Operating heavy duty equipment/automated, not driving" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11510, - "mets": 4.5, - "name": "Orange grove work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11520, - "mets": 2.299999952316284, - "name": "Printing (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11530, - "mets": 2.5, - "name": "Shoe repair, general" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 11540, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "Digging ditches" - }, - { - "id": 11550, - "maxSpeedMPH": -1, - "mets": 9, - "minSpeedMPH": -1, - "name": "Heavy lifting (more than 16 pounds/minute)" - }, - { - "id": 11560, - "maxSpeedMPH": -1, - "mets": 6, - "minSpeedMPH": -1, - "name": "Light lifting (less than 10 pounds/minute)" - }, - { - "id": 11570, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "Moderate lifting (10 to 15 pounds/minute)" - } - ], - "hasSpeed": false, - "id": 90007, - "name": "Shoveling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11580, - "mets": 1.5, - "name": "Sitting - light office work, general (chemistry lab work, light use of hand tools, watch repair or micro-assembly, light assembly/repair), sitting, reading, driving at work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11585, - "mets": 1.5, - "name": "Sitting - meetings, general, and/or with talking involved, eating at a business meeting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11590, - "mets": 2.5, - "name": "Sitting; moderate (heavy levers, riding mower/forklift, crane operation), teaching stretching or yoga" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11495, - "mets": 12, - "name": "Skindiving or SCUBA diving as a frogman (Navy Seal)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11600, - "mets": 2.299999952316284, - "name": "Standing; light (bartending, store clerk, assembling, filing, duplicating, putting up a Christmas tree), standing and talking at work, changing clothes when teaching physical education" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11610, - "mets": 3, - "name": "Standing; light/moderate (assemble/repair heavy parts, welding, stocking, auto repair, pack boxes for moving, etc.), patient care (as in nursing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11620, - "mets": 3.5, - "name": "Standing; moderate (assembling at fast rate, intermittent, lifting 50 lbs, hitch/twisting ropes)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11630, - "mets": 4, - "name": "Standing; moderate/heavy (lifting more than 50 lbs, masonry, painting, paper hanging)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11875, - "mets": 4, - "name": "Teach physical education, exercise, sports classes (non-sport play)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11876, - "mets": 6.5, - "name": "Teach physical education, exercise, sports classes (participate in the class)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11766, - "mets": 6.5, - "name": "Truck driving, loading and unloading truck (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11770, - "mets": 1.5, - "name": "Typing, electric, manual or computer" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11780, - "mets": 6, - "name": "Using heavy power tools such as pneumatic tools (jackhammers, drills. etc.)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11790, - "mets": 8, - "name": "Using heavy tools (not power) such as shovel, pick, tunnel bar, spade" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 11810, - "maxSpeedMPH": -1, - "mets": 4.5, - "minSpeedMPH": -1, - "name": "Carrying under 25 pounds" - }, - { - "id": 11820, - "maxSpeedMPH": -1, - "mets": 5, - "minSpeedMPH": -1, - "name": "Carrying 25 to 49 pounds" - }, - { - "id": 11830, - "maxSpeedMPH": -1, - "mets": 6.5, - "minSpeedMPH": -1, - "name": "Carrying 50 to 74 pounds" - }, - { - "id": 11840, - "maxSpeedMPH": -1, - "mets": 7.5, - "minSpeedMPH": -1, - "name": "Carrying 75 to 99 pounds" - }, - { - "id": 11850, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "Carrying over 100 pounds" - } - ], - "hasSpeed": false, - "id": 90008, - "name": "Walking while carrying things" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11870, - "mets": 3, - "name": "Working in scene shop, theater actor, backstage employee" - } - ], - "id": 8, - "name": "Occupations", - "subCategories": [ - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11090, - "mets": 6.5, - "name": "Coal mining, erecting supports" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11100, - "mets": 6, - "name": "Coal mining, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11080, - "mets": 6.5, - "name": "Drilling coal, rock" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11110, - "mets": 7, - "name": "Shoveling coal" - } - ], - "id": 9, - "name": "Coal Mining" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11121, - "mets": 3, - "name": "Buffing the floor with electric buffer" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11122, - "mets": 2.5, - "name": "Cleaning sink and toilet, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11123, - "mets": 2.5, - "name": "Dusting, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11124, - "mets": 4, - "name": "Feathering arena floor, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11125, - "mets": 3.5, - "name": "General cleaning, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11126, - "mets": 3.5, - "name": "Mopping, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11127, - "mets": 3, - "name": "Taking out the trash, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11128, - "mets": 2.5, - "name": "Vacuuming, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11129, - "mets": 3, - "name": "Vacuuming, moderate effort" - } - ], - "id": 10, - "name": "Custodial Work" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11140, - "mets": 8, - "name": "Baling hay, cleaning barn, poultry work, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11151, - "mets": 4, - "name": "Chasing cattle or other livestock on horseback, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11152, - "mets": 2, - "name": "Chasing cattle or other livestock, driving, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11150, - "mets": 3.5, - "name": "Chasing cattle, non-strenuous (walking), moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11160, - "mets": 2.5, - "name": "Driving a harvester, cutting hay, irrigation work" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11170, - "mets": 2.5, - "name": "Driving a tractor" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11190, - "mets": 4.5, - "name": "Feeding cattle, horses" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11180, - "mets": 4, - "name": "Feeding small animals" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11200, - "mets": 8, - "name": "Forking straw bales, cleaning corral or bars, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11191, - "mets": 4.5, - "name": "Hauling water for animals, general hauling water" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11210, - "mets": 3, - "name": "Milking by hand, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11220, - "mets": 1.5, - "name": "Milking by machine, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11230, - "mets": 5.5, - "name": "Shoveling grain, moderate effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11192, - "mets": 6, - "name": "Taking care of animals (grooming, brushing, shearing sheep, assisting with birthing, medical care, branding)" - } - ], - "id": 11, - "name": "Farming" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11245, - "mets": 11, - "name": "Fire fighter, climbing ladder with full gear" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11240, - "mets": 12, - "name": "Fire fighter, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11246, - "mets": 8, - "name": "Fire fighter, hauling hoses on ground" - } - ], - "id": 12, - "name": "Fire Fighter" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11250, - "mets": 17, - "name": "Ax chopping, fast" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11260, - "mets": 5, - "name": "Ax chopping, slow" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11270, - "mets": 7, - "name": "Barking trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11280, - "mets": 11, - "name": "Carrying logs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11290, - "mets": 8, - "name": "Felling trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11300, - "mets": 8, - "name": "Forestry, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11310, - "mets": 5, - "name": "Hoeing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11320, - "mets": 6, - "name": "Planting by hand" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11330, - "mets": 7, - "name": "Sawing by hand" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11340, - "mets": 4.5, - "name": "Sawing with a chainsaw" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11350, - "mets": 9, - "name": "Trimming trees" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11360, - "mets": 4, - "name": "Weeding" - } - ], - "id": 13, - "name": "Forestry" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11390, - "mets": 8, - "name": "Horseback riding - galloping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11400, - "mets": 6.5, - "name": "Horseback riding - trotting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11410, - "mets": 2.5999999046325684, - "name": "Horseback riding - walking" - } - ], - "id": 14, - "name": "Horse Trainer" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11430, - "mets": 2.5, - "name": "Machine tooling, machining, working sheet metal" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11440, - "mets": 3, - "name": "Operating a lathe" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11450, - "mets": 5, - "name": "Operating a punch press" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11460, - "mets": 4, - "name": "Tapping and drilling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11470, - "mets": 3, - "name": "Welding" - } - ], - "id": 15, - "name": "Machine Shop" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11525, - "mets": 2.5, - "name": "Directing traffic (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11526, - "mets": 2, - "name": "Driving a squad car (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11528, - "mets": 4, - "name": "Making an arrest (standing)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11527, - "mets": 1.2999999523162842, - "name": "Riding in a squad car (sitting)" - } - ], - "id": 16, - "name": "Police" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11640, - "mets": 5, - "name": "Fettling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11650, - "mets": 5.5, - "name": "Forging" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11660, - "mets": 8, - "name": "Hand rolling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11670, - "mets": 8, - "name": "Merchant mill rolling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11680, - "mets": 11, - "name": "Removing slag" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11690, - "mets": 7.5, - "name": "Tending furnace" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11700, - "mets": 5.5, - "name": "Tipping molds" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11710, - "mets": 8, - "name": "Working in general" - } - ], - "id": 17, - "name": "Steel Mill" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11740, - "mets": 2, - "name": "Hand sewing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11750, - "mets": 2.5, - "name": "Machine sewing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11760, - "mets": 4, - "name": "Pressing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11720, - "mets": 2.5, - "name": "Tailoring, cutting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11730, - "mets": 2.5, - "name": "Tailoring, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11765, - "mets": 3.5, - "name": "Weaving" - } - ], - "id": 18, - "name": "Tailoring" - } - ] - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 53003, - "mets": 5.300000190734863, - "name": "Pilates, Advanced" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 53001, - "mets": 3.0999999046325684, - "name": "Pilates, Beginner" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 53002, - "mets": 4.300000190734863, - "name": "Pilates, Intermediate" - } - ], - "id": 32, - "name": "Pilates" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13030, - "mets": 1.5, - "name": "Eating (sitting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13040, - "mets": 2, - "name": "Grooming (washing, shaving, brushing teeth, washing hands, putting on make-up), sitting or standing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13045, - "mets": 2.5, - "name": "Hairstyling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13046, - "mets": 1, - "name": "Having hair or nails done by someone else, sitting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13000, - "mets": 2, - "name": "Standing - getting ready for bed, in general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13036, - "mets": 1, - "name": "Taking medication, sitting or standing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 13035, - "mets": 2, - "name": "Talking and eating or eating only (standing)" - } - ], - "id": 19, - "name": "Self care" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15010, - "mets": 3.5, - "name": "Archery (non-hunting)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15020, - "mets": 7, - "name": "Badminton, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15030, - "mets": 4.5, - "name": "Badminton, social singles and doubles, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15040, - "mets": 8, - "name": "Basketball, game" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15050, - "mets": 6, - "name": "Basketball, non-game, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15060, - "mets": 7, - "name": "Basketball, officiating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15070, - "mets": 4.5, - "name": "Basketball, shooting baskets" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15075, - "mets": 6.5, - "name": "Basketball, wheelchair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15080, - "mets": 2.5, - "name": "Billiards" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 55003, - "mets": 8.899999618530273, - "name": "Bootcamp" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15090, - "mets": 3, - "name": "Bowling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15100, - "mets": 12, - "name": "Boxing, in ring, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15110, - "mets": 6, - "name": "Boxing, punching bag" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15120, - "mets": 9, - "name": "Boxing, sparring" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15130, - "mets": 7, - "name": "Broombail" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15135, - "mets": 5, - "name": "Children's games (hopscotch, 4-square, dodge bail, playground apparatus, t-ball, tetherbai;, marbles, jacks, acrace games)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15140, - "mets": 4, - "name": "Coaching: football, soccer, basketball, baseball, swimming, etc." - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15150, - "mets": 5, - "name": "Cricket (batting, bowling)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15160, - "mets": 2.5, - "name": "Croquet" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15170, - "mets": 4, - "name": "Curling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15180, - "mets": 2.5, - "name": "Darts, wail or lawn" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15190, - "mets": 6, - "name": "Drag racing, pushing or driving a car" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15200, - "mets": 6, - "name": "Fencing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15235, - "mets": 2.5, - "name": "Football or baseball, playing catch" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15210, - "mets": 9, - "name": "Football, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15230, - "mets": 8, - "name": "Football, touch, flag, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15240, - "mets": 3, - "name": "Frisbee playing, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15250, - "mets": 8, - "name": "Frisbee, ultimate" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15255, - "mets": 4.5, - "name": "Golf" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15270, - "mets": 3, - "name": "Golf, miniature, driving range" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15290, - "mets": 3.5, - "name": "Golf, using power cart" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15265, - "mets": 4.5, - "name": "Golf, walking and carrying clubs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15285, - "mets": 4.300000190734863, - "name": "Golf, walking and pulling clubs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15300, - "mets": 4, - "name": "Gymnastics, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15310, - "mets": 4, - "name": "Hacky sack" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15340, - "mets": 3.5, - "name": "Hand gliding" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15320, - "mets": 12, - "name": "Handball, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15330, - "mets": 8, - "name": "Handball, team" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15350, - "mets": 8, - "name": "Hockey, field" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15360, - "mets": 8, - "name": "Hockey, ice" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11390, - "mets": 8, - "name": "Horseback riding - galloping" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11400, - "mets": 6.5, - "name": "Horseback riding - trotting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 11410, - "mets": 2.5999999046325684, - "name": "Horseback riding - walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15370, - "mets": 4, - "name": "Horseback riding, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15380, - "mets": 3.5, - "name": "Horseback riding, saddling horse, grooming horse" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15410, - "mets": 3, - "name": "Horseshoe pitching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15420, - "mets": 12, - "name": "Jai alai" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15440, - "mets": 4, - "name": "Juggling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15450, - "mets": 7, - "name": "Kickball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 55002, - "mets": 9.399999618530273, - "name": "Kickboxing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15460, - "mets": 8, - "name": "Lacrosse" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15430, - "mets": 10, - "name": "Martial Arts" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15470, - "mets": 4, - "name": "Motor-cross" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15480, - "mets": 9, - "name": "Orienteering" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15500, - "mets": 6, - "name": "Paddleball, casual, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15490, - "mets": 10, - "name": "Paddleball, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15510, - "mets": 8, - "name": "Polo" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15530, - "mets": 7, - "name": "Racquetball, casual, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15520, - "mets": 10, - "name": "Racquetball, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15535, - "mets": 11, - "name": "Rock climbing, ascending rock" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15540, - "mets": 8, - "name": "Rock climbing, rappelling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15591, - "mets": 12.5, - "name": "Roller blading (in-line skating)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15550, - "mets": 12, - "name": "Rope jumping, fast" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15551, - "mets": 10, - "name": "Rope jumping, moderate, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15552, - "mets": 8, - "name": "Rope jumping, slow" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15560, - "mets": 10, - "name": "Rugby" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15570, - "mets": 3, - "name": "Shuffleboard, lawn bowling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15580, - "mets": 5, - "name": "Skateboarding" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15590, - "mets": 7, - "name": "Skating, roller" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15600, - "mets": 3.5, - "name": "Sky diving" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15610, - "mets": 7, - "name": "Soccer, casual, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15605, - "mets": 10, - "name": "Soccer, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15620, - "mets": 5, - "name": "Softball or baseball, fast or slow pitch, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15630, - "mets": 4, - "name": "Softball, officiating" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15640, - "mets": 6, - "name": "Softball, pitching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 55001, - "mets": 6.5, - "name": "Spinning" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15650, - "mets": 12, - "name": "Squash" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15660, - "mets": 4, - "name": "Table tennis, ping pong" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15670, - "mets": 4, - "name": "Tai chi" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15675, - "mets": 7, - "name": "Tennis" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15680, - "mets": 6, - "name": "Tennis, doubles" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15690, - "mets": 8, - "name": "Tennis, singles" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15733, - "mets": 6, - "name": "Track and field (high jump, long jump, triple jump, javelin, pole vault)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15732, - "mets": 4, - "name": "Track and field (shot, discus, hammer throw)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15734, - "mets": 10, - "name": "Track and field (steeplechase, hurdles)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15700, - "mets": 3.5, - "name": "Trampoline" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15710, - "mets": 4, - "name": "Volleyball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15725, - "mets": 8, - "name": "Volleyball, beach" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15711, - "mets": 8, - "name": "Volleyball, competitive, in gymnasium" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15720, - "mets": 3, - "name": "Volleyball, non-competitive, 6 - 9 member team, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15731, - "mets": 7, - "name": "Wallyball, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 15730, - "mets": 6, - "name": "Wrestling (one match = 5 minutes)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 56001, - "mets": 5.5, - "name": "Zumba" - } - ], - "id": 20, - "name": "Sports and Workouts", - "subCategories": [ - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 1010, - "maxSpeedMPH": 9.9, - "mets": 4, - "minSpeedMPH": -1, - "name": "under 10 mph" - }, - { - "id": 1020, - "maxSpeedMPH": 11.9, - "mets": 6, - "minSpeedMPH": 10, - "name": "10-11.9 mph" - }, - { - "id": 1030, - "maxSpeedMPH": 13.9, - "mets": 8, - "minSpeedMPH": 12, - "name": "12 to 13.9mph" - }, - { - "id": 1009, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "Mountain Bike" - }, - { - "id": 1040, - "maxSpeedMPH": 15.9, - "mets": 10, - "minSpeedMPH": 14, - "name": "14 to 15.9 mph" - }, - { - "id": 1050, - "maxSpeedMPH": 19, - "mets": 12, - "minSpeedMPH": 16, - "name": "16 to 19mph" - }, - { - "id": 1060, - "maxSpeedMPH": -1, - "mets": 16, - "minSpeedMPH": 20, - "name": "over 20mph" - } - ], - "hasSpeed": true, - "id": 90001, - "name": "Bike" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 1070, - "mets": 5, - "name": "Unicycling" - } - ], - "id": 21, - "name": "Bicycling" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2020, - "mets": 8, - "name": "Calisthenics (e.g. pushups, situps, pullups, jumping jacks), heavy, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2030, - "mets": 3.5, - "name": "Calisthenics, home exercise, light or moderate effort, general (example: back exercises), going up and down from floor" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2040, - "mets": 8, - "name": "Circuit training" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2060, - "mets": 5.5, - "name": "Health club exercise, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2101, - "mets": 2.5, - "name": "Mild stretching" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 2071, - "maxSpeedMPH": -1, - "mets": 3.5, - "minSpeedMPH": -1, - "name": "light effort (50 watts)" - }, - { - "id": 2072, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "moderate effort (100 watts)" - }, - { - "id": 2073, - "maxSpeedMPH": -1, - "mets": 8.5, - "minSpeedMPH": -1, - "name": "vigorous effort (150 watts)" - }, - { - "id": 2074, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": -1, - "name": "very vigorous effort (200 watts)" - } - ], - "hasSpeed": false, - "id": 90003, - "name": "Rowing Machine" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2080, - "mets": 7, - "name": "Ski machine, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2090, - "mets": 6, - "name": "Slimnastics, jazzercise" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2065, - "mets": 9, - "name": "Stair Climber" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 2011, - "maxSpeedMPH": -1, - "mets": 3, - "minSpeedMPH": -1, - "name": "very light effort (50 watts)" - }, - { - "id": 2012, - "maxSpeedMPH": -1, - "mets": 5.5, - "minSpeedMPH": -1, - "name": "light effort (100 watts)" - }, - { - "id": 2013, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "moderate effort (150 watts)" - }, - { - "id": 2014, - "maxSpeedMPH": -1, - "mets": 10.5, - "minSpeedMPH": -1, - "name": "vigorous effort (200 watts)" - }, - { - "id": 2015, - "maxSpeedMPH": -1, - "mets": 12.5, - "minSpeedMPH": -1, - "name": "very vigorous effort (250 watts)" - } - ], - "hasSpeed": false, - "id": 90002, - "name": "Stationary Bike" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2100, - "mets": 2.5, - "name": "Stretching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2110, - "mets": 6, - "name": "Teaching aerobic exercise class" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2120, - "mets": 4, - "name": "Water aerobics, water calisthenics" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2050, - "mets": 6, - "name": "Weight lifting (free weight, nautilus or universal-type), power lifting or body building, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2130, - "mets": 3, - "name": "Weight lifting (free, nautilus or universal-type), light or moderate effort, light workout, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 2135, - "mets": 1, - "name": "Whirlpool, sitting" - } - ], - "id": 22, - "name": "Conditioning exercises" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 50201, - "maxSpeedMPH": -1, - "mets": 10.399999618530273, - "minSpeedMPH": -1, - "name": "Low Speed" - }, - { - "id": 50202, - "maxSpeedMPH": -1, - "mets": 13.199999809265137, - "minSpeedMPH": -1, - "name": "Medium Speed" - }, - { - "id": 50203, - "maxSpeedMPH": -1, - "mets": 16.399999618530273, - "minSpeedMPH": -1, - "name": "High Speed" - } - ], - "hasSpeed": false, - "id": 90018, - "name": "Elliptical, High Resistance" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 50001, - "maxSpeedMPH": -1, - "mets": 2, - "minSpeedMPH": -1, - "name": "Low Speed" - }, - { - "id": 50002, - "maxSpeedMPH": -1, - "mets": 4.699999809265137, - "minSpeedMPH": -1, - "name": "Medium Speed" - }, - { - "id": 50003, - "maxSpeedMPH": -1, - "mets": 8, - "minSpeedMPH": -1, - "name": "High Speed" - } - ], - "hasSpeed": false, - "id": 90016, - "name": "Elliptical, Low Resistance" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 50101, - "maxSpeedMPH": -1, - "mets": 6, - "minSpeedMPH": -1, - "name": "Low Speed" - }, - { - "id": 50102, - "maxSpeedMPH": -1, - "mets": 8.699999809265137, - "minSpeedMPH": -1, - "name": "Medium Speed" - }, - { - "id": 50103, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": -1, - "name": "High Speed" - } - ], - "hasSpeed": false, - "id": 90017, - "name": "Elliptical, Medium Resistance" - } - ], - "id": 29, - "name": "Elliptical" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12010, - "mets": 6, - "name": "Jog/walk combination (jogging component of less than 10 minutes)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12027, - "mets": 4.5, - "name": "Jogging on a mini-tramp" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12020, - "mets": 7, - "name": "Jogging, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12025, - "mets": 8, - "name": "Jogging, in place" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 12030, - "maxSpeedMPH": 5, - "mets": 8, - "minSpeedMPH": -1, - "name": "5 mph (12 min/mile)" - }, - { - "id": 12040, - "maxSpeedMPH": 5.2, - "mets": 9, - "minSpeedMPH": -1, - "name": "5.2 mph (11.5 min/mile)" - }, - { - "id": 12050, - "maxSpeedMPH": 6, - "mets": 10, - "minSpeedMPH": -1, - "name": "6 mph (10 min/mile)" - }, - { - "id": 12060, - "maxSpeedMPH": 6.7, - "mets": 11, - "minSpeedMPH": -1, - "name": "6.7 mph (9 min/mile)" - }, - { - "id": 12070, - "maxSpeedMPH": 7, - "mets": 11.5, - "minSpeedMPH": -1, - "name": "7 mph (8.5 min/mile)" - }, - { - "id": 12080, - "maxSpeedMPH": 7.5, - "mets": 12.5, - "minSpeedMPH": -1, - "name": "7.5 mph (8 min/mile)" - }, - { - "id": 12090, - "maxSpeedMPH": 8, - "mets": 13.5, - "minSpeedMPH": -1, - "name": "8 mph (7.5 min/mile)" - }, - { - "id": 12100, - "maxSpeedMPH": 8.6, - "mets": 14, - "minSpeedMPH": -1, - "name": "8.6 mph (7 min/mile)" - }, - { - "id": 12110, - "maxSpeedMPH": 9, - "mets": 15, - "minSpeedMPH": -1, - "name": "9 mph (6.5 min/mile)" - }, - { - "id": 12120, - "maxSpeedMPH": 10, - "mets": 16, - "minSpeedMPH": -1, - "name": "10 mph (6 min/mile)" - }, - { - "id": 12130, - "maxSpeedMPH": 10.9, - "mets": 18, - "minSpeedMPH": -1, - "name": "10.9 mph (5.5 min/mile)" - } - ], - "hasSpeed": true, - "id": 90009, - "name": "Run" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12190, - "mets": 8, - "name": "Running while pushing a stroller or wheelchair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12140, - "mets": 9, - "name": "Running, cross country" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12180, - "mets": 10, - "name": "Running, on a track, team practice" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 12170, - "mets": 15, - "name": "Running, stairs, up" - } - ], - "id": 23, - "name": "Running" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51002, - "maxSpeedMPH": 2.4, - "mets": 3, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51003, - "maxSpeedMPH": 3.4, - "mets": 3.9000000953674316, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51004, - "maxSpeedMPH": 4.4, - "mets": 4.800000190734863, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51005, - "maxSpeedMPH": 5.4, - "mets": 10.399999618530273, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51006, - "maxSpeedMPH": 6.4, - "mets": 12.199999809265137, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51007, - "maxSpeedMPH": 7.4, - "mets": 14, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51008, - "maxSpeedMPH": 8.4, - "mets": 16, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51009, - "maxSpeedMPH": 9.4, - "mets": 17.799999237060547, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51010, - "maxSpeedMPH": 10.4, - "mets": 19.600000381469727, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90019, - "name": "Treadmill, 0% Incline" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51202, - "maxSpeedMPH": 2.4, - "mets": 6.300000190734863, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51203, - "maxSpeedMPH": 3.4, - "mets": 8.800000190734863, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51204, - "maxSpeedMPH": 4.4, - "mets": 11.5, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51205, - "maxSpeedMPH": 5.4, - "mets": 14.5, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51206, - "maxSpeedMPH": 6.4, - "mets": 17.200000762939453, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51207, - "maxSpeedMPH": 7.4, - "mets": 19.899999618530273, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51208, - "maxSpeedMPH": 8.4, - "mets": 22.5, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51209, - "maxSpeedMPH": 9.4, - "mets": 25.200000762939453, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51210, - "maxSpeedMPH": 10.4, - "mets": 28, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90021, - "name": "Treadmill, 10% Incline" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51302, - "maxSpeedMPH": 2.4, - "mets": 8, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51303, - "maxSpeedMPH": 3.4, - "mets": 11.399999618530273, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51304, - "maxSpeedMPH": 4.4, - "mets": 14.800000190734863, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51305, - "maxSpeedMPH": 5.4, - "mets": 16.600000381469727, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51306, - "maxSpeedMPH": 6.4, - "mets": 19.700000762939453, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51307, - "maxSpeedMPH": 7.4, - "mets": 22.799999237060547, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51308, - "maxSpeedMPH": 8.4, - "mets": 25.899999618530273, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51309, - "maxSpeedMPH": 9.4, - "mets": 28.899999618530273, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51310, - "maxSpeedMPH": 10.4, - "mets": 32, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90022, - "name": "Treadmill, 15% Incline" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 51102, - "maxSpeedMPH": 2.4, - "mets": 4.599999904632568, - "minSpeedMPH": -1, - "name": "2mph" - }, - { - "id": 51103, - "maxSpeedMPH": 3.4, - "mets": 6.400000095367432, - "minSpeedMPH": 2.5, - "name": "3mph" - }, - { - "id": 51104, - "maxSpeedMPH": 4.4, - "mets": 8.100000381469727, - "minSpeedMPH": 3.5, - "name": "4mph" - }, - { - "id": 51105, - "maxSpeedMPH": 5.4, - "mets": 12.5, - "minSpeedMPH": 4.5, - "name": "5mph" - }, - { - "id": 51106, - "maxSpeedMPH": 6.4, - "mets": 14.800000190734863, - "minSpeedMPH": 5.5, - "name": "6mph" - }, - { - "id": 51107, - "maxSpeedMPH": 7.4, - "mets": 16.899999618530273, - "minSpeedMPH": 6.5, - "name": "7mph" - }, - { - "id": 51108, - "maxSpeedMPH": 8.4, - "mets": 19.200000762939453, - "minSpeedMPH": 7.5, - "name": "8mph" - }, - { - "id": 51109, - "maxSpeedMPH": 9.4, - "mets": 21.5, - "minSpeedMPH": 8.5, - "name": "9mph" - }, - { - "id": 51110, - "maxSpeedMPH": 10.4, - "mets": 23.700000762939453, - "minSpeedMPH": 9.5, - "name": "10mph" - } - ], - "hasSpeed": true, - "id": 90020, - "name": "Treadmill, 5% Incline" - } - ], - "id": 30, - "name": "Treadmill" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18010, - "mets": 2.5, - "name": "Boating, power" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 18040, - "maxSpeedMPH": 3.9, - "mets": 3, - "minSpeedMPH": -1, - "name": "2.0 - 3.9 mph, light effort" - }, - { - "id": 18050, - "maxSpeedMPH": 5.9, - "mets": 7, - "minSpeedMPH": 4, - "name": "4.0 - 5.9 mph, moderate effort" - }, - { - "id": 18060, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": 6, - "name": "Faster than 6 mph, vigorous effort" - } - ], - "hasSpeed": true, - "id": 90014, - "name": "Canoeing or Rowing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18025, - "mets": 3.299999952316284, - "name": "Canoeing, harvesting wild rice, knocking rice off the stalks" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18020, - "mets": 4, - "name": "Canoeing, on camping trip" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18030, - "mets": 7, - "name": "Canoeing, portaging" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18080, - "mets": 12, - "name": "Canoeing, rowing, in competition, or crew or sculling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18090, - "mets": 3, - "name": "Diving, springboard or platform" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18100, - "mets": 5, - "name": "Kayaking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18110, - "mets": 4, - "name": "Paddle boat" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18140, - "mets": 3, - "name": "Sailing, Sunfish/Laser/Hobby Cat, Keel boats, ocean sailing, yachting" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18120, - "mets": 3, - "name": "Sailing, boat and board sailing, windsurfing, ice sailing, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18130, - "mets": 5, - "name": "Sailing, in competition" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18150, - "mets": 6, - "name": "Skiing, water" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18160, - "mets": 7, - "name": "Skimobiling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18180, - "mets": 16, - "name": "Skindiving, fast" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18190, - "mets": 12.5, - "name": "Skindiving, moderate" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18200, - "mets": 7, - "name": "Skindiving, scuba diving, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18210, - "mets": 5, - "name": "Snorkeling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18220, - "mets": 3, - "name": "Surfing, body or board" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 58001, - "maxSpeedMPH": 0.9, - "mets": 4, - "minSpeedMPH": -1, - "name": "less than 25 yards/min" - }, - { - "id": 18240, - "maxSpeedMPH": 1.7, - "mets": 8, - "minSpeedMPH": 1, - "name": "25-50 yards/min" - }, - { - "id": 58002, - "maxSpeedMPH": 2.6, - "mets": 11, - "minSpeedMPH": 1.8, - "name": "50-75 yards/min" - }, - { - "id": 58003, - "maxSpeedMPH": 3.4, - "mets": 14, - "minSpeedMPH": 2.7, - "name": "75-100 yards/min" - } - ], - "hasSpeed": true, - "id": 90024, - "name": "Swimming" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18230, - "mets": 10, - "name": "Swimming laps, freestyle, fast, vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18250, - "mets": 7, - "name": "Swimming, backstroke, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18260, - "mets": 10, - "name": "Swimming, breaststroke, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18270, - "mets": 11, - "name": "Swimming, butterfly, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18280, - "mets": 11, - "name": "Swimming, crawl, fast (75 yards/minute), vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18290, - "mets": 8, - "name": "Swimming, crawl, slow (50 yards/minute), moderate or light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18300, - "mets": 6, - "name": "Swimming, lake, ocean, river" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18310, - "mets": 6, - "name": "Swimming, leisurely, not lap swimming, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18320, - "mets": 8, - "name": "Swimming, sidestroke, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18330, - "mets": 8, - "name": "Swimming, synchronized" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18340, - "mets": 10, - "name": "Swimming, treading water, fast vigorous effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18350, - "mets": 4, - "name": "Swimming, treading water, moderate effort, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18355, - "mets": 4, - "name": "Water aerobics, water calisthenics" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18366, - "mets": 8, - "name": "Water jogging" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18360, - "mets": 10, - "name": "Water polo" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18365, - "mets": 3, - "name": "Water volleyball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 18370, - "mets": 5, - "name": "Whitewater rafting, kayaking, or canoeing" - } - ], - "id": 25, - "name": "Water Activities" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 19080, - "maxSpeedMPH": 2.5, - "mets": 7, - "minSpeedMPH": -1, - "name": "2.5 mph, slow or light effort, ski walking" - }, - { - "id": 19090, - "maxSpeedMPH": 4.9, - "mets": 8, - "minSpeedMPH": 4, - "name": "4.0-4.9 mph, moderate speed and effort, general" - }, - { - "id": 19100, - "maxSpeedMPH": 7.9, - "mets": 9, - "minSpeedMPH": 5, - "name": "5.0-7.9 mph, brisk speed, vigorous effort" - }, - { - "id": 19110, - "maxSpeedMPH": -1, - "mets": 14, - "minSpeedMPH": 8, - "name": "faster than 8.0 mph, racing" - } - ], - "hasSpeed": true, - "id": 90015, - "name": "Cross Country Skiing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19010, - "mets": 6, - "name": "Moving ice house (set up/drill holes, etc.)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19020, - "mets": 5.5, - "name": "Skating, ice, 9 mph or less" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19030, - "mets": 7, - "name": "Skating, ice, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19040, - "mets": 9, - "name": "Skating, ice, rapidly, more than 9 mph" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19050, - "mets": 15, - "name": "Skating, speed, competitive" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19060, - "mets": 7, - "name": "Ski jumping (climb up carrying skis)" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19130, - "mets": 16.5, - "name": "Skiing, cross country, hard snow, uphill, maximum, snow mountaineering" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19150, - "mets": 5, - "name": "Skiing, downhill, light effort" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19160, - "mets": 6, - "name": "Skiing, downhill, moderate effort, general" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19170, - "mets": 8, - "name": "Skiing, downhill, vigorous effort, racing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19180, - "mets": 7, - "name": "Sledding, tobogganing, bobsledding, luge" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19190, - "mets": 8, - "name": "Snow shoeing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 19200, - "mets": 3.5, - "name": "Snowmobiling" - } - ], - "id": 24, - "name": "Winter activities" - } - ] - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16010, - "mets": 2, - "name": "Automobile or light truck (not a semi) driving" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16050, - "mets": 3, - "name": "Driving heavy truck, tractor, bus" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16020, - "mets": 2, - "name": "Flying airplane" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16030, - "mets": 2.5, - "name": "Motor scooter, motorcycle" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16040, - "mets": 6, - "name": "Pushing plane in and out of hangar" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16016, - "mets": 1, - "name": "Riding in a bus" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 16015, - "mets": 1, - "name": "Riding in a car or truck" - } - ], - "id": 26, - "name": "Transportation" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17010, - "mets": 7, - "name": "Backpacking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17085, - "mets": 2.5, - "name": "Bird watching" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17020, - "mets": 3.5, - "name": "Carrying infant or 15 pound load (e.g. suitcase)" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 17026, - "maxSpeedMPH": -1, - "mets": 5, - "minSpeedMPH": -1, - "name": "1 to 15 lb load" - }, - { - "id": 17027, - "maxSpeedMPH": -1, - "mets": 6, - "minSpeedMPH": -1, - "name": "16 to 24 lb load" - }, - { - "id": 17028, - "maxSpeedMPH": -1, - "mets": 8, - "minSpeedMPH": -1, - "name": "25 to 49 lb load" - }, - { - "id": 17029, - "maxSpeedMPH": -1, - "mets": 10, - "minSpeedMPH": -1, - "name": "50 to 74 lb load" - }, - { - "id": 17030, - "maxSpeedMPH": -1, - "mets": 12, - "minSpeedMPH": -1, - "name": "74+ lb load" - } - ], - "hasSpeed": false, - "id": 90011, - "name": "Carrying load upstairs" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17130, - "mets": 8, - "name": "Going up stairs, using or climbing up ladder" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 17035, - "maxSpeedMPH": -1, - "mets": 7, - "minSpeedMPH": -1, - "name": "With 0 to 9 pound pack" - }, - { - "id": 17040, - "maxSpeedMPH": -1, - "mets": 7.5, - "minSpeedMPH": -1, - "name": "With 10 to 20 pound pack" - }, - { - "id": 17050, - "maxSpeedMPH": -1, - "mets": 8, - "minSpeedMPH": -1, - "name": "With 21 to 42 pound pack" - }, - { - "id": 17060, - "maxSpeedMPH": -1, - "mets": 9, - "minSpeedMPH": -1, - "name": "With 42+ pound pack" - } - ], - "hasSpeed": true, - "id": 90012, - "name": "Hike" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17080, - "mets": 6, - "name": "Hiking, cross country" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17090, - "mets": 6.5, - "name": "Marching, rapidly, military" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17105, - "mets": 4, - "name": "Pushing a wheelchair" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17100, - "mets": 2.5, - "name": "Pushing or pulling stroller with child or walking with children" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17110, - "mets": 6.5, - "name": "Race walking" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17120, - "mets": 8, - "name": "Rock or mountain climbing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17140, - "mets": 5, - "name": "Using crutches" - }, - { - "accessLevel": "PUBLIC", - "activityLevels": [ - { - "id": 17151, - "maxSpeedMPH": 1.5, - "mets": 2, - "minSpeedMPH": -1, - "name": "less than 2 mph, strolling very slowly" - }, - { - "id": 17152, - "maxSpeedMPH": 2, - "mets": 2.5, - "minSpeedMPH": -1, - "name": "2.0 mph, slow pace" - }, - { - "id": 17170, - "maxSpeedMPH": 2.5, - "mets": 3, - "minSpeedMPH": -1, - "name": "2.5 mph" - }, - { - "id": 17190, - "maxSpeedMPH": 3, - "mets": 3.299999952316284, - "minSpeedMPH": -1, - "name": "3.0 mph, moderate pace" - }, - { - "id": 17200, - "maxSpeedMPH": 3.5, - "mets": 3.799999952316284, - "minSpeedMPH": -1, - "name": "3.5 mph, walking for exercise" - }, - { - "id": 17220, - "maxSpeedMPH": 4, - "mets": 5, - "minSpeedMPH": -1, - "name": "4.0 mph, very brisk pace" - }, - { - "id": 17230, - "maxSpeedMPH": 4.5, - "mets": 6.300000190734863, - "minSpeedMPH": -1, - "name": "4.5 mph, very, very brisk" - }, - { - "id": 17231, - "maxSpeedMPH": 5, - "mets": 8, - "minSpeedMPH": -1, - "name": "5.0 mph, speed walking" - }, - { - "id": 17165, - "maxSpeedMPH": -1, - "mets": 3, - "minSpeedMPH": -1, - "name": "Walking the dog" - } - ], - "hasSpeed": true, - "id": 90013, - "name": "Walk" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17160, - "mets": 3.5, - "name": "Walking for pleasure" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17260, - "mets": 5, - "name": "Walking, grass track" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 17150, - "mets": 2, - "name": "Walking, household" - } - ], - "id": 27, - "name": "Walking" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54011, - "mets": 3.200000047683716, - "name": "Wii Baseball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54003, - "mets": 2.799999952316284, - "name": "Wii Bowling" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54004, - "mets": 4.800000190734863, - "name": "Wii Boxing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54001, - "mets": 2.5, - "name": "Wii Fit Advanced Step" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54006, - "mets": 3.700000047683716, - "name": "Wii Fit Free Run" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54007, - "mets": 2.299999952316284, - "name": "Wii Fit Free step" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54009, - "mets": 3.700000047683716, - "name": "Wii Fit Island Run" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54005, - "mets": 2.799999952316284, - "name": "Wii Fit Rhythm Boxing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54002, - "mets": 2.5, - "name": "Wii Fit Super Hula Hoop" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54010, - "mets": 2.299999952316284, - "name": "Wii Golf" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 54008, - "mets": 3.700000047683716, - "name": "Wii Tennis" - } - ], - "id": 33, - "name": "Wii Games" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57001, - "mets": 3.200000047683716, - "name": "XBOX Sports - Baseball" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57002, - "mets": 2.299999952316284, - "name": "XBOX Sports - Darts" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57004, - "mets": 3.700000047683716, - "name": "XBOX Sports - Football" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57003, - "mets": 2.299999952316284, - "name": "XBOX Sports - Golf" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57007, - "mets": 2.799999952316284, - "name": "XBOX Sports - Just Dance 3" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57005, - "mets": 3.700000047683716, - "name": "XBOX Sports - Skiing" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 57006, - "mets": 3.700000047683716, - "name": "XBOX Sports - Tennis" - } - ], - "id": 35, - "name": "XBOX Games" - }, - { - "activities": [ - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52001, - "mets": 4.300000190734863, - "name": "Yoga, Ashtanga" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52002, - "mets": 5.900000095367432, - "name": "Yoga, Bikram" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52003, - "mets": 2.299999952316284, - "name": "Yoga, Hatha" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52004, - "mets": 4.300000190734863, - "name": "Yoga, Power" - }, - { - "accessLevel": "PUBLIC", - "hasSpeed": false, - "id": 52005, - "mets": 7.300000190734863, - "name": "Yoga, Vinyasa" - } - ], - "id": 31, - "name": "Yoga" - } - ] - } - } - } -]} \ No newline at end of file diff --git a/shim-server/src/test/resources/fitbit-activity.json b/shim-server/src/test/resources/fitbit-activity.json deleted file mode 100644 index 93fe5b5f..00000000 --- a/shim-server/src/test/resources/fitbit-activity.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "result": { - "date": "2015-04-28", - "content": { - "activities": [ - { - "activityId": 58001, - "activityParentId": 90024, - "activityParentName": "Swimming", - "calories": 141, - "description": "less than 25 yards/min", - "distance": 0.48, - "duration": 1800000, - "hasStartTime": true, - "isFavorite": false, - "lastModified": "2015-04-30T00:45:22.290-07:00", - "logId": 207756316, - "name": "Swimming", - "startDate": "2015-04-28", - "startTime": "21:30" - } - ], - "goals": { - "activeMinutes": 30, - "caloriesOut": 0, - "distance": 8.05, - "floors": 30, - "steps": 10000 - }, - "summary": { - "activeScore": -1, - "activityCalories": 1024, - "caloriesBMR": 1688, - "caloriesOut": 2484, - "distances": [ - { - "activity": "total", - "distance": 5.8 - }, - { - "activity": "tracker", - "distance": 5.8 - }, - { - "activity": "loggedActivities", - "distance": 0.48 - }, - { - "activity": "veryActive", - "distance": 4.01 - }, - { - "activity": "moderatelyActive", - "distance": 0.3 - }, - { - "activity": "lightlyActive", - "distance": 1.49 - }, - { - "activity": "sedentaryActive", - "distance": 0 - }, - { - "activity": "Swimming", - "distance": 0.48 - } - ], - "elevation": 85.34, - "fairlyActiveMinutes": 37, - "floors": 28, - "lightlyActiveMinutes": 109, - "marginalCalories": 649, - "sedentaryMinutes": 740, - "steps": 7551, - "veryActiveMinutes": 48 - } - } - } -} diff --git a/shim-server/src/test/resources/fitbit-bp.json b/shim-server/src/test/resources/fitbit-bp.json deleted file mode 100644 index e45a7585..00000000 --- a/shim-server/src/test/resources/fitbit-bp.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "result": { - "date": "2014-07-13", - "content": { - "average": { - "condition": "Prehypertension", - "diastolic": 79, - "systolic": 124 - }, - "bp": [ - { - "diastolic": 82, - "logId": 437175259, - "systolic": 131, - "time": "11:30" - }, - { - "diastolic": 75, - "logId": 437206449, - "systolic": 122, - "time": "12:30" - }, - { - "diastolic": 80, - "logId": 437214560, - "systolic": 120, - "time": "13:30" - } - ] - } - } -} diff --git a/shim-server/src/test/resources/fitbit-glucose.json b/shim-server/src/test/resources/fitbit-glucose.json deleted file mode 100644 index 7fa6376a..00000000 --- a/shim-server/src/test/resources/fitbit-glucose.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "result": { - "date": "2014-07-13", - "content": { - "glucose": [ - { - "glucose": 5, - "tracker": "Morning", - "time": "12:30" - }, - { - "glucose": 5.1, - "tracker": "Afternoon", - "time": "12:30" - }, - { - "glucose": 5.5, - "tracker": "Evening", - "time": "12:30" - } - ] - } - } -} diff --git a/shim-server/src/test/resources/fitbit-heart-count-1m.json b/shim-server/src/test/resources/fitbit-heart-count-1m.json deleted file mode 100644 index 1c841511..00000000 --- a/shim-server/src/test/resources/fitbit-heart-count-1m.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "activities-heart": [ - { - "customHeartRateZones": [], - "dateTime": "today", - "heartRateZones": [ - { - "caloriesOut": 2.3246, - "max": 94, - "min": 30, - "minutes": 2, - "name": "Out of Range" - }, - { - "caloriesOut": 0, - "max": 132, - "min": 94, - "minutes": 0, - "name": "Fat Burn" - }, - { - "caloriesOut": 0, - "max": 160, - "min": 132, - "minutes": 0, - "name": "Cardio" - }, - { - "caloriesOut": 0, - "max": 220, - "min": 160, - "minutes": 0, - "name": "Peak" - } - ], - "value": "64.2" - } - ], - "activities-heart-intraday": { - "dataset": [ - { - "time": "00:00:00", - "value": 64 - }, - { - "time": "00:00:10", - "value": 63 - }, - { - "time": "00:00:20", - "value": 64 - }, - { - "time": "00:00:30", - "value": 65 - }, - { - "time": "00:00:45", - "value": 65 - } - ], - "datasetInterval": 1, - "datasetType": "second" - } -} \ No newline at end of file diff --git a/shim-server/src/test/resources/fitbit-heart.json b/shim-server/src/test/resources/fitbit-heart.json deleted file mode 100644 index 012f5470..00000000 --- a/shim-server/src/test/resources/fitbit-heart.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "result": { - "date": "2014-07-13", - "content": { - "average": [ - { - "heartRate": 50, - "tracker": "Resting Heart Rate" - }, - { - "heartRate": 61, - "tracker": "Normal Heart Rate" - }, - { - "heartRate": 75, - "tracker": "Exertive Heart Rate" - } - ], - "heart": [ - { - "heartRate": 50, - "logId": 437197297, - "tracker": "Resting Heart Rate" - }, - { - "heartRate": 51, - "logId": 437186452, - "tracker": "Resting Heart Rate" - }, - { - "heartRate": 50, - "logId": 438204160, - "time": "03:30", - "tracker": "Resting Heart Rate" - }, - { - "heartRate": 50, - "logId": 458225625, - "time": "10:13", - "tracker": "Resting Heart Rate" - }, - { - "heartRate": 60, - "logId": 437197296, - "tracker": "Normal Heart Rate" - }, - { - "heartRate": 62, - "logId": 437186451, - "tracker": "Normal Heart Rate" - }, - { - "heartRate": 61, - "logId": 438204159, - "time": "05:50", - "tracker": "Normal Heart Rate" - }, - { - "heartRate": 61, - "logId": 458225624, - "time": "20:14", - "tracker": "Normal Heart Rate" - }, - { - "heartRate": 77, - "logId": 437197298, - "tracker": "Exertive Heart Rate" - }, - { - "heartRate": 73, - "logId": 437186453, - "tracker": "Exertive Heart Rate" - }, - { - "heartRate": 75, - "logId": 438204161, - "time": "07:23", - "tracker": "Exertive Heart Rate" - }, - { - "heartRate": 75, - "logId": 458225626, - "time": "15:30", - "tracker": "Exertive Heart Rate" - } - ] - } - } -} \ No newline at end of file diff --git a/shim-server/src/test/resources/fitbit-step-count-1m.json b/shim-server/src/test/resources/fitbit-step-count-1m.json deleted file mode 100644 index 76ebfd36..00000000 --- a/shim-server/src/test/resources/fitbit-step-count-1m.json +++ /dev/null @@ -1,9276 +0,0 @@ -[ - { - "result": { - "date": "2014-08-20", - "content": { - "activities-steps": [ - { - "dateTime": "2014-08-20", - "value": "4332" - } - ], - "activities-steps-intraday": { - "dataset": [ - { - "time": "00:00:00", - "value": 0 - }, - { - "time": "00:01:00", - "value": 0 - }, - { - "time": "00:02:00", - "value": 0 - }, - { - "time": "00:03:00", - "value": 0 - }, - { - "time": "00:04:00", - "value": 0 - }, - { - "time": "00:05:00", - "value": 0 - }, - { - "time": "00:06:00", - "value": 0 - }, - { - "time": "00:07:00", - "value": 0 - }, - { - "time": "00:08:00", - "value": 0 - }, - { - "time": "00:09:00", - "value": 0 - }, - { - "time": "00:10:00", - "value": 0 - }, - { - "time": "00:11:00", - "value": 0 - }, - { - "time": "00:12:00", - "value": 0 - }, - { - "time": "00:13:00", - "value": 0 - }, - { - "time": "00:14:00", - "value": 0 - }, - { - "time": "00:15:00", - "value": 0 - }, - { - "time": "00:16:00", - "value": 0 - }, - { - "time": "00:17:00", - "value": 0 - }, - { - "time": "00:18:00", - "value": 0 - }, - { - "time": "00:19:00", - "value": 0 - }, - { - "time": "00:20:00", - "value": 0 - }, - { - "time": "00:21:00", - "value": 0 - }, - { - "time": "00:22:00", - "value": 0 - }, - { - "time": "00:23:00", - "value": 0 - }, - { - "time": "00:24:00", - "value": 0 - }, - { - "time": "00:25:00", - "value": 0 - }, - { - "time": "00:26:00", - "value": 7 - }, - { - "time": "00:27:00", - "value": 0 - }, - { - "time": "00:28:00", - "value": 0 - }, - { - "time": "00:29:00", - "value": 0 - }, - { - "time": "00:30:00", - "value": 0 - }, - { - "time": "00:31:00", - "value": 0 - }, - { - "time": "00:32:00", - "value": 0 - }, - { - "time": "00:33:00", - "value": 0 - }, - { - "time": "00:34:00", - "value": 0 - }, - { - "time": "00:35:00", - "value": 0 - }, - { - "time": "00:36:00", - "value": 0 - }, - { - "time": "00:37:00", - "value": 0 - }, - { - "time": "00:38:00", - "value": 13 - }, - { - "time": "00:39:00", - "value": 17 - }, - { - "time": "00:40:00", - "value": 1 - }, - { - "time": "00:41:00", - "value": 0 - }, - { - "time": "00:42:00", - "value": 0 - }, - { - "time": "00:43:00", - "value": 0 - }, - { - "time": "00:44:00", - "value": 0 - }, - { - "time": "00:45:00", - "value": 20 - }, - { - "time": "00:46:00", - "value": 34 - }, - { - "time": "00:47:00", - "value": 22 - }, - { - "time": "00:48:00", - "value": 0 - }, - { - "time": "00:49:00", - "value": 0 - }, - { - "time": "00:50:00", - "value": 0 - }, - { - "time": "00:51:00", - "value": 0 - }, - { - "time": "00:52:00", - "value": 0 - }, - { - "time": "00:53:00", - "value": 0 - }, - { - "time": "00:54:00", - "value": 0 - }, - { - "time": "00:55:00", - "value": 0 - }, - { - "time": "00:56:00", - "value": 0 - }, - { - "time": "00:57:00", - "value": 0 - }, - { - "time": "00:58:00", - "value": 0 - }, - { - "time": "00:59:00", - "value": 0 - }, - { - "time": "01:00:00", - "value": 0 - }, - { - "time": "01:01:00", - "value": 0 - }, - { - "time": "01:02:00", - "value": 0 - }, - { - "time": "01:03:00", - "value": 0 - }, - { - "time": "01:04:00", - "value": 0 - }, - { - "time": "01:05:00", - "value": 0 - }, - { - "time": "01:06:00", - "value": 0 - }, - { - "time": "01:07:00", - "value": 0 - }, - { - "time": "01:08:00", - "value": 0 - }, - { - "time": "01:09:00", - "value": 0 - }, - { - "time": "01:10:00", - "value": 0 - }, - { - "time": "01:11:00", - "value": 0 - }, - { - "time": "01:12:00", - "value": 0 - }, - { - "time": "01:13:00", - "value": 0 - }, - { - "time": "01:14:00", - "value": 0 - }, - { - "time": "01:15:00", - "value": 0 - }, - { - "time": "01:16:00", - "value": 0 - }, - { - "time": "01:17:00", - "value": 0 - }, - { - "time": "01:18:00", - "value": 0 - }, - { - "time": "01:19:00", - "value": 0 - }, - { - "time": "01:20:00", - "value": 0 - }, - { - "time": "01:21:00", - "value": 0 - }, - { - "time": "01:22:00", - "value": 0 - }, - { - "time": "01:23:00", - "value": 0 - }, - { - "time": "01:24:00", - "value": 0 - }, - { - "time": "01:25:00", - "value": 0 - }, - { - "time": "01:26:00", - "value": 0 - }, - { - "time": "01:27:00", - "value": 0 - }, - { - "time": "01:28:00", - "value": 0 - }, - { - "time": "01:29:00", - "value": 0 - }, - { - "time": "01:30:00", - "value": 0 - }, - { - "time": "01:31:00", - "value": 0 - }, - { - "time": "01:32:00", - "value": 0 - }, - { - "time": "01:33:00", - "value": 0 - }, - { - "time": "01:34:00", - "value": 0 - }, - { - "time": "01:35:00", - "value": 0 - }, - { - "time": "01:36:00", - "value": 0 - }, - { - "time": "01:37:00", - "value": 0 - }, - { - "time": "01:38:00", - "value": 0 - }, - { - "time": "01:39:00", - "value": 0 - }, - { - "time": "01:40:00", - "value": 0 - }, - { - "time": "01:41:00", - "value": 0 - }, - { - "time": "01:42:00", - "value": 0 - }, - { - "time": "01:43:00", - "value": 0 - }, - { - "time": "01:44:00", - "value": 0 - }, - { - "time": "01:45:00", - "value": 0 - }, - { - "time": "01:46:00", - "value": 0 - }, - { - "time": "01:47:00", - "value": 0 - }, - { - "time": "01:48:00", - "value": 0 - }, - { - "time": "01:49:00", - "value": 0 - }, - { - "time": "01:50:00", - "value": 0 - }, - { - "time": "01:51:00", - "value": 0 - }, - { - "time": "01:52:00", - "value": 0 - }, - { - "time": "01:53:00", - "value": 0 - }, - { - "time": "01:54:00", - "value": 0 - }, - { - "time": "01:55:00", - "value": 0 - }, - { - "time": "01:56:00", - "value": 0 - }, - { - "time": "01:57:00", - "value": 0 - }, - { - "time": "01:58:00", - "value": 0 - }, - { - "time": "01:59:00", - "value": 0 - }, - { - "time": "02:00:00", - "value": 0 - }, - { - "time": "02:01:00", - "value": 0 - }, - { - "time": "02:02:00", - "value": 0 - }, - { - "time": "02:03:00", - "value": 0 - }, - { - "time": "02:04:00", - "value": 0 - }, - { - "time": "02:05:00", - "value": 0 - }, - { - "time": "02:06:00", - "value": 0 - }, - { - "time": "02:07:00", - "value": 0 - }, - { - "time": "02:08:00", - "value": 0 - }, - { - "time": "02:09:00", - "value": 0 - }, - { - "time": "02:10:00", - "value": 0 - }, - { - "time": "02:11:00", - "value": 0 - }, - { - "time": "02:12:00", - "value": 0 - }, - { - "time": "02:13:00", - "value": 0 - }, - { - "time": "02:14:00", - "value": 0 - }, - { - "time": "02:15:00", - "value": 0 - }, - { - "time": "02:16:00", - "value": 0 - }, - { - "time": "02:17:00", - "value": 12 - }, - { - "time": "02:18:00", - "value": 6 - }, - { - "time": "02:19:00", - "value": 35 - }, - { - "time": "02:20:00", - "value": 15 - }, - { - "time": "02:21:00", - "value": 0 - }, - { - "time": "02:22:00", - "value": 0 - }, - { - "time": "02:23:00", - "value": 0 - }, - { - "time": "02:24:00", - "value": 0 - }, - { - "time": "02:25:00", - "value": 0 - }, - { - "time": "02:26:00", - "value": 0 - }, - { - "time": "02:27:00", - "value": 0 - }, - { - "time": "02:28:00", - "value": 0 - }, - { - "time": "02:29:00", - "value": 0 - }, - { - "time": "02:30:00", - "value": 0 - }, - { - "time": "02:31:00", - "value": 0 - }, - { - "time": "02:32:00", - "value": 0 - }, - { - "time": "02:33:00", - "value": 0 - }, - { - "time": "02:34:00", - "value": 0 - }, - { - "time": "02:35:00", - "value": 0 - }, - { - "time": "02:36:00", - "value": 0 - }, - { - "time": "02:37:00", - "value": 0 - }, - { - "time": "02:38:00", - "value": 0 - }, - { - "time": "02:39:00", - "value": 0 - }, - { - "time": "02:40:00", - "value": 0 - }, - { - "time": "02:41:00", - "value": 0 - }, - { - "time": "02:42:00", - "value": 0 - }, - { - "time": "02:43:00", - "value": 0 - }, - { - "time": "02:44:00", - "value": 0 - }, - { - "time": "02:45:00", - "value": 0 - }, - { - "time": "02:46:00", - "value": 0 - }, - { - "time": "02:47:00", - "value": 0 - }, - { - "time": "02:48:00", - "value": 0 - }, - { - "time": "02:49:00", - "value": 0 - }, - { - "time": "02:50:00", - "value": 0 - }, - { - "time": "02:51:00", - "value": 0 - }, - { - "time": "02:52:00", - "value": 0 - }, - { - "time": "02:53:00", - "value": 0 - }, - { - "time": "02:54:00", - "value": 0 - }, - { - "time": "02:55:00", - "value": 0 - }, - { - "time": "02:56:00", - "value": 0 - }, - { - "time": "02:57:00", - "value": 0 - }, - { - "time": "02:58:00", - "value": 0 - }, - { - "time": "02:59:00", - "value": 0 - }, - { - "time": "03:00:00", - "value": 0 - }, - { - "time": "03:01:00", - "value": 0 - }, - { - "time": "03:02:00", - "value": 0 - }, - { - "time": "03:03:00", - "value": 0 - }, - { - "time": "03:04:00", - "value": 0 - }, - { - "time": "03:05:00", - "value": 0 - }, - { - "time": "03:06:00", - "value": 0 - }, - { - "time": "03:07:00", - "value": 0 - }, - { - "time": "03:08:00", - "value": 0 - }, - { - "time": "03:09:00", - "value": 0 - }, - { - "time": "03:10:00", - "value": 0 - }, - { - "time": "03:11:00", - "value": 0 - }, - { - "time": "03:12:00", - "value": 0 - }, - { - "time": "03:13:00", - "value": 0 - }, - { - "time": "03:14:00", - "value": 0 - }, - { - "time": "03:15:00", - "value": 0 - }, - { - "time": "03:16:00", - "value": 0 - }, - { - "time": "03:17:00", - "value": 0 - }, - { - "time": "03:18:00", - "value": 0 - }, - { - "time": "03:19:00", - "value": 0 - }, - { - "time": "03:20:00", - "value": 0 - }, - { - "time": "03:21:00", - "value": 0 - }, - { - "time": "03:22:00", - "value": 0 - }, - { - "time": "03:23:00", - "value": 0 - }, - { - "time": "03:24:00", - "value": 0 - }, - { - "time": "03:25:00", - "value": 0 - }, - { - "time": "03:26:00", - "value": 0 - }, - { - "time": "03:27:00", - "value": 0 - }, - { - "time": "03:28:00", - "value": 0 - }, - { - "time": "03:29:00", - "value": 0 - }, - { - "time": "03:30:00", - "value": 0 - }, - { - "time": "03:31:00", - "value": 0 - }, - { - "time": "03:32:00", - "value": 0 - }, - { - "time": "03:33:00", - "value": 0 - }, - { - "time": "03:34:00", - "value": 0 - }, - { - "time": "03:35:00", - "value": 0 - }, - { - "time": "03:36:00", - "value": 0 - }, - { - "time": "03:37:00", - "value": 0 - }, - { - "time": "03:38:00", - "value": 0 - }, - { - "time": "03:39:00", - "value": 0 - }, - { - "time": "03:40:00", - "value": 0 - }, - { - "time": "03:41:00", - "value": 0 - }, - { - "time": "03:42:00", - "value": 0 - }, - { - "time": "03:43:00", - "value": 0 - }, - { - "time": "03:44:00", - "value": 0 - }, - { - "time": "03:45:00", - "value": 0 - }, - { - "time": "03:46:00", - "value": 0 - }, - { - "time": "03:47:00", - "value": 0 - }, - { - "time": "03:48:00", - "value": 0 - }, - { - "time": "03:49:00", - "value": 0 - }, - { - "time": "03:50:00", - "value": 0 - }, - { - "time": "03:51:00", - "value": 0 - }, - { - "time": "03:52:00", - "value": 0 - }, - { - "time": "03:53:00", - "value": 0 - }, - { - "time": "03:54:00", - "value": 0 - }, - { - "time": "03:55:00", - "value": 0 - }, - { - "time": "03:56:00", - "value": 0 - }, - { - "time": "03:57:00", - "value": 0 - }, - { - "time": "03:58:00", - "value": 0 - }, - { - "time": "03:59:00", - "value": 0 - }, - { - "time": "04:00:00", - "value": 0 - }, - { - "time": "04:01:00", - "value": 0 - }, - { - "time": "04:02:00", - "value": 0 - }, - { - "time": "04:03:00", - "value": 0 - }, - { - "time": "04:04:00", - "value": 0 - }, - { - "time": "04:05:00", - "value": 0 - }, - { - "time": "04:06:00", - "value": 0 - }, - { - "time": "04:07:00", - "value": 0 - }, - { - "time": "04:08:00", - "value": 0 - }, - { - "time": "04:09:00", - "value": 0 - }, - { - "time": "04:10:00", - "value": 0 - }, - { - "time": "04:11:00", - "value": 0 - }, - { - "time": "04:12:00", - "value": 0 - }, - { - "time": "04:13:00", - "value": 0 - }, - { - "time": "04:14:00", - "value": 0 - }, - { - "time": "04:15:00", - "value": 0 - }, - { - "time": "04:16:00", - "value": 0 - }, - { - "time": "04:17:00", - "value": 0 - }, - { - "time": "04:18:00", - "value": 0 - }, - { - "time": "04:19:00", - "value": 0 - }, - { - "time": "04:20:00", - "value": 0 - }, - { - "time": "04:21:00", - "value": 0 - }, - { - "time": "04:22:00", - "value": 0 - }, - { - "time": "04:23:00", - "value": 0 - }, - { - "time": "04:24:00", - "value": 0 - }, - { - "time": "04:25:00", - "value": 0 - }, - { - "time": "04:26:00", - "value": 0 - }, - { - "time": "04:27:00", - "value": 0 - }, - { - "time": "04:28:00", - "value": 0 - }, - { - "time": "04:29:00", - "value": 0 - }, - { - "time": "04:30:00", - "value": 0 - }, - { - "time": "04:31:00", - "value": 0 - }, - { - "time": "04:32:00", - "value": 0 - }, - { - "time": "04:33:00", - "value": 0 - }, - { - "time": "04:34:00", - "value": 0 - }, - { - "time": "04:35:00", - "value": 0 - }, - { - "time": "04:36:00", - "value": 0 - }, - { - "time": "04:37:00", - "value": 0 - }, - { - "time": "04:38:00", - "value": 0 - }, - { - "time": "04:39:00", - "value": 0 - }, - { - "time": "04:40:00", - "value": 0 - }, - { - "time": "04:41:00", - "value": 0 - }, - { - "time": "04:42:00", - "value": 0 - }, - { - "time": "04:43:00", - "value": 0 - }, - { - "time": "04:44:00", - "value": 0 - }, - { - "time": "04:45:00", - "value": 0 - }, - { - "time": "04:46:00", - "value": 0 - }, - { - "time": "04:47:00", - "value": 0 - }, - { - "time": "04:48:00", - "value": 0 - }, - { - "time": "04:49:00", - "value": 0 - }, - { - "time": "04:50:00", - "value": 0 - }, - { - "time": "04:51:00", - "value": 0 - }, - { - "time": "04:52:00", - "value": 0 - }, - { - "time": "04:53:00", - "value": 0 - }, - { - "time": "04:54:00", - "value": 0 - }, - { - "time": "04:55:00", - "value": 0 - }, - { - "time": "04:56:00", - "value": 0 - }, - { - "time": "04:57:00", - "value": 0 - }, - { - "time": "04:58:00", - "value": 0 - }, - { - "time": "04:59:00", - "value": 0 - }, - { - "time": "05:00:00", - "value": 0 - }, - { - "time": "05:01:00", - "value": 0 - }, - { - "time": "05:02:00", - "value": 0 - }, - { - "time": "05:03:00", - "value": 0 - }, - { - "time": "05:04:00", - "value": 0 - }, - { - "time": "05:05:00", - "value": 0 - }, - { - "time": "05:06:00", - "value": 0 - }, - { - "time": "05:07:00", - "value": 0 - }, - { - "time": "05:08:00", - "value": 0 - }, - { - "time": "05:09:00", - "value": 0 - }, - { - "time": "05:10:00", - "value": 0 - }, - { - "time": "05:11:00", - "value": 0 - }, - { - "time": "05:12:00", - "value": 0 - }, - { - "time": "05:13:00", - "value": 0 - }, - { - "time": "05:14:00", - "value": 0 - }, - { - "time": "05:15:00", - "value": 0 - }, - { - "time": "05:16:00", - "value": 0 - }, - { - "time": "05:17:00", - "value": 0 - }, - { - "time": "05:18:00", - "value": 0 - }, - { - "time": "05:19:00", - "value": 0 - }, - { - "time": "05:20:00", - "value": 0 - }, - { - "time": "05:21:00", - "value": 0 - }, - { - "time": "05:22:00", - "value": 0 - }, - { - "time": "05:23:00", - "value": 0 - }, - { - "time": "05:24:00", - "value": 0 - }, - { - "time": "05:25:00", - "value": 0 - }, - { - "time": "05:26:00", - "value": 0 - }, - { - "time": "05:27:00", - "value": 0 - }, - { - "time": "05:28:00", - "value": 0 - }, - { - "time": "05:29:00", - "value": 0 - }, - { - "time": "05:30:00", - "value": 0 - }, - { - "time": "05:31:00", - "value": 0 - }, - { - "time": "05:32:00", - "value": 0 - }, - { - "time": "05:33:00", - "value": 0 - }, - { - "time": "05:34:00", - "value": 0 - }, - { - "time": "05:35:00", - "value": 0 - }, - { - "time": "05:36:00", - "value": 0 - }, - { - "time": "05:37:00", - "value": 0 - }, - { - "time": "05:38:00", - "value": 0 - }, - { - "time": "05:39:00", - "value": 0 - }, - { - "time": "05:40:00", - "value": 0 - }, - { - "time": "05:41:00", - "value": 0 - }, - { - "time": "05:42:00", - "value": 0 - }, - { - "time": "05:43:00", - "value": 0 - }, - { - "time": "05:44:00", - "value": 0 - }, - { - "time": "05:45:00", - "value": 0 - }, - { - "time": "05:46:00", - "value": 0 - }, - { - "time": "05:47:00", - "value": 0 - }, - { - "time": "05:48:00", - "value": 0 - }, - { - "time": "05:49:00", - "value": 0 - }, - { - "time": "05:50:00", - "value": 0 - }, - { - "time": "05:51:00", - "value": 0 - }, - { - "time": "05:52:00", - "value": 0 - }, - { - "time": "05:53:00", - "value": 0 - }, - { - "time": "05:54:00", - "value": 0 - }, - { - "time": "05:55:00", - "value": 0 - }, - { - "time": "05:56:00", - "value": 0 - }, - { - "time": "05:57:00", - "value": 0 - }, - { - "time": "05:58:00", - "value": 0 - }, - { - "time": "05:59:00", - "value": 0 - }, - { - "time": "06:00:00", - "value": 0 - }, - { - "time": "06:01:00", - "value": 0 - }, - { - "time": "06:02:00", - "value": 0 - }, - { - "time": "06:03:00", - "value": 0 - }, - { - "time": "06:04:00", - "value": 0 - }, - { - "time": "06:05:00", - "value": 0 - }, - { - "time": "06:06:00", - "value": 0 - }, - { - "time": "06:07:00", - "value": 0 - }, - { - "time": "06:08:00", - "value": 0 - }, - { - "time": "06:09:00", - "value": 0 - }, - { - "time": "06:10:00", - "value": 0 - }, - { - "time": "06:11:00", - "value": 0 - }, - { - "time": "06:12:00", - "value": 0 - }, - { - "time": "06:13:00", - "value": 0 - }, - { - "time": "06:14:00", - "value": 0 - }, - { - "time": "06:15:00", - "value": 0 - }, - { - "time": "06:16:00", - "value": 0 - }, - { - "time": "06:17:00", - "value": 0 - }, - { - "time": "06:18:00", - "value": 0 - }, - { - "time": "06:19:00", - "value": 0 - }, - { - "time": "06:20:00", - "value": 0 - }, - { - "time": "06:21:00", - "value": 0 - }, - { - "time": "06:22:00", - "value": 0 - }, - { - "time": "06:23:00", - "value": 0 - }, - { - "time": "06:24:00", - "value": 0 - }, - { - "time": "06:25:00", - "value": 0 - }, - { - "time": "06:26:00", - "value": 0 - }, - { - "time": "06:27:00", - "value": 0 - }, - { - "time": "06:28:00", - "value": 0 - }, - { - "time": "06:29:00", - "value": 0 - }, - { - "time": "06:30:00", - "value": 0 - }, - { - "time": "06:31:00", - "value": 0 - }, - { - "time": "06:32:00", - "value": 0 - }, - { - "time": "06:33:00", - "value": 0 - }, - { - "time": "06:34:00", - "value": 0 - }, - { - "time": "06:35:00", - "value": 0 - }, - { - "time": "06:36:00", - "value": 0 - }, - { - "time": "06:37:00", - "value": 0 - }, - { - "time": "06:38:00", - "value": 0 - }, - { - "time": "06:39:00", - "value": 0 - }, - { - "time": "06:40:00", - "value": 0 - }, - { - "time": "06:41:00", - "value": 0 - }, - { - "time": "06:42:00", - "value": 0 - }, - { - "time": "06:43:00", - "value": 0 - }, - { - "time": "06:44:00", - "value": 0 - }, - { - "time": "06:45:00", - "value": 0 - }, - { - "time": "06:46:00", - "value": 0 - }, - { - "time": "06:47:00", - "value": 0 - }, - { - "time": "06:48:00", - "value": 0 - }, - { - "time": "06:49:00", - "value": 0 - }, - { - "time": "06:50:00", - "value": 0 - }, - { - "time": "06:51:00", - "value": 0 - }, - { - "time": "06:52:00", - "value": 0 - }, - { - "time": "06:53:00", - "value": 0 - }, - { - "time": "06:54:00", - "value": 0 - }, - { - "time": "06:55:00", - "value": 0 - }, - { - "time": "06:56:00", - "value": 0 - }, - { - "time": "06:57:00", - "value": 0 - }, - { - "time": "06:58:00", - "value": 0 - }, - { - "time": "06:59:00", - "value": 0 - }, - { - "time": "07:00:00", - "value": 0 - }, - { - "time": "07:01:00", - "value": 0 - }, - { - "time": "07:02:00", - "value": 0 - }, - { - "time": "07:03:00", - "value": 0 - }, - { - "time": "07:04:00", - "value": 0 - }, - { - "time": "07:05:00", - "value": 0 - }, - { - "time": "07:06:00", - "value": 0 - }, - { - "time": "07:07:00", - "value": 0 - }, - { - "time": "07:08:00", - "value": 0 - }, - { - "time": "07:09:00", - "value": 0 - }, - { - "time": "07:10:00", - "value": 0 - }, - { - "time": "07:11:00", - "value": 0 - }, - { - "time": "07:12:00", - "value": 0 - }, - { - "time": "07:13:00", - "value": 0 - }, - { - "time": "07:14:00", - "value": 0 - }, - { - "time": "07:15:00", - "value": 0 - }, - { - "time": "07:16:00", - "value": 0 - }, - { - "time": "07:17:00", - "value": 0 - }, - { - "time": "07:18:00", - "value": 0 - }, - { - "time": "07:19:00", - "value": 0 - }, - { - "time": "07:20:00", - "value": 0 - }, - { - "time": "07:21:00", - "value": 0 - }, - { - "time": "07:22:00", - "value": 0 - }, - { - "time": "07:23:00", - "value": 0 - }, - { - "time": "07:24:00", - "value": 0 - }, - { - "time": "07:25:00", - "value": 0 - }, - { - "time": "07:26:00", - "value": 0 - }, - { - "time": "07:27:00", - "value": 0 - }, - { - "time": "07:28:00", - "value": 0 - }, - { - "time": "07:29:00", - "value": 0 - }, - { - "time": "07:30:00", - "value": 0 - }, - { - "time": "07:31:00", - "value": 0 - }, - { - "time": "07:32:00", - "value": 0 - }, - { - "time": "07:33:00", - "value": 0 - }, - { - "time": "07:34:00", - "value": 0 - }, - { - "time": "07:35:00", - "value": 0 - }, - { - "time": "07:36:00", - "value": 0 - }, - { - "time": "07:37:00", - "value": 0 - }, - { - "time": "07:38:00", - "value": 0 - }, - { - "time": "07:39:00", - "value": 0 - }, - { - "time": "07:40:00", - "value": 0 - }, - { - "time": "07:41:00", - "value": 0 - }, - { - "time": "07:42:00", - "value": 0 - }, - { - "time": "07:43:00", - "value": 0 - }, - { - "time": "07:44:00", - "value": 0 - }, - { - "time": "07:45:00", - "value": 0 - }, - { - "time": "07:46:00", - "value": 0 - }, - { - "time": "07:47:00", - "value": 0 - }, - { - "time": "07:48:00", - "value": 0 - }, - { - "time": "07:49:00", - "value": 0 - }, - { - "time": "07:50:00", - "value": 0 - }, - { - "time": "07:51:00", - "value": 0 - }, - { - "time": "07:52:00", - "value": 0 - }, - { - "time": "07:53:00", - "value": 0 - }, - { - "time": "07:54:00", - "value": 0 - }, - { - "time": "07:55:00", - "value": 0 - }, - { - "time": "07:56:00", - "value": 0 - }, - { - "time": "07:57:00", - "value": 0 - }, - { - "time": "07:58:00", - "value": 0 - }, - { - "time": "07:59:00", - "value": 0 - }, - { - "time": "08:00:00", - "value": 0 - }, - { - "time": "08:01:00", - "value": 0 - }, - { - "time": "08:02:00", - "value": 0 - }, - { - "time": "08:03:00", - "value": 0 - }, - { - "time": "08:04:00", - "value": 0 - }, - { - "time": "08:05:00", - "value": 0 - }, - { - "time": "08:06:00", - "value": 0 - }, - { - "time": "08:07:00", - "value": 0 - }, - { - "time": "08:08:00", - "value": 0 - }, - { - "time": "08:09:00", - "value": 0 - }, - { - "time": "08:10:00", - "value": 0 - }, - { - "time": "08:11:00", - "value": 0 - }, - { - "time": "08:12:00", - "value": 0 - }, - { - "time": "08:13:00", - "value": 0 - }, - { - "time": "08:14:00", - "value": 0 - }, - { - "time": "08:15:00", - "value": 0 - }, - { - "time": "08:16:00", - "value": 0 - }, - { - "time": "08:17:00", - "value": 0 - }, - { - "time": "08:18:00", - "value": 0 - }, - { - "time": "08:19:00", - "value": 0 - }, - { - "time": "08:20:00", - "value": 0 - }, - { - "time": "08:21:00", - "value": 0 - }, - { - "time": "08:22:00", - "value": 0 - }, - { - "time": "08:23:00", - "value": 0 - }, - { - "time": "08:24:00", - "value": 0 - }, - { - "time": "08:25:00", - "value": 0 - }, - { - "time": "08:26:00", - "value": 0 - }, - { - "time": "08:27:00", - "value": 0 - }, - { - "time": "08:28:00", - "value": 0 - }, - { - "time": "08:29:00", - "value": 0 - }, - { - "time": "08:30:00", - "value": 0 - }, - { - "time": "08:31:00", - "value": 0 - }, - { - "time": "08:32:00", - "value": 0 - }, - { - "time": "08:33:00", - "value": 0 - }, - { - "time": "08:34:00", - "value": 0 - }, - { - "time": "08:35:00", - "value": 0 - }, - { - "time": "08:36:00", - "value": 0 - }, - { - "time": "08:37:00", - "value": 0 - }, - { - "time": "08:38:00", - "value": 0 - }, - { - "time": "08:39:00", - "value": 0 - }, - { - "time": "08:40:00", - "value": 0 - }, - { - "time": "08:41:00", - "value": 0 - }, - { - "time": "08:42:00", - "value": 0 - }, - { - "time": "08:43:00", - "value": 0 - }, - { - "time": "08:44:00", - "value": 0 - }, - { - "time": "08:45:00", - "value": 0 - }, - { - "time": "08:46:00", - "value": 0 - }, - { - "time": "08:47:00", - "value": 0 - }, - { - "time": "08:48:00", - "value": 0 - }, - { - "time": "08:49:00", - "value": 0 - }, - { - "time": "08:50:00", - "value": 0 - }, - { - "time": "08:51:00", - "value": 0 - }, - { - "time": "08:52:00", - "value": 0 - }, - { - "time": "08:53:00", - "value": 0 - }, - { - "time": "08:54:00", - "value": 0 - }, - { - "time": "08:55:00", - "value": 0 - }, - { - "time": "08:56:00", - "value": 0 - }, - { - "time": "08:57:00", - "value": 0 - }, - { - "time": "08:58:00", - "value": 0 - }, - { - "time": "08:59:00", - "value": 0 - }, - { - "time": "09:00:00", - "value": 0 - }, - { - "time": "09:01:00", - "value": 0 - }, - { - "time": "09:02:00", - "value": 0 - }, - { - "time": "09:03:00", - "value": 0 - }, - { - "time": "09:04:00", - "value": 0 - }, - { - "time": "09:05:00", - "value": 0 - }, - { - "time": "09:06:00", - "value": 0 - }, - { - "time": "09:07:00", - "value": 0 - }, - { - "time": "09:08:00", - "value": 0 - }, - { - "time": "09:09:00", - "value": 0 - }, - { - "time": "09:10:00", - "value": 0 - }, - { - "time": "09:11:00", - "value": 0 - }, - { - "time": "09:12:00", - "value": 0 - }, - { - "time": "09:13:00", - "value": 0 - }, - { - "time": "09:14:00", - "value": 0 - }, - { - "time": "09:15:00", - "value": 0 - }, - { - "time": "09:16:00", - "value": 0 - }, - { - "time": "09:17:00", - "value": 0 - }, - { - "time": "09:18:00", - "value": 0 - }, - { - "time": "09:19:00", - "value": 0 - }, - { - "time": "09:20:00", - "value": 0 - }, - { - "time": "09:21:00", - "value": 0 - }, - { - "time": "09:22:00", - "value": 0 - }, - { - "time": "09:23:00", - "value": 0 - }, - { - "time": "09:24:00", - "value": 0 - }, - { - "time": "09:25:00", - "value": 0 - }, - { - "time": "09:26:00", - "value": 0 - }, - { - "time": "09:27:00", - "value": 0 - }, - { - "time": "09:28:00", - "value": 0 - }, - { - "time": "09:29:00", - "value": 0 - }, - { - "time": "09:30:00", - "value": 0 - }, - { - "time": "09:31:00", - "value": 0 - }, - { - "time": "09:32:00", - "value": 0 - }, - { - "time": "09:33:00", - "value": 0 - }, - { - "time": "09:34:00", - "value": 0 - }, - { - "time": "09:35:00", - "value": 0 - }, - { - "time": "09:36:00", - "value": 0 - }, - { - "time": "09:37:00", - "value": 0 - }, - { - "time": "09:38:00", - "value": 0 - }, - { - "time": "09:39:00", - "value": 0 - }, - { - "time": "09:40:00", - "value": 0 - }, - { - "time": "09:41:00", - "value": 0 - }, - { - "time": "09:42:00", - "value": 0 - }, - { - "time": "09:43:00", - "value": 0 - }, - { - "time": "09:44:00", - "value": 0 - }, - { - "time": "09:45:00", - "value": 0 - }, - { - "time": "09:46:00", - "value": 0 - }, - { - "time": "09:47:00", - "value": 0 - }, - { - "time": "09:48:00", - "value": 0 - }, - { - "time": "09:49:00", - "value": 0 - }, - { - "time": "09:50:00", - "value": 0 - }, - { - "time": "09:51:00", - "value": 0 - }, - { - "time": "09:52:00", - "value": 0 - }, - { - "time": "09:53:00", - "value": 0 - }, - { - "time": "09:54:00", - "value": 0 - }, - { - "time": "09:55:00", - "value": 0 - }, - { - "time": "09:56:00", - "value": 0 - }, - { - "time": "09:57:00", - "value": 0 - }, - { - "time": "09:58:00", - "value": 0 - }, - { - "time": "09:59:00", - "value": 0 - }, - { - "time": "10:00:00", - "value": 0 - }, - { - "time": "10:01:00", - "value": 0 - }, - { - "time": "10:02:00", - "value": 0 - }, - { - "time": "10:03:00", - "value": 0 - }, - { - "time": "10:04:00", - "value": 0 - }, - { - "time": "10:05:00", - "value": 0 - }, - { - "time": "10:06:00", - "value": 0 - }, - { - "time": "10:07:00", - "value": 0 - }, - { - "time": "10:08:00", - "value": 0 - }, - { - "time": "10:09:00", - "value": 0 - }, - { - "time": "10:10:00", - "value": 0 - }, - { - "time": "10:11:00", - "value": 0 - }, - { - "time": "10:12:00", - "value": 0 - }, - { - "time": "10:13:00", - "value": 0 - }, - { - "time": "10:14:00", - "value": 0 - }, - { - "time": "10:15:00", - "value": 0 - }, - { - "time": "10:16:00", - "value": 0 - }, - { - "time": "10:17:00", - "value": 0 - }, - { - "time": "10:18:00", - "value": 0 - }, - { - "time": "10:19:00", - "value": 0 - }, - { - "time": "10:20:00", - "value": 0 - }, - { - "time": "10:21:00", - "value": 0 - }, - { - "time": "10:22:00", - "value": 0 - }, - { - "time": "10:23:00", - "value": 0 - }, - { - "time": "10:24:00", - "value": 0 - }, - { - "time": "10:25:00", - "value": 0 - }, - { - "time": "10:26:00", - "value": 0 - }, - { - "time": "10:27:00", - "value": 0 - }, - { - "time": "10:28:00", - "value": 0 - }, - { - "time": "10:29:00", - "value": 0 - }, - { - "time": "10:30:00", - "value": 0 - }, - { - "time": "10:31:00", - "value": 0 - }, - { - "time": "10:32:00", - "value": 0 - }, - { - "time": "10:33:00", - "value": 0 - }, - { - "time": "10:34:00", - "value": 0 - }, - { - "time": "10:35:00", - "value": 0 - }, - { - "time": "10:36:00", - "value": 0 - }, - { - "time": "10:37:00", - "value": 0 - }, - { - "time": "10:38:00", - "value": 0 - }, - { - "time": "10:39:00", - "value": 0 - }, - { - "time": "10:40:00", - "value": 0 - }, - { - "time": "10:41:00", - "value": 0 - }, - { - "time": "10:42:00", - "value": 0 - }, - { - "time": "10:43:00", - "value": 0 - }, - { - "time": "10:44:00", - "value": 0 - }, - { - "time": "10:45:00", - "value": 0 - }, - { - "time": "10:46:00", - "value": 0 - }, - { - "time": "10:47:00", - "value": 0 - }, - { - "time": "10:48:00", - "value": 0 - }, - { - "time": "10:49:00", - "value": 0 - }, - { - "time": "10:50:00", - "value": 0 - }, - { - "time": "10:51:00", - "value": 0 - }, - { - "time": "10:52:00", - "value": 0 - }, - { - "time": "10:53:00", - "value": 0 - }, - { - "time": "10:54:00", - "value": 0 - }, - { - "time": "10:55:00", - "value": 0 - }, - { - "time": "10:56:00", - "value": 0 - }, - { - "time": "10:57:00", - "value": 0 - }, - { - "time": "10:58:00", - "value": 0 - }, - { - "time": "10:59:00", - "value": 0 - }, - { - "time": "11:00:00", - "value": 0 - }, - { - "time": "11:01:00", - "value": 13 - }, - { - "time": "11:02:00", - "value": 17 - }, - { - "time": "11:03:00", - "value": 7 - }, - { - "time": "11:04:00", - "value": 18 - }, - { - "time": "11:05:00", - "value": 0 - }, - { - "time": "11:06:00", - "value": 0 - }, - { - "time": "11:07:00", - "value": 0 - }, - { - "time": "11:08:00", - "value": 0 - }, - { - "time": "11:09:00", - "value": 0 - }, - { - "time": "11:10:00", - "value": 0 - }, - { - "time": "11:11:00", - "value": 0 - }, - { - "time": "11:12:00", - "value": 0 - }, - { - "time": "11:13:00", - "value": 21 - }, - { - "time": "11:14:00", - "value": 24 - }, - { - "time": "11:15:00", - "value": 8 - }, - { - "time": "11:16:00", - "value": 0 - }, - { - "time": "11:17:00", - "value": 0 - }, - { - "time": "11:18:00", - "value": 0 - }, - { - "time": "11:19:00", - "value": 0 - }, - { - "time": "11:20:00", - "value": 50 - }, - { - "time": "11:21:00", - "value": 27 - }, - { - "time": "11:22:00", - "value": 4 - }, - { - "time": "11:23:00", - "value": 0 - }, - { - "time": "11:24:00", - "value": 15 - }, - { - "time": "11:25:00", - "value": 6 - }, - { - "time": "11:26:00", - "value": 0 - }, - { - "time": "11:27:00", - "value": 20 - }, - { - "time": "11:28:00", - "value": 0 - }, - { - "time": "11:29:00", - "value": 44 - }, - { - "time": "11:30:00", - "value": 11 - }, - { - "time": "11:31:00", - "value": 0 - }, - { - "time": "11:32:00", - "value": 0 - }, - { - "time": "11:33:00", - "value": 0 - }, - { - "time": "11:34:00", - "value": 0 - }, - { - "time": "11:35:00", - "value": 0 - }, - { - "time": "11:36:00", - "value": 0 - }, - { - "time": "11:37:00", - "value": 0 - }, - { - "time": "11:38:00", - "value": 0 - }, - { - "time": "11:39:00", - "value": 0 - }, - { - "time": "11:40:00", - "value": 0 - }, - { - "time": "11:41:00", - "value": 0 - }, - { - "time": "11:42:00", - "value": 0 - }, - { - "time": "11:43:00", - "value": 0 - }, - { - "time": "11:44:00", - "value": 0 - }, - { - "time": "11:45:00", - "value": 0 - }, - { - "time": "11:46:00", - "value": 0 - }, - { - "time": "11:47:00", - "value": 0 - }, - { - "time": "11:48:00", - "value": 0 - }, - { - "time": "11:49:00", - "value": 0 - }, - { - "time": "11:50:00", - "value": 0 - }, - { - "time": "11:51:00", - "value": 0 - }, - { - "time": "11:52:00", - "value": 0 - }, - { - "time": "11:53:00", - "value": 0 - }, - { - "time": "11:54:00", - "value": 0 - }, - { - "time": "11:55:00", - "value": 0 - }, - { - "time": "11:56:00", - "value": 0 - }, - { - "time": "11:57:00", - "value": 0 - }, - { - "time": "11:58:00", - "value": 0 - }, - { - "time": "11:59:00", - "value": 0 - }, - { - "time": "12:00:00", - "value": 0 - }, - { - "time": "12:01:00", - "value": 0 - }, - { - "time": "12:02:00", - "value": 0 - }, - { - "time": "12:03:00", - "value": 0 - }, - { - "time": "12:04:00", - "value": 0 - }, - { - "time": "12:05:00", - "value": 0 - }, - { - "time": "12:06:00", - "value": 0 - }, - { - "time": "12:07:00", - "value": 0 - }, - { - "time": "12:08:00", - "value": 0 - }, - { - "time": "12:09:00", - "value": 0 - }, - { - "time": "12:10:00", - "value": 0 - }, - { - "time": "12:11:00", - "value": 0 - }, - { - "time": "12:12:00", - "value": 0 - }, - { - "time": "12:13:00", - "value": 0 - }, - { - "time": "12:14:00", - "value": 0 - }, - { - "time": "12:15:00", - "value": 0 - }, - { - "time": "12:16:00", - "value": 0 - }, - { - "time": "12:17:00", - "value": 0 - }, - { - "time": "12:18:00", - "value": 0 - }, - { - "time": "12:19:00", - "value": 0 - }, - { - "time": "12:20:00", - "value": 0 - }, - { - "time": "12:21:00", - "value": 0 - }, - { - "time": "12:22:00", - "value": 0 - }, - { - "time": "12:23:00", - "value": 0 - }, - { - "time": "12:24:00", - "value": 0 - }, - { - "time": "12:25:00", - "value": 0 - }, - { - "time": "12:26:00", - "value": 0 - }, - { - "time": "12:27:00", - "value": 0 - }, - { - "time": "12:28:00", - "value": 6 - }, - { - "time": "12:29:00", - "value": 10 - }, - { - "time": "12:30:00", - "value": 0 - }, - { - "time": "12:31:00", - "value": 0 - }, - { - "time": "12:32:00", - "value": 0 - }, - { - "time": "12:33:00", - "value": 11 - }, - { - "time": "12:34:00", - "value": 0 - }, - { - "time": "12:35:00", - "value": 0 - }, - { - "time": "12:36:00", - "value": 0 - }, - { - "time": "12:37:00", - "value": 0 - }, - { - "time": "12:38:00", - "value": 0 - }, - { - "time": "12:39:00", - "value": 0 - }, - { - "time": "12:40:00", - "value": 14 - }, - { - "time": "12:41:00", - "value": 0 - }, - { - "time": "12:42:00", - "value": 0 - }, - { - "time": "12:43:00", - "value": 15 - }, - { - "time": "12:44:00", - "value": 15 - }, - { - "time": "12:45:00", - "value": 0 - }, - { - "time": "12:46:00", - "value": 0 - }, - { - "time": "12:47:00", - "value": 0 - }, - { - "time": "12:48:00", - "value": 0 - }, - { - "time": "12:49:00", - "value": 0 - }, - { - "time": "12:50:00", - "value": 0 - }, - { - "time": "12:51:00", - "value": 0 - }, - { - "time": "12:52:00", - "value": 0 - }, - { - "time": "12:53:00", - "value": 0 - }, - { - "time": "12:54:00", - "value": 0 - }, - { - "time": "12:55:00", - "value": 0 - }, - { - "time": "12:56:00", - "value": 0 - }, - { - "time": "12:57:00", - "value": 0 - }, - { - "time": "12:58:00", - "value": 42 - }, - { - "time": "12:59:00", - "value": 0 - }, - { - "time": "13:00:00", - "value": 50 - }, - { - "time": "13:01:00", - "value": 49 - }, - { - "time": "13:02:00", - "value": 0 - }, - { - "time": "13:03:00", - "value": 0 - }, - { - "time": "13:04:00", - "value": 0 - }, - { - "time": "13:05:00", - "value": 0 - }, - { - "time": "13:06:00", - "value": 0 - }, - { - "time": "13:07:00", - "value": 17 - }, - { - "time": "13:08:00", - "value": 0 - }, - { - "time": "13:09:00", - "value": 0 - }, - { - "time": "13:10:00", - "value": 0 - }, - { - "time": "13:11:00", - "value": 0 - }, - { - "time": "13:12:00", - "value": 0 - }, - { - "time": "13:13:00", - "value": 0 - }, - { - "time": "13:14:00", - "value": 0 - }, - { - "time": "13:15:00", - "value": 0 - }, - { - "time": "13:16:00", - "value": 0 - }, - { - "time": "13:17:00", - "value": 18 - }, - { - "time": "13:18:00", - "value": 2 - }, - { - "time": "13:19:00", - "value": 21 - }, - { - "time": "13:20:00", - "value": 0 - }, - { - "time": "13:21:00", - "value": 0 - }, - { - "time": "13:22:00", - "value": 0 - }, - { - "time": "13:23:00", - "value": 0 - }, - { - "time": "13:24:00", - "value": 0 - }, - { - "time": "13:25:00", - "value": 0 - }, - { - "time": "13:26:00", - "value": 0 - }, - { - "time": "13:27:00", - "value": 0 - }, - { - "time": "13:28:00", - "value": 0 - }, - { - "time": "13:29:00", - "value": 0 - }, - { - "time": "13:30:00", - "value": 0 - }, - { - "time": "13:31:00", - "value": 0 - }, - { - "time": "13:32:00", - "value": 24 - }, - { - "time": "13:33:00", - "value": 23 - }, - { - "time": "13:34:00", - "value": 0 - }, - { - "time": "13:35:00", - "value": 0 - }, - { - "time": "13:36:00", - "value": 0 - }, - { - "time": "13:37:00", - "value": 0 - }, - { - "time": "13:38:00", - "value": 0 - }, - { - "time": "13:39:00", - "value": 0 - }, - { - "time": "13:40:00", - "value": 0 - }, - { - "time": "13:41:00", - "value": 12 - }, - { - "time": "13:42:00", - "value": 1 - }, - { - "time": "13:43:00", - "value": 4 - }, - { - "time": "13:44:00", - "value": 1 - }, - { - "time": "13:45:00", - "value": 0 - }, - { - "time": "13:46:00", - "value": 0 - }, - { - "time": "13:47:00", - "value": 0 - }, - { - "time": "13:48:00", - "value": 0 - }, - { - "time": "13:49:00", - "value": 0 - }, - { - "time": "13:50:00", - "value": 0 - }, - { - "time": "13:51:00", - "value": 0 - }, - { - "time": "13:52:00", - "value": 0 - }, - { - "time": "13:53:00", - "value": 0 - }, - { - "time": "13:54:00", - "value": 0 - }, - { - "time": "13:55:00", - "value": 0 - }, - { - "time": "13:56:00", - "value": 0 - }, - { - "time": "13:57:00", - "value": 0 - }, - { - "time": "13:58:00", - "value": 0 - }, - { - "time": "13:59:00", - "value": 0 - }, - { - "time": "14:00:00", - "value": 0 - }, - { - "time": "14:01:00", - "value": 0 - }, - { - "time": "14:02:00", - "value": 37 - }, - { - "time": "14:03:00", - "value": 0 - }, - { - "time": "14:04:00", - "value": 0 - }, - { - "time": "14:05:00", - "value": 29 - }, - { - "time": "14:06:00", - "value": 0 - }, - { - "time": "14:07:00", - "value": 0 - }, - { - "time": "14:08:00", - "value": 17 - }, - { - "time": "14:09:00", - "value": 0 - }, - { - "time": "14:10:00", - "value": 0 - }, - { - "time": "14:11:00", - "value": 0 - }, - { - "time": "14:12:00", - "value": 0 - }, - { - "time": "14:13:00", - "value": 0 - }, - { - "time": "14:14:00", - "value": 0 - }, - { - "time": "14:15:00", - "value": 0 - }, - { - "time": "14:16:00", - "value": 0 - }, - { - "time": "14:17:00", - "value": 0 - }, - { - "time": "14:18:00", - "value": 0 - }, - { - "time": "14:19:00", - "value": 18 - }, - { - "time": "14:20:00", - "value": 0 - }, - { - "time": "14:21:00", - "value": 0 - }, - { - "time": "14:22:00", - "value": 0 - }, - { - "time": "14:23:00", - "value": 0 - }, - { - "time": "14:24:00", - "value": 0 - }, - { - "time": "14:25:00", - "value": 9 - }, - { - "time": "14:26:00", - "value": 0 - }, - { - "time": "14:27:00", - "value": 0 - }, - { - "time": "14:28:00", - "value": 0 - }, - { - "time": "14:29:00", - "value": 0 - }, - { - "time": "14:30:00", - "value": 0 - }, - { - "time": "14:31:00", - "value": 0 - }, - { - "time": "14:32:00", - "value": 0 - }, - { - "time": "14:33:00", - "value": 0 - }, - { - "time": "14:34:00", - "value": 0 - }, - { - "time": "14:35:00", - "value": 0 - }, - { - "time": "14:36:00", - "value": 0 - }, - { - "time": "14:37:00", - "value": 0 - }, - { - "time": "14:38:00", - "value": 0 - }, - { - "time": "14:39:00", - "value": 0 - }, - { - "time": "14:40:00", - "value": 33 - }, - { - "time": "14:41:00", - "value": 33 - }, - { - "time": "14:42:00", - "value": 0 - }, - { - "time": "14:43:00", - "value": 20 - }, - { - "time": "14:44:00", - "value": 0 - }, - { - "time": "14:45:00", - "value": 18 - }, - { - "time": "14:46:00", - "value": 0 - }, - { - "time": "14:47:00", - "value": 0 - }, - { - "time": "14:48:00", - "value": 0 - }, - { - "time": "14:49:00", - "value": 0 - }, - { - "time": "14:50:00", - "value": 0 - }, - { - "time": "14:51:00", - "value": 0 - }, - { - "time": "14:52:00", - "value": 0 - }, - { - "time": "14:53:00", - "value": 7 - }, - { - "time": "14:54:00", - "value": 0 - }, - { - "time": "14:55:00", - "value": 0 - }, - { - "time": "14:56:00", - "value": 0 - }, - { - "time": "14:57:00", - "value": 0 - }, - { - "time": "14:58:00", - "value": 0 - }, - { - "time": "14:59:00", - "value": 7 - }, - { - "time": "15:00:00", - "value": 0 - }, - { - "time": "15:01:00", - "value": 0 - }, - { - "time": "15:02:00", - "value": 0 - }, - { - "time": "15:03:00", - "value": 7 - }, - { - "time": "15:04:00", - "value": 0 - }, - { - "time": "15:05:00", - "value": 0 - }, - { - "time": "15:06:00", - "value": 0 - }, - { - "time": "15:07:00", - "value": 0 - }, - { - "time": "15:08:00", - "value": 0 - }, - { - "time": "15:09:00", - "value": 0 - }, - { - "time": "15:10:00", - "value": 0 - }, - { - "time": "15:11:00", - "value": 0 - }, - { - "time": "15:12:00", - "value": 0 - }, - { - "time": "15:13:00", - "value": 0 - }, - { - "time": "15:14:00", - "value": 0 - }, - { - "time": "15:15:00", - "value": 0 - }, - { - "time": "15:16:00", - "value": 0 - }, - { - "time": "15:17:00", - "value": 0 - }, - { - "time": "15:18:00", - "value": 0 - }, - { - "time": "15:19:00", - "value": 0 - }, - { - "time": "15:20:00", - "value": 0 - }, - { - "time": "15:21:00", - "value": 0 - }, - { - "time": "15:22:00", - "value": 0 - }, - { - "time": "15:23:00", - "value": 0 - }, - { - "time": "15:24:00", - "value": 0 - }, - { - "time": "15:25:00", - "value": 0 - }, - { - "time": "15:26:00", - "value": 0 - }, - { - "time": "15:27:00", - "value": 0 - }, - { - "time": "15:28:00", - "value": 0 - }, - { - "time": "15:29:00", - "value": 0 - }, - { - "time": "15:30:00", - "value": 0 - }, - { - "time": "15:31:00", - "value": 0 - }, - { - "time": "15:32:00", - "value": 0 - }, - { - "time": "15:33:00", - "value": 0 - }, - { - "time": "15:34:00", - "value": 0 - }, - { - "time": "15:35:00", - "value": 0 - }, - { - "time": "15:36:00", - "value": 0 - }, - { - "time": "15:37:00", - "value": 0 - }, - { - "time": "15:38:00", - "value": 0 - }, - { - "time": "15:39:00", - "value": 0 - }, - { - "time": "15:40:00", - "value": 0 - }, - { - "time": "15:41:00", - "value": 0 - }, - { - "time": "15:42:00", - "value": 0 - }, - { - "time": "15:43:00", - "value": 0 - }, - { - "time": "15:44:00", - "value": 0 - }, - { - "time": "15:45:00", - "value": 0 - }, - { - "time": "15:46:00", - "value": 0 - }, - { - "time": "15:47:00", - "value": 0 - }, - { - "time": "15:48:00", - "value": 0 - }, - { - "time": "15:49:00", - "value": 9 - }, - { - "time": "15:50:00", - "value": 10 - }, - { - "time": "15:51:00", - "value": 0 - }, - { - "time": "15:52:00", - "value": 29 - }, - { - "time": "15:53:00", - "value": 29 - }, - { - "time": "15:54:00", - "value": 34 - }, - { - "time": "15:55:00", - "value": 21 - }, - { - "time": "15:56:00", - "value": 0 - }, - { - "time": "15:57:00", - "value": 0 - }, - { - "time": "15:58:00", - "value": 0 - }, - { - "time": "15:59:00", - "value": 7 - }, - { - "time": "16:00:00", - "value": 0 - }, - { - "time": "16:01:00", - "value": 0 - }, - { - "time": "16:02:00", - "value": 0 - }, - { - "time": "16:03:00", - "value": 17 - }, - { - "time": "16:04:00", - "value": 52 - }, - { - "time": "16:05:00", - "value": 18 - }, - { - "time": "16:06:00", - "value": 9 - }, - { - "time": "16:07:00", - "value": 2 - }, - { - "time": "16:08:00", - "value": 27 - }, - { - "time": "16:09:00", - "value": 21 - }, - { - "time": "16:10:00", - "value": 23 - }, - { - "time": "16:11:00", - "value": 7 - }, - { - "time": "16:12:00", - "value": 0 - }, - { - "time": "16:13:00", - "value": 29 - }, - { - "time": "16:14:00", - "value": 0 - }, - { - "time": "16:15:00", - "value": 19 - }, - { - "time": "16:16:00", - "value": 27 - }, - { - "time": "16:17:00", - "value": 0 - }, - { - "time": "16:18:00", - "value": 0 - }, - { - "time": "16:19:00", - "value": 7 - }, - { - "time": "16:20:00", - "value": 6 - }, - { - "time": "16:21:00", - "value": 12 - }, - { - "time": "16:22:00", - "value": 4 - }, - { - "time": "16:23:00", - "value": 14 - }, - { - "time": "16:24:00", - "value": 7 - }, - { - "time": "16:25:00", - "value": 0 - }, - { - "time": "16:26:00", - "value": 0 - }, - { - "time": "16:27:00", - "value": 0 - }, - { - "time": "16:28:00", - "value": 0 - }, - { - "time": "16:29:00", - "value": 0 - }, - { - "time": "16:30:00", - "value": 0 - }, - { - "time": "16:31:00", - "value": 0 - }, - { - "time": "16:32:00", - "value": 0 - }, - { - "time": "16:33:00", - "value": 0 - }, - { - "time": "16:34:00", - "value": 0 - }, - { - "time": "16:35:00", - "value": 0 - }, - { - "time": "16:36:00", - "value": 0 - }, - { - "time": "16:37:00", - "value": 10 - }, - { - "time": "16:38:00", - "value": 0 - }, - { - "time": "16:39:00", - "value": 0 - }, - { - "time": "16:40:00", - "value": 0 - }, - { - "time": "16:41:00", - "value": 0 - }, - { - "time": "16:42:00", - "value": 0 - }, - { - "time": "16:43:00", - "value": 6 - }, - { - "time": "16:44:00", - "value": 0 - }, - { - "time": "16:45:00", - "value": 20 - }, - { - "time": "16:46:00", - "value": 0 - }, - { - "time": "16:47:00", - "value": 0 - }, - { - "time": "16:48:00", - "value": 18 - }, - { - "time": "16:49:00", - "value": 15 - }, - { - "time": "16:50:00", - "value": 31 - }, - { - "time": "16:51:00", - "value": 0 - }, - { - "time": "16:52:00", - "value": 14 - }, - { - "time": "16:53:00", - "value": 0 - }, - { - "time": "16:54:00", - "value": 76 - }, - { - "time": "16:55:00", - "value": 0 - }, - { - "time": "16:56:00", - "value": 0 - }, - { - "time": "16:57:00", - "value": 0 - }, - { - "time": "16:58:00", - "value": 7 - }, - { - "time": "16:59:00", - "value": 0 - }, - { - "time": "17:00:00", - "value": 44 - }, - { - "time": "17:01:00", - "value": 0 - }, - { - "time": "17:02:00", - "value": 0 - }, - { - "time": "17:03:00", - "value": 10 - }, - { - "time": "17:04:00", - "value": 11 - }, - { - "time": "17:05:00", - "value": 0 - }, - { - "time": "17:06:00", - "value": 0 - }, - { - "time": "17:07:00", - "value": 0 - }, - { - "time": "17:08:00", - "value": 0 - }, - { - "time": "17:09:00", - "value": 0 - }, - { - "time": "17:10:00", - "value": 21 - }, - { - "time": "17:11:00", - "value": 0 - }, - { - "time": "17:12:00", - "value": 0 - }, - { - "time": "17:13:00", - "value": 0 - }, - { - "time": "17:14:00", - "value": 0 - }, - { - "time": "17:15:00", - "value": 0 - }, - { - "time": "17:16:00", - "value": 0 - }, - { - "time": "17:17:00", - "value": 0 - }, - { - "time": "17:18:00", - "value": 0 - }, - { - "time": "17:19:00", - "value": 18 - }, - { - "time": "17:20:00", - "value": 15 - }, - { - "time": "17:21:00", - "value": 10 - }, - { - "time": "17:22:00", - "value": 18 - }, - { - "time": "17:23:00", - "value": 23 - }, - { - "time": "17:24:00", - "value": 49 - }, - { - "time": "17:25:00", - "value": 32 - }, - { - "time": "17:26:00", - "value": 37 - }, - { - "time": "17:27:00", - "value": 0 - }, - { - "time": "17:28:00", - "value": 0 - }, - { - "time": "17:29:00", - "value": 0 - }, - { - "time": "17:30:00", - "value": 0 - }, - { - "time": "17:31:00", - "value": 0 - }, - { - "time": "17:32:00", - "value": 0 - }, - { - "time": "17:33:00", - "value": 0 - }, - { - "time": "17:34:00", - "value": 0 - }, - { - "time": "17:35:00", - "value": 0 - }, - { - "time": "17:36:00", - "value": 0 - }, - { - "time": "17:37:00", - "value": 0 - }, - { - "time": "17:38:00", - "value": 0 - }, - { - "time": "17:39:00", - "value": 0 - }, - { - "time": "17:40:00", - "value": 7 - }, - { - "time": "17:41:00", - "value": 13 - }, - { - "time": "17:42:00", - "value": 0 - }, - { - "time": "17:43:00", - "value": 0 - }, - { - "time": "17:44:00", - "value": 0 - }, - { - "time": "17:45:00", - "value": 0 - }, - { - "time": "17:46:00", - "value": 0 - }, - { - "time": "17:47:00", - "value": 0 - }, - { - "time": "17:48:00", - "value": 12 - }, - { - "time": "17:49:00", - "value": 7 - }, - { - "time": "17:50:00", - "value": 27 - }, - { - "time": "17:51:00", - "value": 11 - }, - { - "time": "17:52:00", - "value": 0 - }, - { - "time": "17:53:00", - "value": 0 - }, - { - "time": "17:54:00", - "value": 22 - }, - { - "time": "17:55:00", - "value": 8 - }, - { - "time": "17:56:00", - "value": 33 - }, - { - "time": "17:57:00", - "value": 27 - }, - { - "time": "17:58:00", - "value": 13 - }, - { - "time": "17:59:00", - "value": 0 - }, - { - "time": "18:00:00", - "value": 0 - }, - { - "time": "18:01:00", - "value": 0 - }, - { - "time": "18:02:00", - "value": 0 - }, - { - "time": "18:03:00", - "value": 0 - }, - { - "time": "18:04:00", - "value": 0 - }, - { - "time": "18:05:00", - "value": 0 - }, - { - "time": "18:06:00", - "value": 0 - }, - { - "time": "18:07:00", - "value": 0 - }, - { - "time": "18:08:00", - "value": 0 - }, - { - "time": "18:09:00", - "value": 0 - }, - { - "time": "18:10:00", - "value": 0 - }, - { - "time": "18:11:00", - "value": 0 - }, - { - "time": "18:12:00", - "value": 0 - }, - { - "time": "18:13:00", - "value": 0 - }, - { - "time": "18:14:00", - "value": 0 - }, - { - "time": "18:15:00", - "value": 0 - }, - { - "time": "18:16:00", - "value": 0 - }, - { - "time": "18:17:00", - "value": 0 - }, - { - "time": "18:18:00", - "value": 0 - }, - { - "time": "18:19:00", - "value": 0 - }, - { - "time": "18:20:00", - "value": 0 - }, - { - "time": "18:21:00", - "value": 0 - }, - { - "time": "18:22:00", - "value": 0 - }, - { - "time": "18:23:00", - "value": 0 - }, - { - "time": "18:24:00", - "value": 0 - }, - { - "time": "18:25:00", - "value": 0 - }, - { - "time": "18:26:00", - "value": 0 - }, - { - "time": "18:27:00", - "value": 0 - }, - { - "time": "18:28:00", - "value": 0 - }, - { - "time": "18:29:00", - "value": 0 - }, - { - "time": "18:30:00", - "value": 0 - }, - { - "time": "18:31:00", - "value": 0 - }, - { - "time": "18:32:00", - "value": 0 - }, - { - "time": "18:33:00", - "value": 0 - }, - { - "time": "18:34:00", - "value": 0 - }, - { - "time": "18:35:00", - "value": 0 - }, - { - "time": "18:36:00", - "value": 0 - }, - { - "time": "18:37:00", - "value": 0 - }, - { - "time": "18:38:00", - "value": 0 - }, - { - "time": "18:39:00", - "value": 0 - }, - { - "time": "18:40:00", - "value": 0 - }, - { - "time": "18:41:00", - "value": 0 - }, - { - "time": "18:42:00", - "value": 0 - }, - { - "time": "18:43:00", - "value": 0 - }, - { - "time": "18:44:00", - "value": 0 - }, - { - "time": "18:45:00", - "value": 0 - }, - { - "time": "18:46:00", - "value": 0 - }, - { - "time": "18:47:00", - "value": 0 - }, - { - "time": "18:48:00", - "value": 0 - }, - { - "time": "18:49:00", - "value": 0 - }, - { - "time": "18:50:00", - "value": 0 - }, - { - "time": "18:51:00", - "value": 0 - }, - { - "time": "18:52:00", - "value": 0 - }, - { - "time": "18:53:00", - "value": 28 - }, - { - "time": "18:54:00", - "value": 59 - }, - { - "time": "18:55:00", - "value": 70 - }, - { - "time": "18:56:00", - "value": 0 - }, - { - "time": "18:57:00", - "value": 0 - }, - { - "time": "18:58:00", - "value": 0 - }, - { - "time": "18:59:00", - "value": 0 - }, - { - "time": "19:00:00", - "value": 13 - }, - { - "time": "19:01:00", - "value": 12 - }, - { - "time": "19:02:00", - "value": 12 - }, - { - "time": "19:03:00", - "value": 0 - }, - { - "time": "19:04:00", - "value": 19 - }, - { - "time": "19:05:00", - "value": 0 - }, - { - "time": "19:06:00", - "value": 0 - }, - { - "time": "19:07:00", - "value": 0 - }, - { - "time": "19:08:00", - "value": 0 - }, - { - "time": "19:09:00", - "value": 0 - }, - { - "time": "19:10:00", - "value": 0 - }, - { - "time": "19:11:00", - "value": 0 - }, - { - "time": "19:12:00", - "value": 0 - }, - { - "time": "19:13:00", - "value": 20 - }, - { - "time": "19:14:00", - "value": 21 - }, - { - "time": "19:15:00", - "value": 0 - }, - { - "time": "19:16:00", - "value": 0 - }, - { - "time": "19:17:00", - "value": 0 - }, - { - "time": "19:18:00", - "value": 0 - }, - { - "time": "19:19:00", - "value": 0 - }, - { - "time": "19:20:00", - "value": 0 - }, - { - "time": "19:21:00", - "value": 0 - }, - { - "time": "19:22:00", - "value": 24 - }, - { - "time": "19:23:00", - "value": 0 - }, - { - "time": "19:24:00", - "value": 22 - }, - { - "time": "19:25:00", - "value": 0 - }, - { - "time": "19:26:00", - "value": 0 - }, - { - "time": "19:27:00", - "value": 27 - }, - { - "time": "19:28:00", - "value": 0 - }, - { - "time": "19:29:00", - "value": 0 - }, - { - "time": "19:30:00", - "value": 0 - }, - { - "time": "19:31:00", - "value": 0 - }, - { - "time": "19:32:00", - "value": 0 - }, - { - "time": "19:33:00", - "value": 0 - }, - { - "time": "19:34:00", - "value": 0 - }, - { - "time": "19:35:00", - "value": 0 - }, - { - "time": "19:36:00", - "value": 0 - }, - { - "time": "19:37:00", - "value": 0 - }, - { - "time": "19:38:00", - "value": 0 - }, - { - "time": "19:39:00", - "value": 0 - }, - { - "time": "19:40:00", - "value": 0 - }, - { - "time": "19:41:00", - "value": 0 - }, - { - "time": "19:42:00", - "value": 0 - }, - { - "time": "19:43:00", - "value": 0 - }, - { - "time": "19:44:00", - "value": 11 - }, - { - "time": "19:45:00", - "value": 29 - }, - { - "time": "19:46:00", - "value": 10 - }, - { - "time": "19:47:00", - "value": 0 - }, - { - "time": "19:48:00", - "value": 14 - }, - { - "time": "19:49:00", - "value": 20 - }, - { - "time": "19:50:00", - "value": 0 - }, - { - "time": "19:51:00", - "value": 0 - }, - { - "time": "19:52:00", - "value": 0 - }, - { - "time": "19:53:00", - "value": 0 - }, - { - "time": "19:54:00", - "value": 0 - }, - { - "time": "19:55:00", - "value": 0 - }, - { - "time": "19:56:00", - "value": 0 - }, - { - "time": "19:57:00", - "value": 0 - }, - { - "time": "19:58:00", - "value": 0 - }, - { - "time": "19:59:00", - "value": 0 - }, - { - "time": "20:00:00", - "value": 0 - }, - { - "time": "20:01:00", - "value": 0 - }, - { - "time": "20:02:00", - "value": 0 - }, - { - "time": "20:03:00", - "value": 0 - }, - { - "time": "20:04:00", - "value": 0 - }, - { - "time": "20:05:00", - "value": 0 - }, - { - "time": "20:06:00", - "value": 0 - }, - { - "time": "20:07:00", - "value": 0 - }, - { - "time": "20:08:00", - "value": 0 - }, - { - "time": "20:09:00", - "value": 0 - }, - { - "time": "20:10:00", - "value": 10 - }, - { - "time": "20:11:00", - "value": 9 - }, - { - "time": "20:12:00", - "value": 0 - }, - { - "time": "20:13:00", - "value": 0 - }, - { - "time": "20:14:00", - "value": 49 - }, - { - "time": "20:15:00", - "value": 49 - }, - { - "time": "20:16:00", - "value": 43 - }, - { - "time": "20:17:00", - "value": 33 - }, - { - "time": "20:18:00", - "value": 15 - }, - { - "time": "20:19:00", - "value": 0 - }, - { - "time": "20:20:00", - "value": 17 - }, - { - "time": "20:21:00", - "value": 32 - }, - { - "time": "20:22:00", - "value": 0 - }, - { - "time": "20:23:00", - "value": 76 - }, - { - "time": "20:24:00", - "value": 82 - }, - { - "time": "20:25:00", - "value": 58 - }, - { - "time": "20:26:00", - "value": 64 - }, - { - "time": "20:27:00", - "value": 63 - }, - { - "time": "20:28:00", - "value": 21 - }, - { - "time": "20:29:00", - "value": 0 - }, - { - "time": "20:30:00", - "value": 0 - }, - { - "time": "20:31:00", - "value": 0 - }, - { - "time": "20:32:00", - "value": 0 - }, - { - "time": "20:33:00", - "value": 9 - }, - { - "time": "20:34:00", - "value": 0 - }, - { - "time": "20:35:00", - "value": 78 - }, - { - "time": "20:36:00", - "value": 46 - }, - { - "time": "20:37:00", - "value": 44 - }, - { - "time": "20:38:00", - "value": 6 - }, - { - "time": "20:39:00", - "value": 32 - }, - { - "time": "20:40:00", - "value": 42 - }, - { - "time": "20:41:00", - "value": 31 - }, - { - "time": "20:42:00", - "value": 16 - }, - { - "time": "20:43:00", - "value": 0 - }, - { - "time": "20:44:00", - "value": 68 - }, - { - "time": "20:45:00", - "value": 69 - }, - { - "time": "20:46:00", - "value": 24 - }, - { - "time": "20:47:00", - "value": 0 - }, - { - "time": "20:48:00", - "value": 20 - }, - { - "time": "20:49:00", - "value": 0 - }, - { - "time": "20:50:00", - "value": 0 - }, - { - "time": "20:51:00", - "value": 0 - }, - { - "time": "20:52:00", - "value": 0 - }, - { - "time": "20:53:00", - "value": 0 - }, - { - "time": "20:54:00", - "value": 0 - }, - { - "time": "20:55:00", - "value": 0 - }, - { - "time": "20:56:00", - "value": 0 - }, - { - "time": "20:57:00", - "value": 0 - }, - { - "time": "20:58:00", - "value": 0 - }, - { - "time": "20:59:00", - "value": 0 - }, - { - "time": "21:00:00", - "value": 0 - }, - { - "time": "21:01:00", - "value": 0 - }, - { - "time": "21:02:00", - "value": 0 - }, - { - "time": "21:03:00", - "value": 0 - }, - { - "time": "21:04:00", - "value": 0 - }, - { - "time": "21:05:00", - "value": 0 - }, - { - "time": "21:06:00", - "value": 0 - }, - { - "time": "21:07:00", - "value": 0 - }, - { - "time": "21:08:00", - "value": 0 - }, - { - "time": "21:09:00", - "value": 0 - }, - { - "time": "21:10:00", - "value": 0 - }, - { - "time": "21:11:00", - "value": 0 - }, - { - "time": "21:12:00", - "value": 0 - }, - { - "time": "21:13:00", - "value": 0 - }, - { - "time": "21:14:00", - "value": 0 - }, - { - "time": "21:15:00", - "value": 0 - }, - { - "time": "21:16:00", - "value": 0 - }, - { - "time": "21:17:00", - "value": 0 - }, - { - "time": "21:18:00", - "value": 0 - }, - { - "time": "21:19:00", - "value": 0 - }, - { - "time": "21:20:00", - "value": 0 - }, - { - "time": "21:21:00", - "value": 0 - }, - { - "time": "21:22:00", - "value": 0 - }, - { - "time": "21:23:00", - "value": 0 - }, - { - "time": "21:24:00", - "value": 0 - }, - { - "time": "21:25:00", - "value": 0 - }, - { - "time": "21:26:00", - "value": 0 - }, - { - "time": "21:27:00", - "value": 0 - }, - { - "time": "21:28:00", - "value": 0 - }, - { - "time": "21:29:00", - "value": 0 - }, - { - "time": "21:30:00", - "value": 0 - }, - { - "time": "21:31:00", - "value": 0 - }, - { - "time": "21:32:00", - "value": 0 - }, - { - "time": "21:33:00", - "value": 0 - }, - { - "time": "21:34:00", - "value": 0 - }, - { - "time": "21:35:00", - "value": 0 - }, - { - "time": "21:36:00", - "value": 0 - }, - { - "time": "21:37:00", - "value": 26 - }, - { - "time": "21:38:00", - "value": 0 - }, - { - "time": "21:39:00", - "value": 16 - }, - { - "time": "21:40:00", - "value": 24 - }, - { - "time": "21:41:00", - "value": 44 - }, - { - "time": "21:42:00", - "value": 49 - }, - { - "time": "21:43:00", - "value": 0 - }, - { - "time": "21:44:00", - "value": 0 - }, - { - "time": "21:45:00", - "value": 0 - }, - { - "time": "21:46:00", - "value": 0 - }, - { - "time": "21:47:00", - "value": 0 - }, - { - "time": "21:48:00", - "value": 0 - }, - { - "time": "21:49:00", - "value": 0 - }, - { - "time": "21:50:00", - "value": 0 - }, - { - "time": "21:51:00", - "value": 0 - }, - { - "time": "21:52:00", - "value": 0 - }, - { - "time": "21:53:00", - "value": 0 - }, - { - "time": "21:54:00", - "value": 0 - }, - { - "time": "21:55:00", - "value": 0 - }, - { - "time": "21:56:00", - "value": 0 - }, - { - "time": "21:57:00", - "value": 0 - }, - { - "time": "21:58:00", - "value": 0 - }, - { - "time": "21:59:00", - "value": 0 - }, - { - "time": "22:00:00", - "value": 0 - }, - { - "time": "22:01:00", - "value": 0 - }, - { - "time": "22:02:00", - "value": 0 - }, - { - "time": "22:03:00", - "value": 0 - }, - { - "time": "22:04:00", - "value": 0 - }, - { - "time": "22:05:00", - "value": 0 - }, - { - "time": "22:06:00", - "value": 0 - }, - { - "time": "22:07:00", - "value": 0 - }, - { - "time": "22:08:00", - "value": 0 - }, - { - "time": "22:09:00", - "value": 13 - }, - { - "time": "22:10:00", - "value": 13 - }, - { - "time": "22:11:00", - "value": 0 - }, - { - "time": "22:12:00", - "value": 0 - }, - { - "time": "22:13:00", - "value": 0 - }, - { - "time": "22:14:00", - "value": 0 - }, - { - "time": "22:15:00", - "value": 0 - }, - { - "time": "22:16:00", - "value": 0 - }, - { - "time": "22:17:00", - "value": 0 - }, - { - "time": "22:18:00", - "value": 0 - }, - { - "time": "22:19:00", - "value": 0 - }, - { - "time": "22:20:00", - "value": 0 - }, - { - "time": "22:21:00", - "value": 0 - }, - { - "time": "22:22:00", - "value": 0 - }, - { - "time": "22:23:00", - "value": 10 - }, - { - "time": "22:24:00", - "value": 12 - }, - { - "time": "22:25:00", - "value": 9 - }, - { - "time": "22:26:00", - "value": 0 - }, - { - "time": "22:27:00", - "value": 0 - }, - { - "time": "22:28:00", - "value": 0 - }, - { - "time": "22:29:00", - "value": 0 - }, - { - "time": "22:30:00", - "value": 0 - }, - { - "time": "22:31:00", - "value": 0 - }, - { - "time": "22:32:00", - "value": 0 - }, - { - "time": "22:33:00", - "value": 0 - }, - { - "time": "22:34:00", - "value": 0 - }, - { - "time": "22:35:00", - "value": 0 - }, - { - "time": "22:36:00", - "value": 0 - }, - { - "time": "22:37:00", - "value": 0 - }, - { - "time": "22:38:00", - "value": 0 - }, - { - "time": "22:39:00", - "value": 0 - }, - { - "time": "22:40:00", - "value": 0 - }, - { - "time": "22:41:00", - "value": 0 - }, - { - "time": "22:42:00", - "value": 0 - }, - { - "time": "22:43:00", - "value": 0 - }, - { - "time": "22:44:00", - "value": 0 - }, - { - "time": "22:45:00", - "value": 0 - }, - { - "time": "22:46:00", - "value": 0 - }, - { - "time": "22:47:00", - "value": 0 - }, - { - "time": "22:48:00", - "value": 0 - }, - { - "time": "22:49:00", - "value": 0 - }, - { - "time": "22:50:00", - "value": 0 - }, - { - "time": "22:51:00", - "value": 0 - }, - { - "time": "22:52:00", - "value": 0 - }, - { - "time": "22:53:00", - "value": 0 - }, - { - "time": "22:54:00", - "value": 0 - }, - { - "time": "22:55:00", - "value": 0 - }, - { - "time": "22:56:00", - "value": 0 - }, - { - "time": "22:57:00", - "value": 0 - }, - { - "time": "22:58:00", - "value": 0 - }, - { - "time": "22:59:00", - "value": 0 - }, - { - "time": "23:00:00", - "value": 0 - }, - { - "time": "23:01:00", - "value": 0 - }, - { - "time": "23:02:00", - "value": 0 - }, - { - "time": "23:03:00", - "value": 0 - }, - { - "time": "23:04:00", - "value": 0 - }, - { - "time": "23:05:00", - "value": 0 - }, - { - "time": "23:06:00", - "value": 0 - }, - { - "time": "23:07:00", - "value": 0 - }, - { - "time": "23:08:00", - "value": 0 - }, - { - "time": "23:09:00", - "value": 0 - }, - { - "time": "23:10:00", - "value": 10 - }, - { - "time": "23:11:00", - "value": 0 - }, - { - "time": "23:12:00", - "value": 0 - }, - { - "time": "23:13:00", - "value": 30 - }, - { - "time": "23:14:00", - "value": 0 - }, - { - "time": "23:15:00", - "value": 0 - }, - { - "time": "23:16:00", - "value": 0 - }, - { - "time": "23:17:00", - "value": 46 - }, - { - "time": "23:18:00", - "value": 1 - }, - { - "time": "23:19:00", - "value": 50 - }, - { - "time": "23:20:00", - "value": 0 - }, - { - "time": "23:21:00", - "value": 0 - }, - { - "time": "23:22:00", - "value": 0 - }, - { - "time": "23:23:00", - "value": 55 - }, - { - "time": "23:24:00", - "value": 16 - }, - { - "time": "23:25:00", - "value": 0 - }, - { - "time": "23:26:00", - "value": 0 - }, - { - "time": "23:27:00", - "value": 0 - }, - { - "time": "23:28:00", - "value": 0 - }, - { - "time": "23:29:00", - "value": 0 - }, - { - "time": "23:30:00", - "value": 0 - }, - { - "time": "23:31:00", - "value": 0 - }, - { - "time": "23:32:00", - "value": 0 - }, - { - "time": "23:33:00", - "value": 0 - }, - { - "time": "23:34:00", - "value": 0 - }, - { - "time": "23:35:00", - "value": 0 - }, - { - "time": "23:36:00", - "value": 0 - }, - { - "time": "23:37:00", - "value": 25 - }, - { - "time": "23:38:00", - "value": 37 - }, - { - "time": "23:39:00", - "value": 0 - }, - { - "time": "23:40:00", - "value": 67 - }, - { - "time": "23:41:00", - "value": 13 - }, - { - "time": "23:42:00", - "value": 0 - }, - { - "time": "23:43:00", - "value": 0 - }, - { - "time": "23:44:00", - "value": 0 - }, - { - "time": "23:45:00", - "value": 52 - }, - { - "time": "23:46:00", - "value": 0 - }, - { - "time": "23:47:00", - "value": 0 - }, - { - "time": "23:48:00", - "value": 34 - }, - { - "time": "23:49:00", - "value": 21 - }, - { - "time": "23:50:00", - "value": 0 - }, - { - "time": "23:51:00", - "value": 0 - }, - { - "time": "23:52:00", - "value": 0 - }, - { - "time": "23:53:00", - "value": 0 - }, - { - "time": "23:54:00", - "value": 0 - }, - { - "time": "23:55:00", - "value": 0 - }, - { - "time": "23:56:00", - "value": 0 - }, - { - "time": "23:57:00", - "value": 0 - }, - { - "time": "23:58:00", - "value": 0 - }, - { - "time": "23:59:00", - "value": 0 - } - ], - "datasetInterval": 1, - "datasetType": "minute" - } - } - } - }, - { - "result": { - "date": "2014-08-21", - "content": { - "activities-steps": [ - { - "dateTime": "2014-08-21", - "value": "1944" - } - ], - "activities-steps-intraday": { - "dataset": [ - { - "time": "00:00:00", - "value": 0 - }, - { - "time": "00:01:00", - "value": 0 - }, - { - "time": "00:02:00", - "value": 0 - }, - { - "time": "00:03:00", - "value": 0 - }, - { - "time": "00:04:00", - "value": 0 - }, - { - "time": "00:05:00", - "value": 0 - }, - { - "time": "00:06:00", - "value": 0 - }, - { - "time": "00:07:00", - "value": 0 - }, - { - "time": "00:08:00", - "value": 0 - }, - { - "time": "00:09:00", - "value": 0 - }, - { - "time": "00:10:00", - "value": 0 - }, - { - "time": "00:11:00", - "value": 0 - }, - { - "time": "00:12:00", - "value": 0 - }, - { - "time": "00:13:00", - "value": 0 - }, - { - "time": "00:14:00", - "value": 0 - }, - { - "time": "00:15:00", - "value": 0 - }, - { - "time": "00:16:00", - "value": 0 - }, - { - "time": "00:17:00", - "value": 0 - }, - { - "time": "00:18:00", - "value": 0 - }, - { - "time": "00:19:00", - "value": 0 - }, - { - "time": "00:20:00", - "value": 0 - }, - { - "time": "00:21:00", - "value": 0 - }, - { - "time": "00:22:00", - "value": 0 - }, - { - "time": "00:23:00", - "value": 0 - }, - { - "time": "00:24:00", - "value": 0 - }, - { - "time": "00:25:00", - "value": 31 - }, - { - "time": "00:26:00", - "value": 36 - }, - { - "time": "00:27:00", - "value": 0 - }, - { - "time": "00:28:00", - "value": 16 - }, - { - "time": "00:29:00", - "value": 18 - }, - { - "time": "00:30:00", - "value": 22 - }, - { - "time": "00:31:00", - "value": 0 - }, - { - "time": "00:32:00", - "value": 0 - }, - { - "time": "00:33:00", - "value": 0 - }, - { - "time": "00:34:00", - "value": 25 - }, - { - "time": "00:35:00", - "value": 50 - }, - { - "time": "00:36:00", - "value": 2 - }, - { - "time": "00:37:00", - "value": 0 - }, - { - "time": "00:38:00", - "value": 12 - }, - { - "time": "00:39:00", - "value": 13 - }, - { - "time": "00:40:00", - "value": 0 - }, - { - "time": "00:41:00", - "value": 7 - }, - { - "time": "00:42:00", - "value": 0 - }, - { - "time": "00:43:00", - "value": 0 - }, - { - "time": "00:44:00", - "value": 38 - }, - { - "time": "00:45:00", - "value": 33 - }, - { - "time": "00:46:00", - "value": 0 - }, - { - "time": "00:47:00", - "value": 0 - }, - { - "time": "00:48:00", - "value": 0 - }, - { - "time": "00:49:00", - "value": 23 - }, - { - "time": "00:50:00", - "value": 0 - }, - { - "time": "00:51:00", - "value": 27 - }, - { - "time": "00:52:00", - "value": 0 - }, - { - "time": "00:53:00", - "value": 0 - }, - { - "time": "00:54:00", - "value": 0 - }, - { - "time": "00:55:00", - "value": 0 - }, - { - "time": "00:56:00", - "value": 0 - }, - { - "time": "00:57:00", - "value": 0 - }, - { - "time": "00:58:00", - "value": 0 - }, - { - "time": "00:59:00", - "value": 0 - }, - { - "time": "01:00:00", - "value": 0 - }, - { - "time": "01:01:00", - "value": 0 - }, - { - "time": "01:02:00", - "value": 0 - }, - { - "time": "01:03:00", - "value": 0 - }, - { - "time": "01:04:00", - "value": 0 - }, - { - "time": "01:05:00", - "value": 0 - }, - { - "time": "01:06:00", - "value": 0 - }, - { - "time": "01:07:00", - "value": 0 - }, - { - "time": "01:08:00", - "value": 0 - }, - { - "time": "01:09:00", - "value": 0 - }, - { - "time": "01:10:00", - "value": 0 - }, - { - "time": "01:11:00", - "value": 0 - }, - { - "time": "01:12:00", - "value": 22 - }, - { - "time": "01:13:00", - "value": 0 - }, - { - "time": "01:14:00", - "value": 7 - }, - { - "time": "01:15:00", - "value": 0 - }, - { - "time": "01:16:00", - "value": 0 - }, - { - "time": "01:17:00", - "value": 0 - }, - { - "time": "01:18:00", - "value": 14 - }, - { - "time": "01:19:00", - "value": 37 - }, - { - "time": "01:20:00", - "value": 0 - }, - { - "time": "01:21:00", - "value": 0 - }, - { - "time": "01:22:00", - "value": 0 - }, - { - "time": "01:23:00", - "value": 0 - }, - { - "time": "01:24:00", - "value": 7 - }, - { - "time": "01:25:00", - "value": 0 - }, - { - "time": "01:26:00", - "value": 17 - }, - { - "time": "01:27:00", - "value": 58 - }, - { - "time": "01:28:00", - "value": 0 - }, - { - "time": "01:29:00", - "value": 0 - }, - { - "time": "01:30:00", - "value": 8 - }, - { - "time": "01:31:00", - "value": 13 - }, - { - "time": "01:32:00", - "value": 0 - }, - { - "time": "01:33:00", - "value": 0 - }, - { - "time": "01:34:00", - "value": 0 - }, - { - "time": "01:35:00", - "value": 0 - }, - { - "time": "01:36:00", - "value": 0 - }, - { - "time": "01:37:00", - "value": 0 - }, - { - "time": "01:38:00", - "value": 0 - }, - { - "time": "01:39:00", - "value": 0 - }, - { - "time": "01:40:00", - "value": 0 - }, - { - "time": "01:41:00", - "value": 0 - }, - { - "time": "01:42:00", - "value": 0 - }, - { - "time": "01:43:00", - "value": 0 - }, - { - "time": "01:44:00", - "value": 0 - }, - { - "time": "01:45:00", - "value": 0 - }, - { - "time": "01:46:00", - "value": 0 - }, - { - "time": "01:47:00", - "value": 0 - }, - { - "time": "01:48:00", - "value": 0 - }, - { - "time": "01:49:00", - "value": 0 - }, - { - "time": "01:50:00", - "value": 0 - }, - { - "time": "01:51:00", - "value": 0 - }, - { - "time": "01:52:00", - "value": 0 - }, - { - "time": "01:53:00", - "value": 0 - }, - { - "time": "01:54:00", - "value": 0 - }, - { - "time": "01:55:00", - "value": 0 - }, - { - "time": "01:56:00", - "value": 0 - }, - { - "time": "01:57:00", - "value": 0 - }, - { - "time": "01:58:00", - "value": 0 - }, - { - "time": "01:59:00", - "value": 0 - }, - { - "time": "02:00:00", - "value": 0 - }, - { - "time": "02:01:00", - "value": 0 - }, - { - "time": "02:02:00", - "value": 0 - }, - { - "time": "02:03:00", - "value": 0 - }, - { - "time": "02:04:00", - "value": 0 - }, - { - "time": "02:05:00", - "value": 0 - }, - { - "time": "02:06:00", - "value": 0 - }, - { - "time": "02:07:00", - "value": 0 - }, - { - "time": "02:08:00", - "value": 0 - }, - { - "time": "02:09:00", - "value": 0 - }, - { - "time": "02:10:00", - "value": 0 - }, - { - "time": "02:11:00", - "value": 0 - }, - { - "time": "02:12:00", - "value": 0 - }, - { - "time": "02:13:00", - "value": 0 - }, - { - "time": "02:14:00", - "value": 0 - }, - { - "time": "02:15:00", - "value": 0 - }, - { - "time": "02:16:00", - "value": 0 - }, - { - "time": "02:17:00", - "value": 0 - }, - { - "time": "02:18:00", - "value": 0 - }, - { - "time": "02:19:00", - "value": 0 - }, - { - "time": "02:20:00", - "value": 0 - }, - { - "time": "02:21:00", - "value": 0 - }, - { - "time": "02:22:00", - "value": 0 - }, - { - "time": "02:23:00", - "value": 0 - }, - { - "time": "02:24:00", - "value": 0 - }, - { - "time": "02:25:00", - "value": 0 - }, - { - "time": "02:26:00", - "value": 0 - }, - { - "time": "02:27:00", - "value": 0 - }, - { - "time": "02:28:00", - "value": 0 - }, - { - "time": "02:29:00", - "value": 0 - }, - { - "time": "02:30:00", - "value": 0 - }, - { - "time": "02:31:00", - "value": 0 - }, - { - "time": "02:32:00", - "value": 0 - }, - { - "time": "02:33:00", - "value": 0 - }, - { - "time": "02:34:00", - "value": 0 - }, - { - "time": "02:35:00", - "value": 0 - }, - { - "time": "02:36:00", - "value": 0 - }, - { - "time": "02:37:00", - "value": 0 - }, - { - "time": "02:38:00", - "value": 0 - }, - { - "time": "02:39:00", - "value": 0 - }, - { - "time": "02:40:00", - "value": 0 - }, - { - "time": "02:41:00", - "value": 0 - }, - { - "time": "02:42:00", - "value": 0 - }, - { - "time": "02:43:00", - "value": 0 - }, - { - "time": "02:44:00", - "value": 0 - }, - { - "time": "02:45:00", - "value": 0 - }, - { - "time": "02:46:00", - "value": 0 - }, - { - "time": "02:47:00", - "value": 0 - }, - { - "time": "02:48:00", - "value": 0 - }, - { - "time": "02:49:00", - "value": 0 - }, - { - "time": "02:50:00", - "value": 0 - }, - { - "time": "02:51:00", - "value": 0 - }, - { - "time": "02:52:00", - "value": 0 - }, - { - "time": "02:53:00", - "value": 0 - }, - { - "time": "02:54:00", - "value": 0 - }, - { - "time": "02:55:00", - "value": 0 - }, - { - "time": "02:56:00", - "value": 0 - }, - { - "time": "02:57:00", - "value": 0 - }, - { - "time": "02:58:00", - "value": 0 - }, - { - "time": "02:59:00", - "value": 0 - }, - { - "time": "03:00:00", - "value": 0 - }, - { - "time": "03:01:00", - "value": 0 - }, - { - "time": "03:02:00", - "value": 0 - }, - { - "time": "03:03:00", - "value": 0 - }, - { - "time": "03:04:00", - "value": 0 - }, - { - "time": "03:05:00", - "value": 0 - }, - { - "time": "03:06:00", - "value": 0 - }, - { - "time": "03:07:00", - "value": 0 - }, - { - "time": "03:08:00", - "value": 0 - }, - { - "time": "03:09:00", - "value": 0 - }, - { - "time": "03:10:00", - "value": 0 - }, - { - "time": "03:11:00", - "value": 0 - }, - { - "time": "03:12:00", - "value": 0 - }, - { - "time": "03:13:00", - "value": 0 - }, - { - "time": "03:14:00", - "value": 0 - }, - { - "time": "03:15:00", - "value": 0 - }, - { - "time": "03:16:00", - "value": 0 - }, - { - "time": "03:17:00", - "value": 0 - }, - { - "time": "03:18:00", - "value": 0 - }, - { - "time": "03:19:00", - "value": 0 - }, - { - "time": "03:20:00", - "value": 0 - }, - { - "time": "03:21:00", - "value": 0 - }, - { - "time": "03:22:00", - "value": 0 - }, - { - "time": "03:23:00", - "value": 0 - }, - { - "time": "03:24:00", - "value": 0 - }, - { - "time": "03:25:00", - "value": 0 - }, - { - "time": "03:26:00", - "value": 0 - }, - { - "time": "03:27:00", - "value": 0 - }, - { - "time": "03:28:00", - "value": 0 - }, - { - "time": "03:29:00", - "value": 0 - }, - { - "time": "03:30:00", - "value": 0 - }, - { - "time": "03:31:00", - "value": 0 - }, - { - "time": "03:32:00", - "value": 0 - }, - { - "time": "03:33:00", - "value": 0 - }, - { - "time": "03:34:00", - "value": 0 - }, - { - "time": "03:35:00", - "value": 0 - }, - { - "time": "03:36:00", - "value": 0 - }, - { - "time": "03:37:00", - "value": 0 - }, - { - "time": "03:38:00", - "value": 0 - }, - { - "time": "03:39:00", - "value": 0 - }, - { - "time": "03:40:00", - "value": 0 - }, - { - "time": "03:41:00", - "value": 0 - }, - { - "time": "03:42:00", - "value": 0 - }, - { - "time": "03:43:00", - "value": 0 - }, - { - "time": "03:44:00", - "value": 0 - }, - { - "time": "03:45:00", - "value": 0 - }, - { - "time": "03:46:00", - "value": 0 - }, - { - "time": "03:47:00", - "value": 0 - }, - { - "time": "03:48:00", - "value": 0 - }, - { - "time": "03:49:00", - "value": 0 - }, - { - "time": "03:50:00", - "value": 0 - }, - { - "time": "03:51:00", - "value": 0 - }, - { - "time": "03:52:00", - "value": 0 - }, - { - "time": "03:53:00", - "value": 0 - }, - { - "time": "03:54:00", - "value": 0 - }, - { - "time": "03:55:00", - "value": 0 - }, - { - "time": "03:56:00", - "value": 0 - }, - { - "time": "03:57:00", - "value": 0 - }, - { - "time": "03:58:00", - "value": 0 - }, - { - "time": "03:59:00", - "value": 0 - }, - { - "time": "04:00:00", - "value": 0 - }, - { - "time": "04:01:00", - "value": 0 - }, - { - "time": "04:02:00", - "value": 0 - }, - { - "time": "04:03:00", - "value": 0 - }, - { - "time": "04:04:00", - "value": 0 - }, - { - "time": "04:05:00", - "value": 0 - }, - { - "time": "04:06:00", - "value": 0 - }, - { - "time": "04:07:00", - "value": 0 - }, - { - "time": "04:08:00", - "value": 0 - }, - { - "time": "04:09:00", - "value": 0 - }, - { - "time": "04:10:00", - "value": 0 - }, - { - "time": "04:11:00", - "value": 0 - }, - { - "time": "04:12:00", - "value": 0 - }, - { - "time": "04:13:00", - "value": 0 - }, - { - "time": "04:14:00", - "value": 0 - }, - { - "time": "04:15:00", - "value": 0 - }, - { - "time": "04:16:00", - "value": 0 - }, - { - "time": "04:17:00", - "value": 0 - }, - { - "time": "04:18:00", - "value": 0 - }, - { - "time": "04:19:00", - "value": 0 - }, - { - "time": "04:20:00", - "value": 0 - }, - { - "time": "04:21:00", - "value": 0 - }, - { - "time": "04:22:00", - "value": 0 - }, - { - "time": "04:23:00", - "value": 0 - }, - { - "time": "04:24:00", - "value": 0 - }, - { - "time": "04:25:00", - "value": 0 - }, - { - "time": "04:26:00", - "value": 0 - }, - { - "time": "04:27:00", - "value": 0 - }, - { - "time": "04:28:00", - "value": 0 - }, - { - "time": "04:29:00", - "value": 0 - }, - { - "time": "04:30:00", - "value": 0 - }, - { - "time": "04:31:00", - "value": 0 - }, - { - "time": "04:32:00", - "value": 0 - }, - { - "time": "04:33:00", - "value": 0 - }, - { - "time": "04:34:00", - "value": 0 - }, - { - "time": "04:35:00", - "value": 0 - }, - { - "time": "04:36:00", - "value": 0 - }, - { - "time": "04:37:00", - "value": 0 - }, - { - "time": "04:38:00", - "value": 0 - }, - { - "time": "04:39:00", - "value": 0 - }, - { - "time": "04:40:00", - "value": 0 - }, - { - "time": "04:41:00", - "value": 0 - }, - { - "time": "04:42:00", - "value": 0 - }, - { - "time": "04:43:00", - "value": 0 - }, - { - "time": "04:44:00", - "value": 0 - }, - { - "time": "04:45:00", - "value": 0 - }, - { - "time": "04:46:00", - "value": 0 - }, - { - "time": "04:47:00", - "value": 0 - }, - { - "time": "04:48:00", - "value": 0 - }, - { - "time": "04:49:00", - "value": 0 - }, - { - "time": "04:50:00", - "value": 0 - }, - { - "time": "04:51:00", - "value": 0 - }, - { - "time": "04:52:00", - "value": 0 - }, - { - "time": "04:53:00", - "value": 0 - }, - { - "time": "04:54:00", - "value": 0 - }, - { - "time": "04:55:00", - "value": 0 - }, - { - "time": "04:56:00", - "value": 0 - }, - { - "time": "04:57:00", - "value": 0 - }, - { - "time": "04:58:00", - "value": 0 - }, - { - "time": "04:59:00", - "value": 0 - }, - { - "time": "05:00:00", - "value": 0 - }, - { - "time": "05:01:00", - "value": 0 - }, - { - "time": "05:02:00", - "value": 0 - }, - { - "time": "05:03:00", - "value": 0 - }, - { - "time": "05:04:00", - "value": 0 - }, - { - "time": "05:05:00", - "value": 0 - }, - { - "time": "05:06:00", - "value": 0 - }, - { - "time": "05:07:00", - "value": 0 - }, - { - "time": "05:08:00", - "value": 0 - }, - { - "time": "05:09:00", - "value": 0 - }, - { - "time": "05:10:00", - "value": 0 - }, - { - "time": "05:11:00", - "value": 0 - }, - { - "time": "05:12:00", - "value": 0 - }, - { - "time": "05:13:00", - "value": 0 - }, - { - "time": "05:14:00", - "value": 0 - }, - { - "time": "05:15:00", - "value": 0 - }, - { - "time": "05:16:00", - "value": 0 - }, - { - "time": "05:17:00", - "value": 0 - }, - { - "time": "05:18:00", - "value": 0 - }, - { - "time": "05:19:00", - "value": 0 - }, - { - "time": "05:20:00", - "value": 0 - }, - { - "time": "05:21:00", - "value": 0 - }, - { - "time": "05:22:00", - "value": 0 - }, - { - "time": "05:23:00", - "value": 0 - }, - { - "time": "05:24:00", - "value": 0 - }, - { - "time": "05:25:00", - "value": 0 - }, - { - "time": "05:26:00", - "value": 0 - }, - { - "time": "05:27:00", - "value": 0 - }, - { - "time": "05:28:00", - "value": 0 - }, - { - "time": "05:29:00", - "value": 0 - }, - { - "time": "05:30:00", - "value": 0 - }, - { - "time": "05:31:00", - "value": 0 - }, - { - "time": "05:32:00", - "value": 0 - }, - { - "time": "05:33:00", - "value": 0 - }, - { - "time": "05:34:00", - "value": 0 - }, - { - "time": "05:35:00", - "value": 0 - }, - { - "time": "05:36:00", - "value": 0 - }, - { - "time": "05:37:00", - "value": 0 - }, - { - "time": "05:38:00", - "value": 0 - }, - { - "time": "05:39:00", - "value": 0 - }, - { - "time": "05:40:00", - "value": 0 - }, - { - "time": "05:41:00", - "value": 0 - }, - { - "time": "05:42:00", - "value": 0 - }, - { - "time": "05:43:00", - "value": 0 - }, - { - "time": "05:44:00", - "value": 0 - }, - { - "time": "05:45:00", - "value": 0 - }, - { - "time": "05:46:00", - "value": 0 - }, - { - "time": "05:47:00", - "value": 0 - }, - { - "time": "05:48:00", - "value": 0 - }, - { - "time": "05:49:00", - "value": 0 - }, - { - "time": "05:50:00", - "value": 0 - }, - { - "time": "05:51:00", - "value": 0 - }, - { - "time": "05:52:00", - "value": 0 - }, - { - "time": "05:53:00", - "value": 0 - }, - { - "time": "05:54:00", - "value": 0 - }, - { - "time": "05:55:00", - "value": 0 - }, - { - "time": "05:56:00", - "value": 0 - }, - { - "time": "05:57:00", - "value": 0 - }, - { - "time": "05:58:00", - "value": 0 - }, - { - "time": "05:59:00", - "value": 0 - }, - { - "time": "06:00:00", - "value": 0 - }, - { - "time": "06:01:00", - "value": 0 - }, - { - "time": "06:02:00", - "value": 0 - }, - { - "time": "06:03:00", - "value": 0 - }, - { - "time": "06:04:00", - "value": 0 - }, - { - "time": "06:05:00", - "value": 0 - }, - { - "time": "06:06:00", - "value": 0 - }, - { - "time": "06:07:00", - "value": 0 - }, - { - "time": "06:08:00", - "value": 0 - }, - { - "time": "06:09:00", - "value": 0 - }, - { - "time": "06:10:00", - "value": 0 - }, - { - "time": "06:11:00", - "value": 0 - }, - { - "time": "06:12:00", - "value": 0 - }, - { - "time": "06:13:00", - "value": 0 - }, - { - "time": "06:14:00", - "value": 0 - }, - { - "time": "06:15:00", - "value": 0 - }, - { - "time": "06:16:00", - "value": 0 - }, - { - "time": "06:17:00", - "value": 0 - }, - { - "time": "06:18:00", - "value": 0 - }, - { - "time": "06:19:00", - "value": 0 - }, - { - "time": "06:20:00", - "value": 0 - }, - { - "time": "06:21:00", - "value": 0 - }, - { - "time": "06:22:00", - "value": 0 - }, - { - "time": "06:23:00", - "value": 0 - }, - { - "time": "06:24:00", - "value": 0 - }, - { - "time": "06:25:00", - "value": 8 - }, - { - "time": "06:26:00", - "value": 37 - }, - { - "time": "06:27:00", - "value": 0 - }, - { - "time": "06:28:00", - "value": 0 - }, - { - "time": "06:29:00", - "value": 20 - }, - { - "time": "06:30:00", - "value": 0 - }, - { - "time": "06:31:00", - "value": 0 - }, - { - "time": "06:32:00", - "value": 18 - }, - { - "time": "06:33:00", - "value": 0 - }, - { - "time": "06:34:00", - "value": 17 - }, - { - "time": "06:35:00", - "value": 13 - }, - { - "time": "06:36:00", - "value": 0 - }, - { - "time": "06:37:00", - "value": 8 - }, - { - "time": "06:38:00", - "value": 0 - }, - { - "time": "06:39:00", - "value": 27 - }, - { - "time": "06:40:00", - "value": 0 - }, - { - "time": "06:41:00", - "value": 0 - }, - { - "time": "06:42:00", - "value": 0 - }, - { - "time": "06:43:00", - "value": 0 - }, - { - "time": "06:44:00", - "value": 17 - }, - { - "time": "06:45:00", - "value": 0 - }, - { - "time": "06:46:00", - "value": 0 - }, - { - "time": "06:47:00", - "value": 0 - }, - { - "time": "06:48:00", - "value": 0 - }, - { - "time": "06:49:00", - "value": 0 - }, - { - "time": "06:50:00", - "value": 0 - }, - { - "time": "06:51:00", - "value": 0 - }, - { - "time": "06:52:00", - "value": 0 - }, - { - "time": "06:53:00", - "value": 0 - }, - { - "time": "06:54:00", - "value": 0 - }, - { - "time": "06:55:00", - "value": 24 - }, - { - "time": "06:56:00", - "value": 18 - }, - { - "time": "06:57:00", - "value": 0 - }, - { - "time": "06:58:00", - "value": 0 - }, - { - "time": "06:59:00", - "value": 15 - }, - { - "time": "07:00:00", - "value": 0 - }, - { - "time": "07:01:00", - "value": 15 - }, - { - "time": "07:02:00", - "value": 0 - }, - { - "time": "07:03:00", - "value": 0 - }, - { - "time": "07:04:00", - "value": 0 - }, - { - "time": "07:05:00", - "value": 0 - }, - { - "time": "07:06:00", - "value": 0 - }, - { - "time": "07:07:00", - "value": 0 - }, - { - "time": "07:08:00", - "value": 17 - }, - { - "time": "07:09:00", - "value": 0 - }, - { - "time": "07:10:00", - "value": 0 - }, - { - "time": "07:11:00", - "value": 0 - }, - { - "time": "07:12:00", - "value": 0 - }, - { - "time": "07:13:00", - "value": 0 - }, - { - "time": "07:14:00", - "value": 0 - }, - { - "time": "07:15:00", - "value": 0 - }, - { - "time": "07:16:00", - "value": 0 - }, - { - "time": "07:17:00", - "value": 34 - }, - { - "time": "07:18:00", - "value": 12 - }, - { - "time": "07:19:00", - "value": 20 - }, - { - "time": "07:20:00", - "value": 2 - }, - { - "time": "07:21:00", - "value": 0 - }, - { - "time": "07:22:00", - "value": 9 - }, - { - "time": "07:23:00", - "value": 1 - }, - { - "time": "07:24:00", - "value": 12 - }, - { - "time": "07:25:00", - "value": 45 - }, - { - "time": "07:26:00", - "value": 0 - }, - { - "time": "07:27:00", - "value": 0 - }, - { - "time": "07:28:00", - "value": 34 - }, - { - "time": "07:29:00", - "value": 23 - }, - { - "time": "07:30:00", - "value": 0 - }, - { - "time": "07:31:00", - "value": 0 - }, - { - "time": "07:32:00", - "value": 0 - }, - { - "time": "07:33:00", - "value": 0 - }, - { - "time": "07:34:00", - "value": 0 - }, - { - "time": "07:35:00", - "value": 21 - }, - { - "time": "07:36:00", - "value": 23 - }, - { - "time": "07:37:00", - "value": 0 - }, - { - "time": "07:38:00", - "value": 38 - }, - { - "time": "07:39:00", - "value": 0 - }, - { - "time": "07:40:00", - "value": 0 - }, - { - "time": "07:41:00", - "value": 16 - }, - { - "time": "07:42:00", - "value": 32 - }, - { - "time": "07:43:00", - "value": 9 - }, - { - "time": "07:44:00", - "value": 0 - }, - { - "time": "07:45:00", - "value": 7 - }, - { - "time": "07:46:00", - "value": 23 - }, - { - "time": "07:47:00", - "value": 0 - }, - { - "time": "07:48:00", - "value": 7 - }, - { - "time": "07:49:00", - "value": 0 - }, - { - "time": "07:50:00", - "value": 0 - }, - { - "time": "07:51:00", - "value": 0 - }, - { - "time": "07:52:00", - "value": 0 - }, - { - "time": "07:53:00", - "value": 0 - }, - { - "time": "07:54:00", - "value": 0 - }, - { - "time": "07:55:00", - "value": 0 - }, - { - "time": "07:56:00", - "value": 0 - }, - { - "time": "07:57:00", - "value": 0 - }, - { - "time": "07:58:00", - "value": 0 - }, - { - "time": "07:59:00", - "value": 0 - }, - { - "time": "08:00:00", - "value": 0 - }, - { - "time": "08:01:00", - "value": 0 - }, - { - "time": "08:02:00", - "value": 0 - }, - { - "time": "08:03:00", - "value": 0 - }, - { - "time": "08:04:00", - "value": 0 - }, - { - "time": "08:05:00", - "value": 0 - }, - { - "time": "08:06:00", - "value": 0 - }, - { - "time": "08:07:00", - "value": 0 - }, - { - "time": "08:08:00", - "value": 0 - }, - { - "time": "08:09:00", - "value": 0 - }, - { - "time": "08:10:00", - "value": 0 - }, - { - "time": "08:11:00", - "value": 0 - }, - { - "time": "08:12:00", - "value": 0 - }, - { - "time": "08:13:00", - "value": 0 - }, - { - "time": "08:14:00", - "value": 0 - }, - { - "time": "08:15:00", - "value": 0 - }, - { - "time": "08:16:00", - "value": 0 - }, - { - "time": "08:17:00", - "value": 0 - }, - { - "time": "08:18:00", - "value": 27 - }, - { - "time": "08:19:00", - "value": 28 - }, - { - "time": "08:20:00", - "value": 0 - }, - { - "time": "08:21:00", - "value": 0 - }, - { - "time": "08:22:00", - "value": 25 - }, - { - "time": "08:23:00", - "value": 59 - }, - { - "time": "08:24:00", - "value": 17 - }, - { - "time": "08:25:00", - "value": 59 - }, - { - "time": "08:26:00", - "value": 0 - }, - { - "time": "08:27:00", - "value": 0 - }, - { - "time": "08:28:00", - "value": 32 - }, - { - "time": "08:29:00", - "value": 0 - }, - { - "time": "08:30:00", - "value": 15 - }, - { - "time": "08:31:00", - "value": 15 - }, - { - "time": "08:32:00", - "value": 0 - }, - { - "time": "08:33:00", - "value": 16 - }, - { - "time": "08:34:00", - "value": 34 - }, - { - "time": "08:35:00", - "value": 49 - }, - { - "time": "08:36:00", - "value": 40 - }, - { - "time": "08:37:00", - "value": 37 - }, - { - "time": "08:38:00", - "value": 0 - }, - { - "time": "08:39:00", - "value": 0 - }, - { - "time": "08:40:00", - "value": 45 - }, - { - "time": "08:41:00", - "value": 0 - }, - { - "time": "08:42:00", - "value": 44 - }, - { - "time": "08:43:00", - "value": 66 - }, - { - "time": "08:44:00", - "value": 0 - }, - { - "time": "08:45:00", - "value": 0 - }, - { - "time": "08:46:00", - "value": 0 - }, - { - "time": "08:47:00", - "value": 0 - }, - { - "time": "08:48:00", - "value": 0 - }, - { - "time": "08:49:00", - "value": 0 - }, - { - "time": "08:50:00", - "value": 0 - }, - { - "time": "08:51:00", - "value": 0 - }, - { - "time": "08:52:00", - "value": 0 - }, - { - "time": "08:53:00", - "value": 0 - }, - { - "time": "08:54:00", - "value": 0 - }, - { - "time": "08:55:00", - "value": 0 - }, - { - "time": "08:56:00", - "value": 0 - }, - { - "time": "08:57:00", - "value": 0 - }, - { - "time": "08:58:00", - "value": 0 - }, - { - "time": "08:59:00", - "value": 0 - }, - { - "time": "09:00:00", - "value": 0 - }, - { - "time": "09:01:00", - "value": 0 - }, - { - "time": "09:02:00", - "value": 0 - }, - { - "time": "09:03:00", - "value": 0 - }, - { - "time": "09:04:00", - "value": 0 - }, - { - "time": "09:05:00", - "value": 0 - }, - { - "time": "09:06:00", - "value": 0 - }, - { - "time": "09:07:00", - "value": 0 - }, - { - "time": "09:08:00", - "value": 0 - }, - { - "time": "09:09:00", - "value": 0 - }, - { - "time": "09:10:00", - "value": 0 - }, - { - "time": "09:11:00", - "value": 0 - }, - { - "time": "09:12:00", - "value": 0 - }, - { - "time": "09:13:00", - "value": 0 - }, - { - "time": "09:14:00", - "value": 0 - }, - { - "time": "09:15:00", - "value": 0 - }, - { - "time": "09:16:00", - "value": 0 - }, - { - "time": "09:17:00", - "value": 0 - }, - { - "time": "09:18:00", - "value": 0 - }, - { - "time": "09:19:00", - "value": 0 - }, - { - "time": "09:20:00", - "value": 0 - }, - { - "time": "09:21:00", - "value": 0 - }, - { - "time": "09:22:00", - "value": 0 - }, - { - "time": "09:23:00", - "value": 0 - }, - { - "time": "09:24:00", - "value": 0 - }, - { - "time": "09:25:00", - "value": 0 - }, - { - "time": "09:26:00", - "value": 0 - }, - { - "time": "09:27:00", - "value": 0 - }, - { - "time": "09:28:00", - "value": 0 - }, - { - "time": "09:29:00", - "value": 0 - }, - { - "time": "09:30:00", - "value": 0 - }, - { - "time": "09:31:00", - "value": 0 - }, - { - "time": "09:32:00", - "value": 0 - }, - { - "time": "09:33:00", - "value": 0 - }, - { - "time": "09:34:00", - "value": 0 - }, - { - "time": "09:35:00", - "value": 0 - }, - { - "time": "09:36:00", - "value": 0 - }, - { - "time": "09:37:00", - "value": 0 - }, - { - "time": "09:38:00", - "value": 0 - }, - { - "time": "09:39:00", - "value": 0 - }, - { - "time": "09:40:00", - "value": 0 - }, - { - "time": "09:41:00", - "value": 0 - }, - { - "time": "09:42:00", - "value": 0 - }, - { - "time": "09:43:00", - "value": 0 - }, - { - "time": "09:44:00", - "value": 0 - }, - { - "time": "09:45:00", - "value": 0 - }, - { - "time": "09:46:00", - "value": 0 - }, - { - "time": "09:47:00", - "value": 0 - }, - { - "time": "09:48:00", - "value": 0 - }, - { - "time": "09:49:00", - "value": 0 - }, - { - "time": "09:50:00", - "value": 0 - }, - { - "time": "09:51:00", - "value": 0 - }, - { - "time": "09:52:00", - "value": 0 - }, - { - "time": "09:53:00", - "value": 0 - }, - { - "time": "09:54:00", - "value": 0 - }, - { - "time": "09:55:00", - "value": 0 - }, - { - "time": "09:56:00", - "value": 0 - }, - { - "time": "09:57:00", - "value": 0 - }, - { - "time": "09:58:00", - "value": 0 - }, - { - "time": "09:59:00", - "value": 0 - }, - { - "time": "10:00:00", - "value": 0 - }, - { - "time": "10:01:00", - "value": 0 - }, - { - "time": "10:02:00", - "value": 0 - }, - { - "time": "10:03:00", - "value": 0 - }, - { - "time": "10:04:00", - "value": 0 - }, - { - "time": "10:05:00", - "value": 0 - }, - { - "time": "10:06:00", - "value": 0 - }, - { - "time": "10:07:00", - "value": 0 - }, - { - "time": "10:08:00", - "value": 0 - }, - { - "time": "10:09:00", - "value": 0 - }, - { - "time": "10:10:00", - "value": 0 - }, - { - "time": "10:11:00", - "value": 0 - }, - { - "time": "10:12:00", - "value": 0 - }, - { - "time": "10:13:00", - "value": 0 - }, - { - "time": "10:14:00", - "value": 0 - }, - { - "time": "10:15:00", - "value": 0 - }, - { - "time": "10:16:00", - "value": 0 - }, - { - "time": "10:17:00", - "value": 0 - }, - { - "time": "10:18:00", - "value": 0 - }, - { - "time": "10:19:00", - "value": 0 - }, - { - "time": "10:20:00", - "value": 0 - }, - { - "time": "10:21:00", - "value": 0 - }, - { - "time": "10:22:00", - "value": 0 - }, - { - "time": "10:23:00", - "value": 0 - }, - { - "time": "10:24:00", - "value": 0 - }, - { - "time": "10:25:00", - "value": 29 - }, - { - "time": "10:26:00", - "value": 12 - }, - { - "time": "10:27:00", - "value": 0 - }, - { - "time": "10:28:00", - "value": 0 - }, - { - "time": "10:29:00", - "value": 0 - }, - { - "time": "10:30:00", - "value": 0 - }, - { - "time": "10:31:00", - "value": 37 - }, - { - "time": "10:32:00", - "value": 32 - }, - { - "time": "10:33:00", - "value": 0 - }, - { - "time": "10:34:00", - "value": 0 - }, - { - "time": "10:35:00", - "value": 0 - }, - { - "time": "10:36:00", - "value": 0 - }, - { - "time": "10:37:00", - "value": 0 - }, - { - "time": "10:38:00", - "value": 0 - }, - { - "time": "10:39:00", - "value": 0 - }, - { - "time": "10:40:00", - "value": 0 - }, - { - "time": "10:41:00", - "value": 0 - }, - { - "time": "10:42:00", - "value": 0 - }, - { - "time": "10:43:00", - "value": 0 - }, - { - "time": "10:44:00", - "value": 0 - }, - { - "time": "10:45:00", - "value": 0 - }, - { - "time": "10:46:00", - "value": 0 - }, - { - "time": "10:47:00", - "value": 0 - }, - { - "time": "10:48:00", - "value": 0 - }, - { - "time": "10:49:00", - "value": 0 - }, - { - "time": "10:50:00", - "value": 0 - }, - { - "time": "10:51:00", - "value": 0 - }, - { - "time": "10:52:00", - "value": 0 - }, - { - "time": "10:53:00", - "value": 0 - }, - { - "time": "10:54:00", - "value": 0 - }, - { - "time": "10:55:00", - "value": 0 - }, - { - "time": "10:56:00", - "value": 0 - }, - { - "time": "10:57:00", - "value": 0 - }, - { - "time": "10:58:00", - "value": 55 - }, - { - "time": "10:59:00", - "value": 7 - }, - { - "time": "11:00:00", - "value": 6 - }, - { - "time": "11:01:00", - "value": 0 - }, - { - "time": "11:02:00", - "value": 0 - }, - { - "time": "11:03:00", - "value": 0 - }, - { - "time": "11:04:00", - "value": 0 - }, - { - "time": "11:05:00", - "value": 0 - }, - { - "time": "11:06:00", - "value": 0 - }, - { - "time": "11:07:00", - "value": 0 - }, - { - "time": "11:08:00", - "value": 0 - }, - { - "time": "11:09:00", - "value": 0 - }, - { - "time": "11:10:00", - "value": 0 - }, - { - "time": "11:11:00", - "value": 0 - }, - { - "time": "11:12:00", - "value": 0 - }, - { - "time": "11:13:00", - "value": 0 - }, - { - "time": "11:14:00", - "value": 0 - }, - { - "time": "11:15:00", - "value": 0 - }, - { - "time": "11:16:00", - "value": 0 - }, - { - "time": "11:17:00", - "value": 0 - }, - { - "time": "11:18:00", - "value": 0 - }, - { - "time": "11:19:00", - "value": 0 - }, - { - "time": "11:20:00", - "value": 0 - }, - { - "time": "11:21:00", - "value": 0 - }, - { - "time": "11:22:00", - "value": 0 - }, - { - "time": "11:23:00", - "value": 0 - }, - { - "time": "11:24:00", - "value": 0 - }, - { - "time": "11:25:00", - "value": 0 - }, - { - "time": "11:26:00", - "value": 0 - }, - { - "time": "11:27:00", - "value": 0 - }, - { - "time": "11:28:00", - "value": 0 - }, - { - "time": "11:29:00", - "value": 0 - }, - { - "time": "11:30:00", - "value": 0 - }, - { - "time": "11:31:00", - "value": 0 - }, - { - "time": "11:32:00", - "value": 0 - }, - { - "time": "11:33:00", - "value": 0 - }, - { - "time": "11:34:00", - "value": 0 - }, - { - "time": "11:35:00", - "value": 0 - }, - { - "time": "11:36:00", - "value": 0 - }, - { - "time": "11:37:00", - "value": 0 - }, - { - "time": "11:38:00", - "value": 0 - }, - { - "time": "11:39:00", - "value": 0 - }, - { - "time": "11:40:00", - "value": 0 - }, - { - "time": "11:41:00", - "value": 0 - }, - { - "time": "11:42:00", - "value": 0 - }, - { - "time": "11:43:00", - "value": 0 - }, - { - "time": "11:44:00", - "value": 0 - }, - { - "time": "11:45:00", - "value": 0 - }, - { - "time": "11:46:00", - "value": 0 - }, - { - "time": "11:47:00", - "value": 0 - }, - { - "time": "11:48:00", - "value": 0 - }, - { - "time": "11:49:00", - "value": 0 - }, - { - "time": "11:50:00", - "value": 0 - }, - { - "time": "11:51:00", - "value": 0 - }, - { - "time": "11:52:00", - "value": 0 - }, - { - "time": "11:53:00", - "value": 0 - }, - { - "time": "11:54:00", - "value": 0 - }, - { - "time": "11:55:00", - "value": 0 - }, - { - "time": "11:56:00", - "value": 0 - }, - { - "time": "11:57:00", - "value": 0 - }, - { - "time": "11:58:00", - "value": 0 - }, - { - "time": "11:59:00", - "value": 0 - }, - { - "time": "12:00:00", - "value": 0 - }, - { - "time": "12:01:00", - "value": 0 - }, - { - "time": "12:02:00", - "value": 0 - }, - { - "time": "12:03:00", - "value": 0 - }, - { - "time": "12:04:00", - "value": 0 - }, - { - "time": "12:05:00", - "value": 0 - }, - { - "time": "12:06:00", - "value": 0 - }, - { - "time": "12:07:00", - "value": 0 - }, - { - "time": "12:08:00", - "value": 0 - }, - { - "time": "12:09:00", - "value": 0 - }, - { - "time": "12:10:00", - "value": 0 - }, - { - "time": "12:11:00", - "value": 0 - }, - { - "time": "12:12:00", - "value": 0 - }, - { - "time": "12:13:00", - "value": 0 - }, - { - "time": "12:14:00", - "value": 0 - }, - { - "time": "12:15:00", - "value": 0 - }, - { - "time": "12:16:00", - "value": 0 - }, - { - "time": "12:17:00", - "value": 0 - }, - { - "time": "12:18:00", - "value": 0 - }, - { - "time": "12:19:00", - "value": 0 - }, - { - "time": "12:20:00", - "value": 0 - }, - { - "time": "12:21:00", - "value": 0 - }, - { - "time": "12:22:00", - "value": 0 - }, - { - "time": "12:23:00", - "value": 0 - }, - { - "time": "12:24:00", - "value": 0 - }, - { - "time": "12:25:00", - "value": 0 - }, - { - "time": "12:26:00", - "value": 0 - }, - { - "time": "12:27:00", - "value": 0 - }, - { - "time": "12:28:00", - "value": 0 - }, - { - "time": "12:29:00", - "value": 0 - }, - { - "time": "12:30:00", - "value": 0 - }, - { - "time": "12:31:00", - "value": 0 - }, - { - "time": "12:32:00", - "value": 0 - }, - { - "time": "12:33:00", - "value": 0 - }, - { - "time": "12:34:00", - "value": 0 - }, - { - "time": "12:35:00", - "value": 0 - }, - { - "time": "12:36:00", - "value": 0 - }, - { - "time": "12:37:00", - "value": 0 - }, - { - "time": "12:38:00", - "value": 0 - }, - { - "time": "12:39:00", - "value": 0 - }, - { - "time": "12:40:00", - "value": 0 - }, - { - "time": "12:41:00", - "value": 0 - }, - { - "time": "12:42:00", - "value": 0 - }, - { - "time": "12:43:00", - "value": 0 - }, - { - "time": "12:44:00", - "value": 0 - }, - { - "time": "12:45:00", - "value": 0 - }, - { - "time": "12:46:00", - "value": 0 - }, - { - "time": "12:47:00", - "value": 0 - }, - { - "time": "12:48:00", - "value": 0 - }, - { - "time": "12:49:00", - "value": 0 - }, - { - "time": "12:50:00", - "value": 0 - }, - { - "time": "12:51:00", - "value": 0 - }, - { - "time": "12:52:00", - "value": 0 - }, - { - "time": "12:53:00", - "value": 0 - }, - { - "time": "12:54:00", - "value": 0 - }, - { - "time": "12:55:00", - "value": 0 - }, - { - "time": "12:56:00", - "value": 0 - }, - { - "time": "12:57:00", - "value": 0 - }, - { - "time": "12:58:00", - "value": 0 - }, - { - "time": "12:59:00", - "value": 0 - }, - { - "time": "13:00:00", - "value": 0 - }, - { - "time": "13:01:00", - "value": 0 - }, - { - "time": "13:02:00", - "value": 0 - }, - { - "time": "13:03:00", - "value": 0 - }, - { - "time": "13:04:00", - "value": 0 - }, - { - "time": "13:05:00", - "value": 0 - }, - { - "time": "13:06:00", - "value": 0 - }, - { - "time": "13:07:00", - "value": 0 - }, - { - "time": "13:08:00", - "value": 0 - }, - { - "time": "13:09:00", - "value": 0 - }, - { - "time": "13:10:00", - "value": 0 - }, - { - "time": "13:11:00", - "value": 0 - }, - { - "time": "13:12:00", - "value": 0 - }, - { - "time": "13:13:00", - "value": 0 - }, - { - "time": "13:14:00", - "value": 0 - }, - { - "time": "13:15:00", - "value": 0 - }, - { - "time": "13:16:00", - "value": 0 - }, - { - "time": "13:17:00", - "value": 0 - }, - { - "time": "13:18:00", - "value": 0 - }, - { - "time": "13:19:00", - "value": 0 - }, - { - "time": "13:20:00", - "value": 0 - }, - { - "time": "13:21:00", - "value": 0 - }, - { - "time": "13:22:00", - "value": 0 - }, - { - "time": "13:23:00", - "value": 0 - }, - { - "time": "13:24:00", - "value": 0 - }, - { - "time": "13:25:00", - "value": 0 - }, - { - "time": "13:26:00", - "value": 0 - }, - { - "time": "13:27:00", - "value": 0 - }, - { - "time": "13:28:00", - "value": 0 - }, - { - "time": "13:29:00", - "value": 0 - }, - { - "time": "13:30:00", - "value": 0 - }, - { - "time": "13:31:00", - "value": 0 - }, - { - "time": "13:32:00", - "value": 0 - }, - { - "time": "13:33:00", - "value": 0 - }, - { - "time": "13:34:00", - "value": 0 - }, - { - "time": "13:35:00", - "value": 0 - }, - { - "time": "13:36:00", - "value": 0 - }, - { - "time": "13:37:00", - "value": 0 - }, - { - "time": "13:38:00", - "value": 0 - }, - { - "time": "13:39:00", - "value": 0 - }, - { - "time": "13:40:00", - "value": 0 - }, - { - "time": "13:41:00", - "value": 0 - }, - { - "time": "13:42:00", - "value": 0 - }, - { - "time": "13:43:00", - "value": 0 - }, - { - "time": "13:44:00", - "value": 0 - }, - { - "time": "13:45:00", - "value": 0 - }, - { - "time": "13:46:00", - "value": 0 - }, - { - "time": "13:47:00", - "value": 0 - }, - { - "time": "13:48:00", - "value": 0 - }, - { - "time": "13:49:00", - "value": 0 - }, - { - "time": "13:50:00", - "value": 0 - }, - { - "time": "13:51:00", - "value": 0 - }, - { - "time": "13:52:00", - "value": 0 - }, - { - "time": "13:53:00", - "value": 0 - }, - { - "time": "13:54:00", - "value": 0 - }, - { - "time": "13:55:00", - "value": 0 - }, - { - "time": "13:56:00", - "value": 0 - }, - { - "time": "13:57:00", - "value": 0 - }, - { - "time": "13:58:00", - "value": 0 - }, - { - "time": "13:59:00", - "value": 0 - }, - { - "time": "14:00:00", - "value": 0 - }, - { - "time": "14:01:00", - "value": 0 - }, - { - "time": "14:02:00", - "value": 0 - }, - { - "time": "14:03:00", - "value": 0 - }, - { - "time": "14:04:00", - "value": 0 - }, - { - "time": "14:05:00", - "value": 0 - }, - { - "time": "14:06:00", - "value": 0 - }, - { - "time": "14:07:00", - "value": 0 - }, - { - "time": "14:08:00", - "value": 0 - }, - { - "time": "14:09:00", - "value": 0 - }, - { - "time": "14:10:00", - "value": 0 - }, - { - "time": "14:11:00", - "value": 0 - }, - { - "time": "14:12:00", - "value": 0 - }, - { - "time": "14:13:00", - "value": 0 - }, - { - "time": "14:14:00", - "value": 0 - }, - { - "time": "14:15:00", - "value": 0 - }, - { - "time": "14:16:00", - "value": 0 - }, - { - "time": "14:17:00", - "value": 0 - }, - { - "time": "14:18:00", - "value": 0 - }, - { - "time": "14:19:00", - "value": 0 - }, - { - "time": "14:20:00", - "value": 0 - }, - { - "time": "14:21:00", - "value": 0 - }, - { - "time": "14:22:00", - "value": 0 - }, - { - "time": "14:23:00", - "value": 0 - }, - { - "time": "14:24:00", - "value": 0 - }, - { - "time": "14:25:00", - "value": 0 - }, - { - "time": "14:26:00", - "value": 0 - }, - { - "time": "14:27:00", - "value": 0 - }, - { - "time": "14:28:00", - "value": 0 - } - ], - "datasetInterval": 1, - "datasetType": "minute" - } - } - } - } -] \ No newline at end of file diff --git a/shim-server/src/test/resources/fitbit-step-count.json b/shim-server/src/test/resources/fitbit-step-count.json deleted file mode 100644 index 7dcf114c..00000000 --- a/shim-server/src/test/resources/fitbit-step-count.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "result": { - "date": "2014-08-20", - "content": { - "activities-steps": [ - { - "dateTime": "2014-08-20", - "value": "4332" - } - ] - } - } - } -] diff --git a/shim-server/src/test/resources/fitbit-weight.json b/shim-server/src/test/resources/fitbit-weight.json deleted file mode 100644 index c58b54be..00000000 --- a/shim-server/src/test/resources/fitbit-weight.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "weight": [ - { - "bmi": 17.07, - "date": "2014-11-12", - "logId": 1415836799000, - "time": "23:59:59", - "weight": 49.4 - }, - { - "bmi": 18.01, - "date": "2014-11-13", - "logId": 1415923199000, - "time": "23:59:59", - "weight": 52.1 - }, - { - "bmi": 17.54, - "date": "2014-11-14", - "logId": 1416009599000, - "time": "23:59:59", - "weight": 50.8 - } - ] -} diff --git a/shim-server/src/test/resources/ihealth-activity.json b/shim-server/src/test/resources/ihealth-activity.json deleted file mode 100644 index 35c651d4..00000000 --- a/shim-server/src/test/resources/ihealth-activity.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "SPORTDataList": [ - { - "SportName": "Run ", - "SportStartTime": 1418292000, - "SportEndTime": 1418295600, - "TimeZone": -1, - "Lat": -1, - "Lon": -1, - "DataID": "90ab964ced9d46b789d9ab84d51beb4d", - "Calories": 1000.1 - } - ], - "CurrentRecordCount": 1, - "NextPageUrl": "", - "PageLength": 50, - "PageNumber": 1, - "PrevPageUrl": "", - "RecordCount": 1 -} diff --git a/shim-server/src/test/resources/ihealth-blood-glucose.json b/shim-server/src/test/resources/ihealth-blood-glucose.json deleted file mode 100644 index c26ec0e9..00000000 --- a/shim-server/src/test/resources/ihealth-blood-glucose.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "BGDataList": [ - { - "BG": 80.5, - "DataID": "90ab964ced9d46b789d9ab84d51beb4d", - "DataSource": "Manual", - "DinnerSituation": "Before_lunch", - "DrugSituation": "Before_taking_pills", - "LastChangeTime": 1418329906, - "Lat": 47.6097, - "Lon": -122.3331, - "MDate": 1418301049, - "Note": "testing" - } - ], - "BGUnit": 0, - "CurrentRecordCount": 1, - "NextPageUrl": "", - "PageLength": 50, - "PageNumber": 1, - "PrevPageUrl": "", - "RecordCount": 1 -} diff --git a/shim-server/src/test/resources/ihealth-blood-pressure.json b/shim-server/src/test/resources/ihealth-blood-pressure.json deleted file mode 100644 index de61e6f4..00000000 --- a/shim-server/src/test/resources/ihealth-blood-pressure.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "BPDataList": [ - { - "BPL": 6, - "DataID": "88bad68878044d079294470c9ffff8aa", - "DataSource": "Manual", - "HP": 100, - "HR": 60, - "IsArr": -1, - "LP": 50, - "LastChangeTime": 1418316989, - "Lat": -1, - "Lon": -1, - "MDate": 1418288156, - "Note": "testing" - } - ], - "BPUnit": 0, - "CurrentRecordCount": 1, - "NextPageUrl": "", - "PageLength": 50, - "PageNumber": 1, - "PrevPageUrl": "", - "RecordCount": 1 -} diff --git a/shim-server/src/test/resources/ihealth-body-weight.json b/shim-server/src/test/resources/ihealth-body-weight.json deleted file mode 100644 index fe574f2a..00000000 --- a/shim-server/src/test/resources/ihealth-body-weight.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "CurrentRecordCount": 2, - "NextPageUrl": "", - "PageLength": 50, - "PageNumber": 1, - "PrevPageUrl": "", - "RecordCount": 2, - "WeightDataList": [ - { - "BMI": 21.13917370644493, - "BoneValue": 0, - "DCI": 1884.3092505737952, - "DataID": "00696c63ca784a4d9c1809a526e23d71", - "DataSource": "Manual", - "FatValue": 11, - "LastChangeTime": 1418315034, - "MDate": 1418197090, - "MuscaleValue": 0, - "Note": "", - "WaterValue": 0, - "WeightValue": 75.86718536514545 - }, - { - "BMI": 20.989716993834165, - "BoneValue": 0, - "DCI": 1859.0779363336994, - "DataID": "38b0ac53a4314cc88566d2097de21b68", - "DataSource": "Manual", - "FatValue": 12, - "LastChangeTime": 1418314989, - "MDate": 1418282981, - "MuscaleValue": 0, - "Note": "testing", - "WaterValue": 0, - "WeightValue": 74.76518405458972 - } - ], - "WeightUnit": 1 -} diff --git a/shim-server/src/test/resources/ihealth-sleep.json b/shim-server/src/test/resources/ihealth-sleep.json deleted file mode 100644 index bd31d23d..00000000 --- a/shim-server/src/test/resources/ihealth-sleep.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "SRDataList": [ - { - "Awaken": 1, - "DataID": "90ab964ced9d46b789d9ab84d51beb4d", - "Fallsleep": "?", - "HoursSlept": 7.5, - "SleepEfficiency": 80, - "Lat": -1, - "Lon": -1, - "StartTime": 1418252400, - "EndTime": 1418281200, - "Note": "zzz" - } - ], - "FoodUnit": 1, - "CurrentRecordCount": 1, - "NextPageUrl": "", - "PageLength": 50, - "PageNumber": 1, - "PrevPageUrl": "", - "RecordCount": 1 -} diff --git a/shim-server/src/test/resources/ihealth-step-count.json b/shim-server/src/test/resources/ihealth-step-count.json deleted file mode 100644 index 9e412f89..00000000 --- a/shim-server/src/test/resources/ihealth-step-count.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "ARDataList": [ - { - "Calories": 1000.1, - "DataID": "90ab964ced9d46b789d9ab84d51beb4d", - "DistanceTraveled": 5.5, - "SleepEfficiency": 80, - "Lat": -1, - "Lon": -1, - "Steps": 9000, - "MDate": 1418281200, - "Note": "" - } - ], - "DistanceUnit": 1, - "CurrentRecordCount": 1, - "NextPageUrl": "", - "PageLength": 50, - "PageNumber": 1, - "PrevPageUrl": "", - "RecordCount": 1 -} diff --git a/shim-server/src/test/resources/jawbone-body-weight.json b/shim-server/src/test/resources/jawbone-body-weight.json deleted file mode 100644 index fadabab2..00000000 --- a/shim-server/src/test/resources/jawbone-body-weight.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "meta": { - "user_xid": "T-v4-jQoO5pN1eqBXkrLkQ", - "message": "OK", - "code": 200, - "time": 1429209473 - }, - "data": { - "items": [ - { - "lean_mass": null, - "time_updated": 1415212004, - "xid": "W7C8ClyS84KPZKT45hK4G3xnGDMYryW4", - "weight": 70.76041, - "title": null, - "body_fat": 12, - "image": "", - "bmi": null, - "time_created": 1415206811, - "note": null, - "details": { - "tz": "America/Los_Angeles" - }, - "waistline": null, - "date": 20141105, - "shared": false, - "type": "body" - }, - { - "lean_mass": null, - "time_updated": 1415212401, - "xid": "W7C8ClyS84LGuL9INlXL0-Nb0YifWU-R", - "weight": 70.7604, - "title": null, - "body_fat": 12, - "image": "", - "bmi": null, - "time_created": 1415212364, - "note": null, - "details": { - "tz": "America/Los_Angeles" - }, - "waistline": null, - "date": 20141105, - "shared": false, - "type": "body" - } - ], - "size": 2 - } -} diff --git a/shim-server/src/test/resources/jawbone-moves.json b/shim-server/src/test/resources/jawbone-moves.json deleted file mode 100644 index 425b6def..00000000 --- a/shim-server/src/test/resources/jawbone-moves.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "meta": { - "user_xid": "6xl39CsoVp2KirfHwVq_Fx", - "message": "OK", - "code": 200, - "time": 1386122022 - }, - "data": { - "items": [ - { - "xid": "40F7_htRRnQwoMjIFucJ2g", - "title": "16,804 steps", - "type": "move", - "time_created": 1384963500, - "time_updated": 1385049599, - "time_completed": 1385099220, - "date": 20131121, - "snapshot_image": "/nudge/image/e/1385107737/40F7_htRRnQwoMjIFucJ2g/grEGutn_XYZ.png", - "details": { - "distance": 14745, - "km": 14.745, - "steps": 16804, - "active_time": 11927, - "longest_active": 2516, - "inactive_time": 32760, - "longest_idle": 27180, - "calories": 1760.30480012, - "bmr_day": 1697.47946931, - "bmr": 1697.47946931, - "bg_calories": 1099.9439497, - "wo_calories": 388.506116077, - "wo_time": 11484, - "wo_active_time": 3902, - "wo_count": 2, - "wo_longest": 2516, - "tz": "America/Los Angeles", - "tzs": [ - [1384963500, "America/Phoenix"], - [1385055720, "America/Los_Angeles"] - ], - "hourly_totals": { - "2013112101": { - "distance": 1324, - "calories": 90.0120018125, - "steps": 1603, - "active_time": 793, - "inactive_time": 220, - "longest_active_time": 302, - "longest_idle_time": 780 - }, - "2013112102": { - "distance": 626, - "calories": 47.0120018125, - "steps": 455, - "active_time": 246, - "inactive_time": 260, - "longest_active_time": 203, - "longest_idle_time": 650 - } - } - } - }, - { - "xid": "40F7_htRRnQwoMjIFucJ2g", - "title": "16,804 steps", - "type": "move", - "time_created": 1384963500, - "time_updated": 1385049599, - "time_completed": 1385099220, - "date": 20131122, - "snapshot_image": "/nudge/image/e/1385107737/40F7_htRRnQwoMjIFucJ2g/grEGutn_XYZ.png", - "details": { - "distance": 14745, - "km": 14.745, - "steps": 16804, - "active_time": 11927, - "longest_active": 2516, - "inactive_time": 32760, - "longest_idle": 27180, - "calories": 1760.30480012, - "bmr_day": 1697.47946931, - "bmr": 1697.47946931, - "bg_calories": 1099.9439497, - "wo_calories": 388.506116077, - "wo_time": 11484, - "wo_active_time": 3902, - "wo_count": 2, - "wo_longest": 2516, - "tz": "America/Los Angeles", - "tzs": [ - [1384963500, "America/Phoenix"], - [1385055720, "America/Los_Angeles"] - ], - "hourly_totals": { - "2013112204": { - "distance": 1324, - "calories": 90.0120018125, - "steps": 1604, - "active_time": 793, - "inactive_time": 220, - "longest_active_time": 302, - "longest_idle_time": 780 - }, - "2013112205": { - "distance": 626, - "calories": 47.0120018125, - "steps": 456, - "active_time": 246, - "inactive_time": 260, - "longest_active_time": 203, - "longest_idle_time": 650 - } - } - } - } - ], - "links": { - "next": "/nudge/api/v.1.1/users/6xl39CsoVp2KirfHwVq_Fx/moves?page_token=1384390680" - }, - "size": 10 - } -} \ No newline at end of file diff --git a/shim-server/src/test/resources/jawbone-sleep.json b/shim-server/src/test/resources/jawbone-sleep.json deleted file mode 100644 index 7f872e0d..00000000 --- a/shim-server/src/test/resources/jawbone-sleep.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "meta": { - "user_xid": "T-v4-jQoO5pN1eqBXkrLkQ", - "message": "OK", - "code": 200, - "time": 1429210224 - }, - "data": { - "items": [ - { - "time_updated": 1409341770, - "xid": "7bmIm0ICa93CjNwY3D2N0g", - "title": "for 6h 12m", - "time_created": 1393995600, - "time_completed": 1394026045, - "details": { - "body": 0, - "sound": 14203, - "smart_alarm_fire": 0, - "awakenings": 0, - "light": 8126, - "mind": 0, - "asleep_time": 1393995600, - "awake_time": 1394026045, - "awake": 8116, - "rem": 0, - "duration": 30445, - "tz": "America/Los_Angeles", - "quality": 0, - "sunset": null, - "sunrise": null - }, - "date": 20140305, - "shared": true, - "snapshot_image": "/nudge/image/e/1409341770/7bmIm0ICa93CjNwY3D2N0g/fqFmGkTSvSw.png", - "sub_type": 0 - } - ], - "links": { - "next": "/nudge/api/v.1.1/users/T-v4-jQoO5pN1eqBXkrLkQ/sleeps?start_time=1397939921&updated_after=0&limit=50&end_time=1429167600" - }, - "size": 50 - } -} diff --git a/shim-server/src/test/resources/misfit-activities.json b/shim-server/src/test/resources/misfit-activities.json deleted file mode 100644 index 38536f8c..00000000 --- a/shim-server/src/test/resources/misfit-activities.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "sessions": [ - { - "id": "552eab896c59ae1f7300003e", - "activityType": "Walking", - "startTime": "2015-04-13T11:46:00-07:00", - "duration": 1140, - "points": 228.8, - "steps": 2026, - "calories": 96.8, - "distance": 0.9371 - }, - { - "id": "552edc21472c922550000027", - "activityType": "Walking", - "startTime": "2015-04-15T13:27:00-07:00", - "duration": 420, - "points": 0.8, - "steps": 8, - "calories": 0.3, - "distance": 0.003 - }, - { - "id": "552edc21472c922550000028", - "activityType": "Walking", - "startTime": "2015-04-15T14:08:00-07:00", - "duration": 2340, - "points": 25.6, - "steps": 256, - "calories": 10.8, - "distance": 0.0969 - } - ] -} diff --git a/shim-server/src/test/resources/org/openmhealth/shim/googlefit/mapper/googlefit-merge-location-samples.json b/shim-server/src/test/resources/org/openmhealth/shim/googlefit/mapper/googlefit-merge-location-samples.json new file mode 100644 index 00000000..1184fdf4 --- /dev/null +++ b/shim-server/src/test/resources/org/openmhealth/shim/googlefit/mapper/googlefit-merge-location-samples.json @@ -0,0 +1,53 @@ +{ + "minStartTimeNs": "1506816000000000000", + "maxEndTimeNs": "1509494399000000000", + "dataSourceId": "derived:com.google.location.sample:com.google.android.gms:merge_location_samples", + "point": [ + { + "modifiedTimeMillis": "1507028376296", + "startTimeNanos": "1507027899615000000", + "endTimeNanos": "1507027899615000000", + "value": [ + { + "mapVal": [ ], + "fpVal": 51.211826373291016 + }, + { + "mapVal": [ ], + "fpVal": -0.57192360877990723 + }, + { + "mapVal": [ ], + "fpVal": 20.0 + } + ], + "dataTypeName": "com.google.location.sample", + "originDataSourceId": "raw:com.google.location.sample:com.google.android.gms:LGE:Nexus 5:foo:live_location" + }, + { + "modifiedTimeMillis": "1507127495556", + "startTimeNanos": "1507124754000000000", + "endTimeNanos": "1507124754000000000", + "value": [ + { + "mapVal": [ ], + "fpVal": 51.233600616455078 + }, + { + "mapVal": [ ], + "fpVal": -0.57613241672515869 + }, + { + "mapVal": [ ], + "fpVal": 43.0 + }, + { + "mapVal": [ ], + "fpVal": 50.0 + } + ], + "dataTypeName": "com.google.location.sample", + "originDataSourceId": "raw:com.google.location.sample:com.google.android.gms:LGE:Nexus 5:bar:live_location" + } + ] +} \ No newline at end of file diff --git a/shim-server/src/test/resources/org/openmhealth/shim/googlefit/mapper/googlefit-merge-speed.json b/shim-server/src/test/resources/org/openmhealth/shim/googlefit/mapper/googlefit-merge-speed.json new file mode 100644 index 00000000..64e04fdf --- /dev/null +++ b/shim-server/src/test/resources/org/openmhealth/shim/googlefit/mapper/googlefit-merge-speed.json @@ -0,0 +1,33 @@ +{ + "minStartTimeNs": "1506816000000000000", + "maxEndTimeNs": "1509494399000000000", + "dataSourceId": "derived:com.google.speed:com.google.android.gms:merge_speed", + "point": [ + { + "modifiedTimeMillis": "1507127495124", + "startTimeNanos": "1507125356000000000", + "endTimeNanos": "1507125356000000000", + "value": [ + { + "mapVal": [ ], + "fpVal": 0.75 + } + ], + "dataTypeName": "com.google.speed", + "originDataSourceId": "derived:com.google.speed:com.google.android.gms:LGE:Nexus 5:foo:live_gps_speed" + }, + { + "modifiedTimeMillis": "1507127495124", + "startTimeNanos": "1507125953000000000", + "endTimeNanos": "1507125953000000000", + "value": [ + { + "mapVal": [ ], + "fpVal": 2.25 + } + ], + "dataTypeName": "com.google.speed", + "originDataSourceId": "derived:com.google.speed:com.google.android.gms:LGE:Nexus 5:bar:live_gps_speed" + } + ] +} \ No newline at end of file diff --git a/shim-server/src/test/resources/org/openmhealth/shim/misfit/mapper/misfit-sleeps-detected-and-not.json b/shim-server/src/test/resources/org/openmhealth/shim/misfit/mapper/misfit-sleeps-detected-and-not.json deleted file mode 100644 index d4cbd65c..00000000 --- a/shim-server/src/test/resources/org/openmhealth/shim/misfit/mapper/misfit-sleeps-detected-and-not.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "sleeps": [ - { - "id": "54fa13a8440f705a7406844f", - "autoDetected": true, - "startTime": "2015-02-23T21:40:59-05:00", - "duration": 11580, - "sleepDetails": [ - { - "datetime": "2015-02-23T21:40:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T21:59:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-23T22:09:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:15:59-05:00", - "value": 1 - }, - { - "datetime": "2015-02-23T22:20:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:26:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-23T22:31:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:53:59-05:00", - "value": 1 - }, - { - "datetime": "2015-02-23T23:12:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T23:18:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-24T00:02:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-24T00:21:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-24T00:49:59-05:00", - "value": 2 - } - ] - }, - { - "id": "54fa13a8440f705a7406844f", - "autoDetected": false, - "startTime": "2015-02-23T21:40:59-05:00", - "duration": 11580, - "sleepDetails": [ - { - "datetime": "2015-02-23T21:40:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T21:59:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-23T22:09:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:15:59-05:00", - "value": 1 - }, - { - "datetime": "2015-02-23T22:20:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:26:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-23T22:31:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:53:59-05:00", - "value": 1 - }, - { - "datetime": "2015-02-23T23:12:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T23:18:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-24T00:02:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-24T00:21:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-24T00:49:59-05:00", - "value": 2 - } - ] - }, - { - "id": "54fa13a8440f705a7406844f", - "startTime": "2015-02-23T21:40:59-05:00", - "duration": 11580, - "sleepDetails": [ - { - "datetime": "2015-02-23T21:40:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T21:59:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-23T22:09:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:15:59-05:00", - "value": 1 - }, - { - "datetime": "2015-02-23T22:20:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:26:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-23T22:31:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T22:53:59-05:00", - "value": 1 - }, - { - "datetime": "2015-02-23T23:12:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-23T23:18:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-24T00:02:59-05:00", - "value": 2 - }, - { - "datetime": "2015-02-24T00:21:59-05:00", - "value": 3 - }, - { - "datetime": "2015-02-24T00:49:59-05:00", - "value": 2 - } - ] - } - ] -} \ No newline at end of file diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-storyline-empty.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-storyline-empty.json deleted file mode 100644 index 66caf9b6..00000000 --- a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-storyline-empty.json +++ /dev/null @@ -1,82 +0,0 @@ -[ - { - "date": "20160403", - "summary": [ - { - "activity": "transport", - "group": "transport", - "duration": 1683.0, - "distance": 37980.0 - } - ], - "segments": [ - { - "type": "move", - "startTime": "20160403T080416-0500", - "endTime": "20160403T082703-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160403T080416-0500", - "endTime": "20160403T082703-0500", - "duration": 1367.0, - "distance": 35841.0 - } - ], - "lastUpdate": "20160403T135826Z" - }, - { - "type": "place", - "startTime": "20160403T082704-0500", - "endTime": "20160403T083233-0500", - "place": { - "id": 271508698, - "type": "unknown", - "location": { - "lat": 45.106348, - "lon": -93.453067 - } - }, - "lastUpdate": "20160403T135826Z" - }, - { - "type": "move", - "startTime": "20160403T083233-0500", - "endTime": "20160403T083601-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160403T083233-0500", - "endTime": "20160403T083601-0500", - "duration": 208.0, - "distance": 1556.0 - } - ], - "lastUpdate": "20160403T135826Z" - }, - { - "type": "move", - "startTime": "20160403T172223-0500", - "endTime": "20160403T172411-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160403T172223-0500", - "endTime": "20160403T172411-0500", - "duration": 108.0, - "distance": 583.0 - } - ], - "lastUpdate": "20160404T000547Z" - } - ], - "lastUpdate": "20160404T121500Z" - } - -] \ No newline at end of file diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-storyline.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-storyline.json deleted file mode 100644 index 044d8c88..00000000 --- a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-storyline.json +++ /dev/null @@ -1,1206 +0,0 @@ -[ - { - "date": "20160403", - "summary": [ - { - "activity": "walking", - "group": "walking", - "duration": 1157.0, - "distance": 1062.0, - "steps": 1741 - }, - { - "activity": "transport", - "group": "transport", - "duration": 1683.0, - "distance": 37980.0 - }, - { - "activity": "cycling", - "group": "cycling", - "duration": 863.0, - "distance": 3791.0 - }, - { - "activity": "racquetball", - "duration": 2589.0 - } - ], - "segments": [ - { - "type": "place", - "startTime": "20160402T164813-0500", - "endTime": "20160403T080416-0500", - "place": { - "id": 271251796, - "type": "unknown", - "location": { - "lat": 44.919955, - "lon": -93.251065 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T004438-0500", - "endTime": "20160403T004445-0500", - "duration": 7.0, - "distance": 5.0, - "steps": 10 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T070314-0500", - "endTime": "20160403T070845-0500", - "duration": 331.0, - "distance": 372.0, - "steps": 496 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T080000-0500", - "endTime": "20160403T080119-0500", - "duration": 79.0, - "distance": 60.0, - "steps": 121 - } - ], - "lastUpdate": "20160403T135826Z" - }, - { - "type": "move", - "startTime": "20160403T080416-0500", - "endTime": "20160403T082703-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160403T080416-0500", - "endTime": "20160403T082703-0500", - "duration": 1367.0, - "distance": 35841.0 - } - ], - "lastUpdate": "20160403T135826Z" - }, - { - "type": "place", - "startTime": "20160403T082704-0500", - "endTime": "20160403T083233-0500", - "place": { - "id": 271508698, - "type": "unknown", - "location": { - "lat": 45.106348, - "lon": -93.453067 - } - }, - "lastUpdate": "20160403T135826Z" - }, - { - "type": "move", - "startTime": "20160403T083233-0500", - "endTime": "20160403T083601-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160403T083233-0500", - "endTime": "20160403T083601-0500", - "duration": 208.0, - "distance": 1556.0 - } - ], - "lastUpdate": "20160403T135826Z" - }, - { - "type": "place", - "startTime": "20160403T083602-0500", - "endTime": "20160403T172223-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T083653-0500", - "endTime": "20160403T083710-0500", - "duration": 17.0, - "distance": 13.0, - "steps": 26 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T091441-0500", - "endTime": "20160403T091527-0500", - "duration": 46.0, - "distance": 34.0, - "steps": 68 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T101821-0500", - "endTime": "20160403T101904-0500", - "duration": 43.0, - "distance": 29.0, - "steps": 59 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T120742-0500", - "endTime": "20160403T120921-0500", - "duration": 99.0, - "distance": 73.0, - "steps": 147 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T132612-0500", - "endTime": "20160403T132621-0500", - "duration": 9.0, - "distance": 6.0, - "steps": 12 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T140307-0500", - "endTime": "20160403T140618-0500", - "duration": 191.0, - "distance": 214.0, - "steps": 286 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T151348-0500", - "endTime": "20160403T151529-0500", - "duration": 101.0, - "distance": 84.0, - "steps": 168 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T171338-0500", - "endTime": "20160403T171359-0500", - "duration": 21.0, - "distance": 15.0, - "steps": 31 - } - ], - "lastUpdate": "20160404T000547Z" - }, - { - "type": "move", - "startTime": "20160403T172223-0500", - "endTime": "20160403T172411-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160403T172223-0500", - "endTime": "20160403T172411-0500", - "duration": 108.0, - "distance": 583.0 - } - ], - "lastUpdate": "20160404T000547Z" - }, - { - "type": "place", - "startTime": "20160403T172411-0500", - "endTime": "20160403T175200-0500", - "place": { - "id": 183785425, - "name": "Schulers House", - "type": "user", - "location": { - "lat": 45.1124, - "lon": -93.4389 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T172423-0500", - "endTime": "20160403T172504-0500", - "duration": 41.0, - "distance": 31.0, - "steps": 62 - } - ], - "lastUpdate": "20160404T025546Z" - }, - { - "type": "move", - "startTime": "20160403T175200-0500", - "endTime": "20160403T175710-0500", - "activities": [ - { - "activity": "cycling", - "group": "cycling", - "manual": false, - "startTime": "20160403T175200-0500", - "endTime": "20160403T175709-0500", - "duration": 309.0, - "distance": 1500.0 - } - ], - "lastUpdate": "20160404T000547Z" - }, - { - "type": "place", - "startTime": "20160403T175710-0500", - "endTime": "20160403T185541-0500", - "place": { - "id": 270593483, - "name": "Central Park Maple Grove", - "type": "facebook", - "facebookPlaceId": "953600528018875", - "location": { - "lat": 45.101954071975, - "lon": -93.432377843758 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T175723-0500", - "endTime": "20160403T175749-0500", - "duration": 26.0, - "distance": 19.0, - "steps": 39 - }, - { - "activity": "racquetball", - "manual": false, - "startTime": "20160403T180451-0500", - "endTime": "20160403T184800-0500", - "duration": 2589.0 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T185407-0500", - "endTime": "20160403T185442-0500", - "duration": 35.0, - "distance": 25.0, - "steps": 51 - } - ], - "lastUpdate": "20160404T025318Z" - }, - { - "type": "move", - "startTime": "20160403T185541-0500", - "endTime": "20160403T190455-0500", - "activities": [ - { - "activity": "cycling", - "group": "cycling", - "manual": false, - "startTime": "20160403T185541-0500", - "endTime": "20160403T190455-0500", - "duration": 554.0, - "distance": 2291.0 - } - ], - "lastUpdate": "20160404T010035Z" - }, - { - "type": "place", - "startTime": "20160403T190455-0500", - "endTime": "20160404T060640-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T190508-0500", - "endTime": "20160403T190611-0500", - "duration": 63.0, - "distance": 47.0, - "steps": 95 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160403T201045-0500", - "endTime": "20160403T201133-0500", - "duration": 48.0, - "distance": 35.0, - "steps": 70 - } - ], - "lastUpdate": "20160404T121500Z" - } - ], - "lastUpdate": "20160404T121500Z" - }, - { - "date": "20160404", - "summary": [ - { - "activity": "walking", - "group": "walking", - "duration": 4455.0, - "distance": 6357.0, - "steps": 7579 - }, - { - "activity": "transport", - "group": "transport", - "duration": 582.0, - "distance": 4596.0 - } - ], - "segments": [ - { - "type": "place", - "startTime": "20160403T190455-0500", - "endTime": "20160404T060640-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T054307-0500", - "endTime": "20160404T054321-0500", - "duration": 14.0, - "distance": 10.0, - "steps": 21 - } - ], - "lastUpdate": "20160404T121500Z" - }, - { - "type": "move", - "startTime": "20160404T060640-0500", - "endTime": "20160404T061208-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160404T060640-0500", - "endTime": "20160404T061207-0500", - "duration": 327.0, - "distance": 2260.0 - } - ], - "lastUpdate": "20160404T121500Z" - }, - { - "type": "place", - "startTime": "20160404T061208-0500", - "endTime": "20160404T111543-0500", - "place": { - "id": 270546654, - "name": "Starbucks", - "type": "facebook", - "facebookPlaceId": "148451518518112", - "location": { - "lat": 45.09955, - "lon": -93.45011 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T061311-0500", - "endTime": "20160404T061424-0500", - "duration": 73.0, - "distance": 55.0, - "steps": 110 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T102452-0500", - "endTime": "20160404T102511-0500", - "duration": 19.0, - "distance": 13.0, - "steps": 26 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T111344-0500", - "endTime": "20160404T111418-0500", - "duration": 34.0, - "distance": 24.0, - "steps": 48 - } - ], - "lastUpdate": "20160404T165620Z" - }, - { - "type": "move", - "startTime": "20160404T111543-0500", - "endTime": "20160404T111958-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160404T111543-0500", - "endTime": "20160404T111958-0500", - "duration": 255.0, - "distance": 2336.0 - } - ], - "lastUpdate": "20160404T165620Z" - }, - { - "type": "place", - "startTime": "20160404T111958-0500", - "endTime": "20160404T175451-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T112048-0500", - "endTime": "20160404T112244-0500", - "duration": 116.0, - "distance": 87.0, - "steps": 175 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T120952-0500", - "endTime": "20160404T121016-0500", - "duration": 24.0, - "distance": 17.0, - "steps": 34 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T174519-0500", - "endTime": "20160404T174654-0500", - "duration": 95.0, - "distance": 70.0, - "steps": 141 - } - ], - "lastUpdate": "20160405T004715Z" - }, - { - "type": "move", - "startTime": "20160404T175451-0500", - "endTime": "20160404T185831-0500", - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T175451-0500", - "endTime": "20160404T185831-0500", - "duration": 3820.0, - "distance": 5844.0, - "steps": 6642 - } - ], - "lastUpdate": "20160405T004715Z" - }, - { - "type": "place", - "startTime": "20160404T185831-0500", - "endTime": "20160405T060850-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T185831-0500", - "endTime": "20160404T185850-0500", - "duration": 19.0, - "distance": 14.0, - "steps": 29 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T190142-0500", - "endTime": "20160404T190348-0500", - "duration": 126.0, - "distance": 139.0, - "steps": 185 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T203019-0500", - "endTime": "20160404T203034-0500", - "duration": 15.0, - "distance": 11.0, - "steps": 22 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160404T212854-0500", - "endTime": "20160404T213034-0500", - "duration": 100.0, - "distance": 73.0, - "steps": 146 - } - ], - "lastUpdate": "20160405T113354Z" - } - ], - "lastUpdate": "20160405T113354Z" - }, - { - "date": "20160405", - "summary": [ - { - "activity": "walking", - "group": "walking", - "duration": 3483.0, - "distance": 3292.0, - "steps": 4696 - }, - { - "activity": "transport", - "group": "transport", - "duration": 4402.0, - "distance": 72735.0 - } - ], - "segments": [ - { - "type": "place", - "startTime": "20160404T185831-0500", - "endTime": "20160405T060850-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T060751-0500", - "endTime": "20160405T060803-0500", - "duration": 12.0, - "distance": 8.0, - "steps": 17 - } - ], - "lastUpdate": "20160405T113354Z" - }, - { - "type": "move", - "startTime": "20160405T060850-0500", - "endTime": "20160405T061330-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160405T060850-0500", - "endTime": "20160405T061330-0500", - "duration": 280.0, - "distance": 2149.0 - } - ], - "lastUpdate": "20160405T113354Z" - }, - { - "type": "place", - "startTime": "20160405T061330-0500", - "endTime": "20160405T100949-0500", - "place": { - "id": 270546654, - "name": "Starbucks", - "type": "facebook", - "facebookPlaceId": "148451518518112", - "location": { - "lat": 45.09955, - "lon": -93.45011 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T061405-0500", - "endTime": "20160405T061436-0500", - "duration": 31.0, - "distance": 23.0, - "steps": 47 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T072328-0500", - "endTime": "20160405T072402-0500", - "duration": 34.0, - "distance": 24.0, - "steps": 48 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T080548-0500", - "endTime": "20160405T080629-0500", - "duration": 41.0, - "distance": 29.0, - "steps": 59 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T093110-0500", - "endTime": "20160405T093146-0500", - "duration": 36.0, - "distance": 26.0, - "steps": 53 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T100403-0500", - "endTime": "20160405T100501-0500", - "duration": 58.0, - "distance": 41.0, - "steps": 83 - } - ], - "lastUpdate": "20160405T163722Z" - }, - { - "type": "move", - "startTime": "20160405T100949-0500", - "endTime": "20160405T101356-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160405T100949-0500", - "endTime": "20160405T101355-0500", - "duration": 246.0, - "distance": 2164.0 - } - ], - "lastUpdate": "20160405T163722Z" - }, - { - "type": "place", - "startTime": "20160405T101356-0500", - "endTime": "20160405T122948-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T101431-0500", - "endTime": "20160405T101622-0500", - "duration": 111.0, - "distance": 83.0, - "steps": 167 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T115841-0500", - "endTime": "20160405T115859-0500", - "duration": 18.0, - "distance": 12.0, - "steps": 24 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T121302-0500", - "endTime": "20160405T121359-0500", - "duration": 57.0, - "distance": 37.0, - "steps": 75 - } - ], - "lastUpdate": "20160405T181037Z" - }, - { - "type": "move", - "startTime": "20160405T122948-0500", - "endTime": "20160405T124749-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160405T122948-0500", - "endTime": "20160405T124248-0500", - "duration": 780.0, - "distance": 13041.0 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T124248-0500", - "endTime": "20160405T124749-0500", - "duration": 301.0, - "distance": 204.0, - "steps": 408 - } - ], - "lastUpdate": "20160405T181037Z" - }, - { - "type": "place", - "startTime": "20160405T124749-0500", - "endTime": "20160405T125907-0500", - "place": { - "id": 272072139, - "type": "unknown", - "location": { - "lat": 45.172561, - "lon": -93.388651 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T124749-0500", - "endTime": "20160405T125246-0500", - "duration": 297.0, - "distance": 334.0, - "steps": 446 - } - ], - "lastUpdate": "20160405T181037Z" - }, - { - "type": "move", - "startTime": "20160405T125907-0500", - "endTime": "20160405T130439-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160405T125907-0500", - "endTime": "20160405T130439-0500", - "duration": 332.0, - "distance": 4025.0 - } - ], - "lastUpdate": "20160405T181037Z" - }, - { - "type": "place", - "startTime": "20160405T130439-0500", - "endTime": "20160405T134453-0500", - "place": { - "id": 272072140, - "type": "unknown", - "location": { - "lat": 45.153884, - "lon": -93.381908 - } - }, - "lastUpdate": "20160405T194550Z" - }, - { - "type": "move", - "startTime": "20160405T134453-0500", - "endTime": "20160405T135453-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160405T134453-0500", - "endTime": "20160405T135453-0500", - "duration": 600.0, - "distance": 8915.0 - } - ], - "lastUpdate": "20160405T194550Z" - }, - { - "type": "place", - "startTime": "20160405T135453-0500", - "endTime": "20160405T190110-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T135609-0500", - "endTime": "20160405T135623-0500", - "duration": 14.0, - "distance": 10.0, - "steps": 21 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T145410-0500", - "endTime": "20160405T145427-0500", - "duration": 17.0, - "distance": 11.0, - "steps": 23 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T170917-0500", - "endTime": "20160405T170932-0500", - "duration": 15.0, - "distance": 10.0, - "steps": 20 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T182858-0500", - "endTime": "20160405T183034-0500", - "duration": 96.0, - "distance": 70.0, - "steps": 141 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T190000-0500", - "endTime": "20160405T190009-0500", - "duration": 9.0, - "distance": 6.0, - "steps": 13 - } - ], - "lastUpdate": "20160406T002743Z" - }, - { - "type": "move", - "startTime": "20160405T190110-0500", - "endTime": "20160405T192037-0500", - "activities": [ - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160405T190110-0500", - "endTime": "20160405T191828-0500", - "duration": 1038.0, - "distance": 20171.0 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T191828-0500", - "endTime": "20160405T192037-0500", - "duration": 129.0, - "distance": 197.0, - "steps": 164 - } - ], - "lastUpdate": "20160406T002743Z" - }, - { - "type": "place", - "startTime": "20160405T192038-0500", - "endTime": "20160405T201339-0500", - "place": { - "id": 272138620, - "type": "unknown", - "location": { - "lat": 44.96811, - "lon": -93.438032 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T192211-0500", - "endTime": "20160405T192811-0500", - "duration": 360.0, - "distance": 405.0, - "steps": 541 - }, - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T201205-0500", - "endTime": "20160405T201250-0500", - "duration": 45.0, - "distance": 32.0, - "steps": 64 - } - ], - "lastUpdate": "20160406T015903Z" - }, - { - "type": "move", - "startTime": "20160405T201339-0500", - "endTime": "20160405T201829-0500", - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T201339-0500", - "endTime": "20160405T201829-0500", - "duration": 290.0, - "distance": 315.0, - "steps": 441 - } - ], - "lastUpdate": "20160406T015903Z" - }, - { - "type": "place", - "startTime": "20160405T201830-0500", - "endTime": "20160405T203143-0500", - "place": { - "id": 272138621, - "type": "unknown", - "location": { - "lat": 44.969012, - "lon": -93.434829 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T201830-0500", - "endTime": "20160405T202158-0500", - "duration": 208.0, - "distance": 234.0, - "steps": 312 - } - ], - "lastUpdate": "20160406T015903Z" - }, - { - "type": "move", - "startTime": "20160405T203143-0500", - "endTime": "20160405T204753-0500", - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T203143-0500", - "endTime": "20160405T204753-0500", - "duration": 970.0, - "distance": 890.0, - "steps": 1071 - } - ], - "lastUpdate": "20160406T141102Z" - }, - { - "type": "place", - "startTime": "20160405T204753-0500", - "endTime": "20160405T205343-0500", - "place": { - "id": 272128625, - "type": "unknown", - "location": { - "lat": 44.968122, - "lon": -93.437331 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T204753-0500", - "endTime": "20160405T204854-0500", - "duration": 61.0, - "distance": 46.0, - "steps": 92 - } - ], - "lastUpdate": "20160406T033046Z" - }, - { - "type": "move", - "startTime": "20160405T205343-0500", - "endTime": "20160405T211636-0500", - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T205343-0500", - "endTime": "20160405T205750-0500", - "duration": 247.0, - "distance": 226.0, - "steps": 327 - }, - { - "activity": "transport", - "group": "transport", - "manual": false, - "startTime": "20160405T205750-0500", - "endTime": "20160405T211636-0500", - "duration": 1126.0, - "distance": 22270.0 - } - ], - "lastUpdate": "20160406T033046Z" - }, - { - "type": "place", - "startTime": "20160405T211636-0500", - "endTime": "20160406T091557-0500", - "place": { - "id": 173763328, - "name": "Home", - "type": "home", - "location": { - "lat": 45.102030, - "lon": -93.441184 - } - }, - "activities": [ - { - "activity": "walking", - "group": "walking", - "manual": false, - "startTime": "20160405T211731-0500", - "endTime": "20160405T211757-0500", - "duration": 26.0, - "distance": 19.0, - "steps": 39 - } - ], - "lastUpdate": "20160406T141558Z" - } - ], - "lastUpdate": "20160406T141558Z" - } -] \ No newline at end of file diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-summary.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-summary.json deleted file mode 100644 index 6df48695..00000000 --- a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-summary.json +++ /dev/null @@ -1,69 +0,0 @@ -[ - { - "date": "20160403", - "summary": [ - { - "activity": "walking", - "group": "walking", - "duration": 1157.0, - "distance": 1062.0, - "steps": 1741 - }, - { - "activity": "transport", - "group": "transport", - "duration": 1683.0, - "distance": 37980.0 - }, - { - "activity": "cycling", - "group": "cycling", - "duration": 863.0, - "distance": 3791.0 - }, - { - "activity": "racquetball", - "duration": 2589.0 - } - ], - "lastUpdate": "20160404T121500Z" - }, - { - "date": "20160404", - "summary": [ - { - "activity": "walking", - "group": "walking", - "duration": 4455.0, - "distance": 6357.0, - "steps": 7579 - }, - { - "activity": "transport", - "group": "transport", - "duration": 582.0, - "distance": 4596.0 - } - ], - "lastUpdate": "20160405T113354Z" - }, - { - "date": "20160405", - "summary": [ - { - "activity": "walking", - "group": "walking", - "duration": 3483.0, - "distance": 3292.0, - "steps": 4696 - }, - { - "activity": "transport", - "group": "transport", - "duration": 4402.0, - "distance": 72735.0 - } - ], - "lastUpdate": "20160406T141558Z" - } -] \ No newline at end of file diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-activities-daily.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-activities-daily.json new file mode 100644 index 00000000..263d82cd --- /dev/null +++ b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-activities-daily.json @@ -0,0 +1,192 @@ +[ + { + "date": "20121212", + "summary": [ + { + "activity": "walking", + "group": "walking", + "duration": 3333, + "distance": 3333, + "steps": 3333, + "calories": 300 + }, + { + "activity": "walking_on_treadmill", + "group": "walking", + "duration": 270, + "steps": 303, + "calories": 30 + }, + { + "activity": "transport", + "group": "transport", + "duration": 1124, + "distance": 8443 + }, + { + "activity": "underground", + "group": "transport", + "duration": 1003, + "distance": 8058 + }, + { + "activity": "zumba", + "duration": 570, + "calories": 200 + } + ], + "segments": [ + { + "type": "move", + "startTime": "20121212T071430+0200", + "endTime": "20121212T074617+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T071430+0200", + "endTime": "20121212T072732+0200", + "duration": 782, + "distance": 1251, + "steps": 1353, + "calories": 99 + }, + { + "activity": "transport", + "group": "transport", + "manual": false, + "startTime": "20121212T072732+0200", + "endTime": "20121212T074616+0200", + "duration": 1124, + "distance": 8443 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T074617+0200", + "endTime": "20121212T100051+0200", + "activities": [ + { + "activity": "walking_on_treadmill", + "group": "walking", + "manual": true, + "duration": 270, + "steps": 303, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T100051+0200", + "endTime": "20121212T100715+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T100051+0200", + "endTime": "20121212T100715+0200", + "duration": 384, + "distance": 421, + "steps": 488, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T100715+0200", + "endTime": "20121212T110530+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T101215+0200", + "endTime": "20121212T101255+0200", + "duration": 40, + "distance": 18, + "steps": 37, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T110530+0200", + "endTime": "20121212T111129+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T110530+0200", + "endTime": "20121212T111128+0200", + "duration": 358, + "distance": 493, + "steps": 441, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T111129+0200", + "endTime": "20121212T153638+0200", + "activities": [ + { + "activity": "zumba", + "manual": true, + "duration": 570, + "calories": 200 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T153638+0200", + "endTime": "20121212T160744+0200", + "activities": [ + { + "activity": "underground", + "group": "transport", + "manual": false, + "startTime": "20121212T153638+0200", + "endTime": "20121212T155321+0200", + "duration": 1003, + "distance": 8058 + }, + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T155322+0200", + "endTime": "20121212T160744+0200", + "duration": 862, + "distance": 1086, + "steps": 1257, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + } + ], + "caloriesIdle": 1785, + "lastUpdate": "20130317T121143Z" + }, + { + "date": "20121213", + "summary": null, + "segments": null, + "caloriesIdle": 1785 + } +] diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-segments.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-segments.json new file mode 100644 index 00000000..08ff8992 --- /dev/null +++ b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-segments.json @@ -0,0 +1,27 @@ +[ + { + "date": "20121212", + "summary": [ + { + "activity": "transport", + "group": "transport", + "duration": 1124, + "distance": 8443 + }, + { + "activity": "underground", + "group": "transport", + "duration": 1003, + "distance": 8058 + }, + { + "activity": "zumba", + "duration": 570, + "calories": 200 + } + ], + "segments": null, + "caloriesIdle": 1785, + "lastUpdate": "20130317T121143Z" + } +] diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-steps.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-steps.json new file mode 100644 index 00000000..4559be72 --- /dev/null +++ b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-steps.json @@ -0,0 +1,179 @@ +[ + { + "date": "20121212", + "summary": [ + { + "activity": "transport", + "group": "transport", + "duration": 1124, + "distance": 8443 + }, + { + "activity": "underground", + "group": "transport", + "duration": 1003, + "distance": 8058 + }, + { + "activity": "zumba", + "duration": 570, + "calories": 200 + } + ], + "segments": [ + { + "type": "place", + "startTime": "20121212T000000+0200", + "endTime": "20121212T071430+0200", + "place": { + "id": 1, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T071430+0200", + "endTime": "20121212T074617+0200", + "activities": [ + { + "activity": "transport", + "group": "transport", + "manual": false, + "startTime": "20121212T072732+0200", + "endTime": "20121212T074616+0200", + "duration": 1124, + "distance": 8443, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T072732+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T074208+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T074617+0200" + } + ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T074617+0200", + "endTime": "20121212T100051+0200", + "place": { + "id": 2, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T100715+0200", + "endTime": "20121212T110530+0200", + "place": { + "id": 4, + "name": "test", + "type": "foursquare", + "foursquareId": "4df0fdb17d8ba370a011d24c", + "foursquareCategoryIds": [ + "4bf58dd8d48988d125941735" + ], + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T111129+0200", + "endTime": "20121212T153638+0200", + "place": { + "id": 2, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + { + "activity": "zumba", + "manual": true, + "duration": 570, + "calories": 200, + "trackPoints": [ ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T153638+0200", + "endTime": "20121212T160744+0200", + "activities": [ + { + "activity": "underground", + "group": "transport", + "manual": false, + "startTime": "20121212T153638+0200", + "endTime": "20121212T155321+0200", + "duration": 1003, + "distance": 8058, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T153638+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T155322+0200" + } + ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T160744+0200", + "endTime": "20121212T232730+0200", + "place": { + "id": 1, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "lastUpdate": "20130317T121143Z" + } + ], + "caloriesIdle": 1785, + "lastUpdate": "20130317T121143Z" + } +] diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-track-points.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-track-points.json new file mode 100644 index 00000000..a160c377 --- /dev/null +++ b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily-no-track-points.json @@ -0,0 +1,247 @@ +[ + { + "date": "20121212", + "summary": [ + { + "activity": "walking", + "group": "walking", + "duration": 3333, + "distance": 3333, + "steps": 3333, + "calories": 300 + }, + { + "activity": "walking_on_treadmill", + "group": "walking", + "duration": 270, + "steps": 303, + "calories": 30 + }, + { + "activity": "transport", + "group": "transport", + "duration": 1124, + "distance": 8443 + }, + { + "activity": "underground", + "group": "transport", + "duration": 1003, + "distance": 8058 + }, + { + "activity": "zumba", + "duration": 570, + "calories": 200 + } + ], + "segments": [ + { + "type": "place", + "startTime": "20121212T000000+0200", + "endTime": "20121212T071430+0200", + "place": { + "id": 1, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T071430+0200", + "endTime": "20121212T074617+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T071430+0200", + "endTime": "20121212T072732+0200", + "duration": 782, + "distance": 1251, + "steps": 1353, + "calories": 99, + "trackPoints": [ ] + }, + { + "activity": "transport", + "group": "transport", + "manual": false, + "startTime": "20121212T072732+0200", + "endTime": "20121212T074616+0200", + "duration": 1124, + "distance": 8443 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T074617+0200", + "endTime": "20121212T100051+0200", + "place": { + "id": 2, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + { + "activity": "walking_on_treadmill", + "group": "walking", + "manual": true, + "duration": 270, + "steps": 303, + "calories": 30, + "trackPoints": [ ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T100051+0200", + "endTime": "20121212T100715+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T100051+0200", + "endTime": "20121212T100715+0200", + "duration": 384, + "distance": 421, + "steps": 488, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T100715+0200", + "endTime": "20121212T110530+0200", + "place": { + "id": 4, + "name": "test", + "type": "foursquare", + "foursquareId": "4df0fdb17d8ba370a011d24c", + "foursquareCategoryIds": [ + "4bf58dd8d48988d125941735" + ], + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T101215+0200", + "endTime": "20121212T101255+0200", + "duration": 40, + "distance": 18, + "steps": 37, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T110530+0200", + "endTime": "20121212T111129+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T110530+0200", + "endTime": "20121212T111128+0200", + "duration": 358, + "distance": 493, + "steps": 441, + "calories": 99, + "trackPoints": [ ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T111129+0200", + "endTime": "20121212T153638+0200", + "place": { + "id": 2, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + { + "activity": "zumba", + "manual": true, + "duration": 570, + "calories": 200, + "trackPoints": [ ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T153638+0200", + "endTime": "20121212T160744+0200", + "activities": [ + { + "activity": "underground", + "group": "transport", + "manual": false, + "startTime": "20121212T153638+0200", + "endTime": "20121212T155321+0200", + "duration": 1003, + "distance": 8058 + }, + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T155322+0200", + "endTime": "20121212T160744+0200", + "duration": 862, + "distance": 1086, + "steps": 1257, + "calories": 99 + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T160744+0200", + "endTime": "20121212T232730+0200", + "place": { + "id": 1, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "lastUpdate": "20130317T121143Z" + } + ], + "caloriesIdle": 1785, + "lastUpdate": "20130317T121143Z" + } +] diff --git a/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily.json b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily.json new file mode 100644 index 00000000..d5b091b3 --- /dev/null +++ b/shim-server/src/test/resources/org/openmhealth/shim/moves/mapper/moves-user-storyline-daily.json @@ -0,0 +1,354 @@ +[ + { + "date": "20121212", + "summary": [ + { + "activity": "walking", + "group": "walking", + "duration": 3333, + "distance": 3333, + "steps": 3333, + "calories": 300 + }, + { + "activity": "walking_on_treadmill", + "group": "walking", + "duration": 270, + "steps": 303, + "calories": 30 + }, + { + "activity": "transport", + "group": "transport", + "duration": 1124, + "distance": 8443 + }, + { + "activity": "underground", + "group": "transport", + "duration": 1003, + "distance": 8058 + }, + { + "activity": "zumba", + "duration": 570, + "calories": 200 + } + ], + "segments": [ + { + "type": "place", + "startTime": "20121212T000000+0200", + "endTime": "20121212T071430+0200", + "place": { + "id": 1, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T071430+0200", + "endTime": "20121212T074617+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T071430+0200", + "endTime": "20121212T072732+0200", + "duration": 782, + "distance": 1251, + "steps": 1353, + "calories": 99, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T071430+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T072732+0200" + } + ] + }, + { + "activity": "transport", + "group": "transport", + "manual": false, + "startTime": "20121212T072732+0200", + "endTime": "20121212T074616+0200", + "duration": 1124, + "distance": 8443, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T072732+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T074208+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T074617+0200" + } + ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T074617+0200", + "endTime": "20121212T100051+0200", + "place": { + "id": 2, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + { + "activity": "walking_on_treadmill", + "group": "walking", + "manual": true, + "duration": 270, + "steps": 303, + "calories": 30, + "trackPoints": [ ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T100051+0200", + "endTime": "20121212T100715+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T100051+0200", + "endTime": "20121212T100715+0200", + "duration": 384, + "distance": 421, + "steps": 488, + "calories": 99, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T100051+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T100715+0200" + } + ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T100715+0200", + "endTime": "20121212T110530+0200", + "place": { + "id": 4, + "name": "test", + "type": "foursquare", + "foursquareId": "4df0fdb17d8ba370a011d24c", + "foursquareCategoryIds": [ + "4bf58dd8d48988d125941735" + ], + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T101215+0200", + "endTime": "20121212T101255+0200", + "duration": 40, + "distance": 18, + "steps": 37, + "calories": 99, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T101215+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T101255+0200" + } + ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T110530+0200", + "endTime": "20121212T111129+0200", + "activities": [ + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T110530+0200", + "endTime": "20121212T111128+0200", + "duration": 358, + "distance": 493, + "steps": 441, + "calories": 99, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T110531+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T110536+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T110947+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T111017+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T111129+0200" + } + ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T111129+0200", + "endTime": "20121212T153638+0200", + "place": { + "id": 2, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "activities": [ + { + "activity": "zumba", + "manual": true, + "duration": 570, + "calories": 200, + "trackPoints": [ ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "move", + "startTime": "20121212T153638+0200", + "endTime": "20121212T160744+0200", + "activities": [ + { + "activity": "underground", + "group": "transport", + "manual": false, + "startTime": "20121212T153638+0200", + "endTime": "20121212T155321+0200", + "duration": 1003, + "distance": 8058, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T153638+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T155322+0200" + } + ] + }, + { + "activity": "walking", + "group": "walking", + "manual": false, + "startTime": "20121212T155322+0200", + "endTime": "20121212T160744+0200", + "duration": 862, + "distance": 1086, + "steps": 1257, + "calories": 99, + "trackPoints": [ + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T155322+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T155402+0200" + }, + { + "lat": 55.55555, + "lon": 33.33333, + "time": "20121212T160744+0200" + } + ] + } + ], + "lastUpdate": "20130317T121143Z" + }, + { + "type": "place", + "startTime": "20121212T160744+0200", + "endTime": "20121212T232730+0200", + "place": { + "id": 1, + "type": "unknown", + "location": { + "lat": 55.55555, + "lon": 33.33333 + } + }, + "lastUpdate": "20130317T121143Z" + } + ], + "caloriesIdle": 1785, + "lastUpdate": "20130317T121143Z" + } +] diff --git a/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-body-measures.json b/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-body-measures.json index 82a32726..26a5f7b1 100644 --- a/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-body-measures.json +++ b/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-body-measures.json @@ -94,6 +94,11 @@ "type": 4, "unit": -3, "value": 1930 + }, + { + "type": 71, + "unit": 0, + "value": 37 } ] } diff --git a/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-sleep-summary.json b/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-sleep-summary.json index 53b3b0a2..4771eee9 100644 --- a/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-sleep-summary.json +++ b/shim-server/src/test/resources/org/openmhealth/shim/withings/mapper/withings-sleep-summary.json @@ -3,24 +3,38 @@ "body": { "series": [ { - "id": 16616514, - "timezone": "Europe/Paris", - "model": 32, - "startdate": 1410521659, - "enddate": 1410542577, - "date": "2014-09-11", + "id": 123, + "timezone": "Europe/London", + "model": 16, + "startdate": 1504307700, + "enddate": 1504319100, + "date": "2017-09-02", "data": { - "wakeupduration": 1800, - "lightsleepduration": 18540, - "deepsleepduration": 8460, - "remsleepduration": 10460, - "durationtosleep": 420, - "durationtowakeup": 360, - "wakeupcount": 3 + "wakeupduration": 0, + "lightsleepduration": 3360, + "deepsleepduration": 7800, + "wakeupcount": 4, + "durationtosleep": 240 }, - "modified": 1412087110 + "modified": 1504477126 + }, + { + "id": 124, + "timezone": "Europe/London", + "model": 16, + "startdate": 1504477140, + "enddate": 1504509540, + "date": "2017-09-04", + "data": { + "wakeupduration": 2700, + "lightsleepduration": 15900, + "deepsleepduration": 13800, + "wakeupcount": 0, + "durationtosleep": 0 + }, + "modified": 1504682107 } ], "more": false } -} \ No newline at end of file +} diff --git a/shim-server/src/test/resources/runkeeper-weight.json b/shim-server/src/test/resources/runkeeper-weight.json deleted file mode 100644 index abb32dd8..00000000 --- a/shim-server/src/test/resources/runkeeper-weight.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "items": [ - { - "timestamp": "Sat, 9 Aug 2014 04:46:47", - "weight": 81.6466265999547, - "fat_percent": 20, - "uri": "/weight/-29974334-1407559607706" - }, - { - "timestamp": "Sat, 2 Aug 2014 04:45:49", - "weight": 86.1825502999521, - "fat_percent": 20, - "uri": "/weight/-29974334-1406954749426" - } - ], - "size": 2 -} \ No newline at end of file diff --git a/shim-server/src/test/resources/withings-body.json b/shim-server/src/test/resources/withings-body.json deleted file mode 100644 index 0652bee6..00000000 --- a/shim-server/src/test/resources/withings-body.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "status": 0, - "body": { - "updatetime": 1408363201, - "measuregrps": [ - { - "grpid": 244644481, - "attrib": 2, - "date": 1408276657, - "category": 1, - "comment": "August yo.", - "measures": [ - { - "value": 75, - "type": 9, - "unit": 0 - }, - { - "value": 133, - "type": 10, - "unit": 0 - }, - { - "value": 55, - "type": 11, - "unit": 0 - } - ] - }, - { - "grpid": 244638321, - "attrib": 2, - "date": 1408275667, - "category": 1, - "comment": "August yo.", - "measures": [ - { - "value": 7257, - "type": 1, - "unit": -2 - } - ] - }, - { - "grpid": 244638640, - "attrib": 2, - "date": 1408189304, - "category": 1, - "measures": [ - { - "value": 78, - "type": 9, - "unit": 0 - }, - { - "value": 133, - "type": 10, - "unit": 0 - }, - { - "value": 100, - "type": 11, - "unit": 0 - } - ] - } - ] - } -} \ No newline at end of file diff --git a/shim-server/src/test/resources/withings-intraday-activity.json b/shim-server/src/test/resources/withings-intraday-activity.json deleted file mode 100644 index 44f1ac97..00000000 --- a/shim-server/src/test/resources/withings-intraday-activity.json +++ /dev/null @@ -1,519 +0,0 @@ -{ - "status": 0, - "body": { - "series": { - "1426550520": { - "steps": 28, - "elevation": 0, - "calories": 2, - "distance": 26, - "duration": 60 - }, - "1426550580": { - "steps": 5, - "elevation": 0, - "calories": 1, - "distance": 3, - "duration": 60 - }, - "1426554900": { - "steps": 5, - "elevation": 0, - "calories": 1, - "distance": 4, - "duration": 60 - }, - "1426555980": { - "steps": 81, - "elevation": 0, - "calories": 3, - "distance": 76, - "duration": 60 - }, - "1426556040": { - "steps": 114, - "elevation": 0, - "calories": 4, - "distance": 107, - "duration": 60 - }, - "1426556100": { - "steps": 33, - "elevation": 0, - "calories": 2, - "distance": 26, - "duration": 60 - }, - "1426556340": { - "steps": 83, - "elevation": 0, - "calories": 3, - "distance": 69, - "duration": 60 - }, - "1426556400": { - "steps": 98, - "elevation": 0, - "calories": 3, - "distance": 83, - "duration": 60 - }, - "1426556460": { - "steps": 89, - "elevation": 0, - "calories": 3, - "distance": 73, - "duration": 60 - }, - "1426556520": { - "steps": 77, - "elevation": 0, - "calories": 3, - "distance": 64, - "duration": 60 - }, - "1426556580": { - "steps": 82, - "elevation": 0, - "calories": 3, - "distance": 68, - "duration": 60 - }, - "1426556640": { - "steps": 95, - "elevation": 0, - "calories": 3, - "distance": 79, - "duration": 60 - }, - "1426556700": { - "steps": 96, - "elevation": 0, - "calories": 3, - "distance": 79, - "duration": 60 - }, - "1426556760": { - "steps": 96, - "elevation": 0, - "calories": 3, - "distance": 80, - "duration": 60 - }, - "1426556820": { - "steps": 94, - "elevation": 0, - "calories": 3, - "distance": 78, - "duration": 60 - }, - "1426556880": { - "steps": 94, - "elevation": 0, - "calories": 3, - "distance": 76, - "duration": 60 - }, - "1426556940": { - "steps": 97, - "elevation": 0, - "calories": 3, - "distance": 80, - "duration": 60 - }, - "1426557000": { - "steps": 95, - "elevation": 0, - "calories": 3, - "distance": 79, - "duration": 60 - }, - "1426557060": { - "steps": 91, - "elevation": 0, - "calories": 3, - "distance": 75, - "duration": 60 - }, - "1426557120": { - "steps": 82, - "elevation": 0, - "calories": 3, - "distance": 66, - "duration": 60 - }, - "1426557180": { - "steps": 99, - "elevation": 0, - "calories": 3, - "distance": 80, - "duration": 60 - }, - "1426557240": { - "steps": 75, - "elevation": 0, - "calories": 2, - "distance": 60, - "duration": 60 - }, - "1426557300": { - "steps": 45, - "elevation": 0, - "calories": 2, - "distance": 34, - "duration": 60 - }, - "1426557360": { - "steps": 22, - "elevation": 0, - "calories": 1, - "distance": 16, - "duration": 60 - }, - "1426557420": { - "steps": 23, - "elevation": 0, - "calories": 1, - "distance": 18, - "duration": 60 - }, - "1426557480": { - "steps": 76, - "elevation": 0, - "calories": 2, - "distance": 58, - "duration": 60 - }, - "1426557540": { - "steps": 80, - "elevation": 0, - "calories": 3, - "distance": 68, - "duration": 60 - }, - "1426557600": { - "steps": 88, - "elevation": 0, - "calories": 3, - "distance": 75, - "duration": 60 - }, - "1426557660": { - "steps": 88, - "elevation": 0, - "calories": 3, - "distance": 72, - "duration": 60 - }, - "1426557720": { - "steps": 79, - "elevation": 0, - "calories": 3, - "distance": 66, - "duration": 60 - }, - "1426557780": { - "steps": 80, - "elevation": 0, - "calories": 3, - "distance": 66, - "duration": 60 - }, - "1426557840": { - "steps": 62, - "elevation": 0, - "calories": 2, - "distance": 52, - "duration": 60 - }, - "1426560420": { - "steps": 5, - "elevation": 0, - "calories": 1, - "distance": 4, - "duration": 60 - }, - "1426560480": { - "steps": 6, - "elevation": 0, - "calories": 1, - "distance": 4, - "duration": 60 - }, - "1426560660": { - "steps": 72, - "elevation": 0, - "calories": 3, - "distance": 61, - "duration": 60 - }, - "1426560720": { - "steps": 90, - "elevation": 0, - "calories": 3, - "distance": 75, - "duration": 60 - }, - "1426566060": { - "steps": 26, - "elevation": 0, - "calories": 1, - "distance": 21, - "duration": 60 - }, - "1426566120": { - "steps": 86, - "elevation": 0, - "calories": 3, - "distance": 73, - "duration": 60 - }, - "1426566180": { - "steps": 41, - "elevation": 0, - "calories": 2, - "distance": 34, - "duration": 60 - }, - "1426568940": { - "steps": 39, - "elevation": 0, - "calories": 2, - "distance": 33, - "duration": 60 - }, - "1426569120": { - "steps": 5, - "elevation": 0, - "calories": 1, - "distance": 4, - "duration": 60 - }, - "1426569360": { - "steps": 17, - "elevation": 0, - "calories": 1, - "distance": 14, - "duration": 60 - }, - "1426570200": { - "steps": 74, - "elevation": 0, - "calories": 3, - "distance": 67, - "duration": 60 - }, - "1426570260": { - "steps": 75, - "elevation": 0, - "calories": 3, - "distance": 63, - "duration": 60 - }, - "1426570320": { - "steps": 91, - "elevation": 0, - "calories": 3, - "distance": 84, - "duration": 60 - }, - "1426570380": { - "steps": 100, - "elevation": 0, - "calories": 4, - "distance": 91, - "duration": 60 - }, - "1426570440": { - "steps": 70, - "elevation": 0, - "calories": 3, - "distance": 64, - "duration": 60 - }, - "1426570500": { - "steps": 82, - "elevation": 0, - "calories": 3, - "distance": 72, - "duration": 60 - }, - "1426570560": { - "steps": 79, - "elevation": 0, - "calories": 3, - "distance": 67, - "duration": 60 - }, - "1426570620": { - "steps": 56, - "elevation": 0, - "calories": 2, - "distance": 50, - "duration": 60 - }, - "1426570680": { - "steps": 41, - "elevation": 0, - "calories": 2, - "distance": 32, - "duration": 60 - }, - "1426570740": { - "steps": 86, - "elevation": 0, - "calories": 3, - "distance": 72, - "duration": 60 - }, - "1426570800": { - "steps": 75, - "elevation": 0, - "calories": 3, - "distance": 64, - "duration": 60 - }, - "1426570860": { - "steps": 81, - "elevation": 0, - "calories": 3, - "distance": 68, - "duration": 60 - }, - "1426570920": { - "steps": 42, - "elevation": 0, - "calories": 2, - "distance": 35, - "duration": 60 - }, - "1426570980": { - "steps": 109, - "elevation": 0, - "calories": 4, - "distance": 97, - "duration": 60 - }, - "1426571040": { - "steps": 104, - "elevation": 0, - "calories": 4, - "distance": 93, - "duration": 60 - }, - "1426571100": { - "steps": 74, - "elevation": 0, - "calories": 3, - "distance": 61, - "duration": 60 - }, - "1426571160": { - "steps": 72, - "elevation": 0, - "calories": 3, - "distance": 60, - "duration": 60 - }, - "1426571220": { - "steps": 87, - "elevation": 0, - "calories": 3, - "distance": 75, - "duration": 60 - }, - "1426571280": { - "steps": 93, - "elevation": 0, - "calories": 3, - "distance": 81, - "duration": 60 - }, - "1426571340": { - "steps": 85, - "elevation": 0, - "calories": 3, - "distance": 70, - "duration": 60 - }, - "1426571400": { - "steps": 82, - "elevation": 0, - "calories": 3, - "distance": 67, - "duration": 60 - }, - "1426571460": { - "steps": 98, - "elevation": 0, - "calories": 3, - "distance": 87, - "duration": 60 - }, - "1426571520": { - "steps": 97, - "elevation": 0, - "calories": 3, - "distance": 82, - "duration": 60 - }, - "1426571580": { - "steps": 71, - "elevation": 0, - "calories": 3, - "distance": 62, - "duration": 60 - }, - "1426571640": { - "steps": 6, - "elevation": 0, - "calories": 1, - "distance": 4, - "duration": 60 - }, - "1426571940": { - "steps": 5, - "elevation": 0, - "calories": 1, - "distance": 5, - "duration": 60 - }, - "1426572660": { - "steps": 6, - "elevation": 0, - "calories": 1, - "distance": 6, - "duration": 60 - }, - "1426572720": { - "steps": 8, - "elevation": 0, - "calories": 1, - "distance": 4, - "duration": 60 - }, - "1426604760": { - "steps": 14, - "elevation": 0, - "calories": 1, - "distance": 12, - "duration": 60 - }, - "1426550640": {"duration": 4260}, - "1426554960": {"duration": 300}, - "1426555260": {"duration": 720}, - "1426556160": {"duration": 180}, - "1426557900": {"duration": 2520}, - "1426560540": {"duration": 120}, - "1426560780": {"duration": 5280}, - "1426566240": {"duration": 2700}, - "1426569000": {"duration": 120}, - "1426569180": {"duration": 180}, - "1426569420": {"duration": 780}, - "1426571700": {"duration": 240}, - "1426572000": {"duration": 660}, - "1426572780": {"duration": 31980}, - "1426604820": {"duration": 32460} - } - } -} \ No newline at end of file