├── frontend └── build.gradle ├── mongoimport.sh ├── backend ├── src │ ├── main │ │ ├── resources │ │ │ ├── config │ │ │ │ └── application.yml │ │ │ ├── logback.xml │ │ │ └── application.yml │ │ └── java │ │ │ └── org │ │ │ └── openmhealth │ │ │ └── data │ │ │ └── generator │ │ │ ├── service │ │ │ ├── DataPointWritingService.java │ │ │ ├── TimestampedValueGroupGenerationService.java │ │ │ ├── DataPointGenerator.java │ │ │ ├── ConsoleDataPointWritingServiceImpl.java │ │ │ ├── HeartRateDataPointGenerator.java │ │ │ ├── BodyWeightDataPointGenerator.java │ │ │ ├── BodyHeightDataPointGenerator.java │ │ │ ├── BodyFatPercentageDataPointGenerator.java │ │ │ ├── AmbientTemperatureDataPointGenerator.java │ │ │ ├── BloodGlucoseDataPointGenerator.java │ │ │ ├── BodyTemperatureDataPointGenerator.java │ │ │ ├── SleepDurationDataPointGenerator.java │ │ │ ├── MinutesModerateActivityDataPointGenerator.java │ │ │ ├── BloodPressureDataPointGenerator.java │ │ │ ├── StepCountDataPointGenerator.java │ │ │ ├── PhysicalActivityDataPointGenerator.java │ │ │ ├── FileSystemDataPointWritingServiceImpl.java │ │ │ ├── TimestampedValueGroupGenerationServiceImpl.java │ │ │ └── AbstractDataPointGeneratorImpl.java │ │ │ ├── configuration │ │ │ ├── ValidationConfiguration.java │ │ │ ├── JacksonConfiguration.java │ │ │ └── DataGenerationSettings.java │ │ │ ├── converter │ │ │ ├── StringToOffsetDateTimeConverter.java │ │ │ └── StringToDurationConverter.java │ │ │ ├── domain │ │ │ ├── TimestampedValueGroup.java │ │ │ ├── BoundedRandomVariableTrend.java │ │ │ ├── MeasureGenerationRequest.java │ │ │ └── BoundedRandomVariable.java │ │ │ └── Application.java │ └── test │ │ └── java │ │ └── org │ │ └── openmhealth │ │ └── data │ │ └── generator │ │ ├── converter │ │ ├── StringToOffsetDateTimeConverterUnitTests.java │ │ └── StringToDurationConverterUnitTests.java │ │ └── service │ │ └── TimestampedValueGroupGenerationServiceUnitTests.java ├── docker │ └── Dockerfile └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── resources └── images │ ├── body-weight-no-variance.png │ ├── body-weight-with-variance.png │ ├── blood-pressure-with-variance.png │ ├── body-weight-no-variance-zoomed.png │ ├── body-weight-with-variance-zoomed.png │ └── body-weight-without-night-time-measures.png ├── .travis.yml ├── .gitignore ├── gradlew.bat ├── gradlew ├── LICENSE └── README.md /frontend/build.gradle: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mongoimport.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mongoimport -d omh -c $1 4 | -------------------------------------------------------------------------------- /backend/src/main/resources/config/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | main: 3 | show-banner: false -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'data-generator' 2 | 3 | include 'backend' 4 | 5 | include 'frontend' 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmhealth/sample-data-generator/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /resources/images/body-weight-no-variance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmhealth/sample-data-generator/HEAD/resources/images/body-weight-no-variance.png -------------------------------------------------------------------------------- /resources/images/body-weight-with-variance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmhealth/sample-data-generator/HEAD/resources/images/body-weight-with-variance.png -------------------------------------------------------------------------------- /resources/images/blood-pressure-with-variance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmhealth/sample-data-generator/HEAD/resources/images/blood-pressure-with-variance.png -------------------------------------------------------------------------------- /resources/images/body-weight-no-variance-zoomed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmhealth/sample-data-generator/HEAD/resources/images/body-weight-no-variance-zoomed.png -------------------------------------------------------------------------------- /resources/images/body-weight-with-variance-zoomed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmhealth/sample-data-generator/HEAD/resources/images/body-weight-with-variance-zoomed.png -------------------------------------------------------------------------------- /resources/images/body-weight-without-night-time-measures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmhealth/sample-data-generator/HEAD/resources/images/body-weight-without-night-time-measures.png -------------------------------------------------------------------------------- /backend/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | notifications: 5 | slack: 6 | secure: ff51FlaHHPoHpcnoegDmY95SCEXglMHWtRei6U3taORVWMYa7ZcL141b5MI2j4cJpj+aPdgZY2WlKG1VgXNwpVjHwClk8qqnOQxQ7dwOWO+LUahtOIvBqH0i2r5RIn6P5fdlmQ6SqAuxuigqhrbWOU19eD6I2GpSjz4IsbEilIQ= 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Mar 23 11:44:02 CET 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip 7 | -------------------------------------------------------------------------------- /backend/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8-jre 2 | MAINTAINER Emerson Farrugia 3 | 4 | ENV BASE_DIR /opt/omh-sample-data-generator 5 | ADD data-generator.jar $BASE_DIR/ 6 | 7 | # the configuration file will be read from a directory called 'mount', and output will be written to it 8 | RUN mkdir -p $BASE_DIR/mount 9 | WORKDIR $BASE_DIR/mount 10 | 11 | CMD /usr/bin/java -jar $BASE_DIR/data-generator.jar 12 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/DataPointWritingService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.schema.domain.omh.DataPoint; 20 | 21 | 22 | /** 23 | * @author Emerson Farrugia 24 | */ 25 | public interface DataPointWritingService { 26 | 27 | /** 28 | * @param dataPoints the data points to write 29 | * @return the number of data points that have been written 30 | * @throws Exception if an error occurred while writing data points 31 | */ 32 | long writeDataPoints(Iterable> dataPoints) throws Exception; 33 | } 34 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/configuration/ValidationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.configuration; 18 | 19 | import org.springframework.context.annotation.Bean; 20 | import org.springframework.context.annotation.Configuration; 21 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 22 | 23 | 24 | /** 25 | * @author Emerson Farrugia 26 | */ 27 | @Configuration 28 | public class ValidationConfiguration { 29 | 30 | @Bean 31 | public javax.validation.Validator localValidatorFactoryBean() { 32 | return new LocalValidatorFactoryBean(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/configuration/JacksonConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.configuration; 18 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | 23 | 24 | /** 25 | * @author Emerson Farrugia 26 | */ 27 | @Configuration 28 | public class JacksonConfiguration { 29 | 30 | /** 31 | * @return an {@link ObjectMapper} that matches schema conventions 32 | */ 33 | @Bean 34 | public ObjectMapper objectMapper() { 35 | return org.openmhealth.schema.configuration.JacksonConfiguration.newObjectMapper(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/TimestampedValueGroupGenerationService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.MeasureGenerationRequest; 20 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 21 | 22 | 23 | /** 24 | * A service that generates timestamped value groups. 25 | * 26 | * @author Emerson Farrugia 27 | */ 28 | public interface TimestampedValueGroupGenerationService { 29 | 30 | /** 31 | * @param request a request to generate measures 32 | * @return a list of timestamped value groups from which measures can be built 33 | */ 34 | Iterable generateValueGroups(MeasureGenerationRequest request); 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### OSX ### 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear on external disk 16 | .Spotlight-V100 17 | .Trashes 18 | 19 | # Directories potentially created on remote AFP share 20 | .AppleDB 21 | .AppleDesktop 22 | Network Trash Folder 23 | Temporary Items 24 | .apdisk 25 | 26 | 27 | ### Intellij ### 28 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 29 | 30 | ## Directory-based project format 31 | .idea/ 32 | *.iml 33 | # if you remove the above rule, at least ignore user-specific stuff: 34 | # .idea/workspace.xml 35 | # .idea/tasks.xml 36 | # .idea/dictionaries 37 | # and these sensitive or high-churn files: 38 | # .idea/dataSources.ids 39 | # .idea/dataSources.xml 40 | # .idea/sqlDataSources.xml 41 | # .idea/dynamic.xml 42 | # and, if using gradle:: 43 | # .idea/gradle.xml 44 | # .idea/libraries 45 | 46 | ## File-based project format 47 | *.ipr 48 | *.iws 49 | 50 | ## Additional for IntelliJ 51 | out/ 52 | 53 | 54 | ### Java ### 55 | *.class 56 | 57 | # Package Files # 58 | *.jar 59 | !gradle/wrapper/gradle-wrapper.jar 60 | *.war 61 | *.ear 62 | 63 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 64 | hs_err_pid* 65 | 66 | 67 | ### Gradle ### 68 | .gradle 69 | build/ 70 | 71 | # Ignore Gradle GUI config 72 | gradle-app.setting 73 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/converter/StringToOffsetDateTimeConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.converter; 18 | 19 | import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; 20 | import org.springframework.core.convert.converter.Converter; 21 | import org.springframework.stereotype.Component; 22 | 23 | import java.time.OffsetDateTime; 24 | 25 | 26 | /** 27 | * A converter that creates {@link OffsetDateTime} objects from strings. 28 | * 29 | * @author Emerson Farrugia 30 | */ 31 | @Component 32 | @ConfigurationPropertiesBinding 33 | public class StringToOffsetDateTimeConverter implements Converter { 34 | 35 | @Override 36 | public OffsetDateTime convert(String source) { 37 | 38 | if (source == null) { 39 | return null; 40 | } 41 | 42 | return OffsetDateTime.parse(source); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/DataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.DataPoint; 21 | import org.openmhealth.schema.domain.omh.Measure; 22 | 23 | import java.util.Set; 24 | 25 | 26 | /** 27 | * @author Emerson Farrugia 28 | */ 29 | public interface DataPointGenerator { 30 | 31 | /** 32 | * @return the name of this generator 33 | */ 34 | String getName(); 35 | 36 | /** 37 | * @return the set of value group keys required by this generator 38 | */ 39 | Set getRequiredValueGroupKeys(); 40 | 41 | /** 42 | * @return the set of value group keys supported by this generator 43 | */ 44 | Set getSupportedValueGroupKeys(); 45 | 46 | /** 47 | * @param valueGroups a list of value groups, where each value group corresponds to a data point 48 | * @return the list of generated data points 49 | */ 50 | Iterable> generateDataPoints(Iterable valueGroups); 51 | } 52 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/ConsoleDataPointWritingServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import org.openmhealth.schema.domain.omh.DataPoint; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.stereotype.Service; 23 | 24 | import java.io.IOException; 25 | 26 | 27 | /** 28 | * @author Emerson Farrugia 29 | */ 30 | @Service 31 | public class ConsoleDataPointWritingServiceImpl implements DataPointWritingService { 32 | 33 | @Autowired 34 | private ObjectMapper objectMapper; 35 | 36 | 37 | @Override 38 | public long writeDataPoints(Iterable> dataPoints) throws IOException { 39 | 40 | long written = 0; 41 | 42 | for (DataPoint dataPoint : dataPoints) { 43 | // trying to use objectMapper.writeValue(System.out) closes the output stream, so doing it this way instead 44 | String dataPointAsString = objectMapper.writeValueAsString(dataPoint); 45 | 46 | System.out.println(dataPointAsString); 47 | written++; 48 | } 49 | 50 | return written; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/domain/TimestampedValueGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.domain; 18 | 19 | import java.time.OffsetDateTime; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | 24 | /** 25 | * A group of real values sharing a common timestamp. 26 | * 27 | * @author Emerson Farrugia 28 | */ 29 | public class TimestampedValueGroup { 30 | 31 | private OffsetDateTime timestamp; 32 | private Map values = new HashMap<>(); 33 | 34 | /** 35 | * @return the timestamp common to the values in this group 36 | */ 37 | public OffsetDateTime getTimestamp() { 38 | return timestamp; 39 | } 40 | 41 | public void setTimestamp(OffsetDateTime timestamp) { 42 | this.timestamp = timestamp; 43 | } 44 | 45 | /** 46 | * @return the values as a map 47 | */ 48 | public Map getValues() { 49 | return values; 50 | } 51 | 52 | public void setValues(Map values) { 53 | this.values = values; 54 | } 55 | 56 | public void setValue(String key, Double value) { 57 | this.values.put(key, value); 58 | } 59 | 60 | public Double getValue(String key) { 61 | return this.values.get(key); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/HeartRateDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.HeartRate; 21 | import org.springframework.stereotype.Component; 22 | 23 | import java.util.Set; 24 | 25 | import static java.util.Collections.singleton; 26 | 27 | 28 | /** 29 | * @author Emerson Farrugia 30 | */ 31 | @Component 32 | public class HeartRateDataPointGenerator extends AbstractDataPointGeneratorImpl { 33 | 34 | public static final String RATE_KEY = "rate-in-beats-per-minute"; 35 | 36 | @Override 37 | public String getName() { 38 | return "heart-rate"; 39 | } 40 | 41 | @Override 42 | public Set getRequiredValueGroupKeys() { 43 | return singleton(RATE_KEY); 44 | } 45 | 46 | @Override 47 | public Set getSupportedValueGroupKeys() { 48 | return singleton(RATE_KEY); 49 | } 50 | 51 | @Override 52 | public HeartRate newMeasure(TimestampedValueGroup valueGroup) { 53 | 54 | return new HeartRate.Builder(valueGroup.getValue(RATE_KEY)) 55 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 56 | .build(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/converter/StringToDurationConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.converter; 18 | 19 | import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; 20 | import org.springframework.core.convert.converter.Converter; 21 | import org.springframework.stereotype.Component; 22 | 23 | import java.time.Duration; 24 | 25 | 26 | /** 27 | * A converter that creates {@link Duration} objects from strings. 28 | * 29 | *

30 | *

31 |  *    "PT20.345S" -- parses as "20.345 seconds"
32 |  *    "PT15M"     -- parses as "15 minutes" (where a minute is 60 seconds)
33 |  *    "PT10H"     -- parses as "10 hours" (where an hour is 3600 seconds)
34 |  *    "P2D"       -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
35 |  *    "P2DT3H4M"  -- parses as "2 days, 3 hours and 4 minutes"
36 |  * 
37 | * 38 | * @author Emerson Farrugia 39 | * @see {@link Duration#parse(CharSequence)} 40 | */ 41 | @Component 42 | @ConfigurationPropertiesBinding 43 | public class StringToDurationConverter implements Converter { 44 | 45 | @Override 46 | public Duration convert(String source) { 47 | 48 | if (source == null) { 49 | return null; 50 | } 51 | 52 | return Duration.parse(source); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /backend/src/test/java/org/openmhealth/data/generator/converter/StringToOffsetDateTimeConverterUnitTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.converter; 18 | 19 | import org.testng.annotations.Test; 20 | 21 | import java.time.OffsetDateTime; 22 | import java.time.ZoneOffset; 23 | import java.time.format.DateTimeParseException; 24 | 25 | import static org.hamcrest.CoreMatchers.*; 26 | import static org.hamcrest.MatcherAssert.assertThat; 27 | 28 | 29 | /** 30 | * @author Emerson Farrugia 31 | */ 32 | public class StringToOffsetDateTimeConverterUnitTests { 33 | 34 | private StringToOffsetDateTimeConverter converter = new StringToOffsetDateTimeConverter(); 35 | 36 | @Test 37 | public void convertShouldReturnNullOnUndefinedSource() throws Exception { 38 | 39 | assertThat(converter.convert(null), nullValue()); 40 | } 41 | 42 | @Test(expectedExceptions = DateTimeParseException.class) 43 | public void convertShouldThrowExceptionOnMalformedSource() throws Exception { 44 | 45 | converter.convert("sbcdo"); 46 | } 47 | 48 | @Test 49 | public void convertShouldReturnCorrectOffsetDateTimeOnValieSource() throws Exception { 50 | 51 | OffsetDateTime dateTime = converter.convert("2013-02-05T07:25:00Z"); 52 | 53 | assertThat(dateTime, notNullValue()); 54 | assertThat(dateTime, equalTo(OffsetDateTime.of(2013, 2, 5, 7, 25, 0, 0, ZoneOffset.UTC))); 55 | } 56 | } -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/BodyWeightDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.BodyWeight; 21 | import org.openmhealth.schema.domain.omh.MassUnitValue; 22 | import org.springframework.stereotype.Component; 23 | 24 | import java.util.Set; 25 | 26 | import static java.util.Collections.singleton; 27 | import static org.openmhealth.schema.domain.omh.MassUnit.KILOGRAM; 28 | 29 | 30 | /** 31 | * @author Emerson Farrugia 32 | */ 33 | @Component 34 | public class BodyWeightDataPointGenerator extends AbstractDataPointGeneratorImpl { 35 | 36 | public static final String WEIGHT_KEY = "weight-in-kg"; 37 | 38 | @Override 39 | public String getName() { 40 | return "body-weight"; 41 | } 42 | 43 | @Override 44 | public Set getRequiredValueGroupKeys() { 45 | return singleton(WEIGHT_KEY); 46 | } 47 | 48 | @Override 49 | public Set getSupportedValueGroupKeys() { 50 | return singleton(WEIGHT_KEY); 51 | } 52 | 53 | @Override 54 | public BodyWeight newMeasure(TimestampedValueGroup valueGroup) { 55 | 56 | return new BodyWeight.Builder(new MassUnitValue(KILOGRAM, valueGroup.getValue(WEIGHT_KEY))) 57 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 58 | .build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/BodyHeightDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.BodyHeight; 21 | import org.openmhealth.schema.domain.omh.LengthUnitValue; 22 | import org.springframework.stereotype.Component; 23 | 24 | import java.util.Set; 25 | 26 | import static java.util.Collections.singleton; 27 | import static org.openmhealth.schema.domain.omh.LengthUnit.METER; 28 | 29 | 30 | /** 31 | * @author Emerson Farrugia 32 | */ 33 | @Component 34 | public class BodyHeightDataPointGenerator extends AbstractDataPointGeneratorImpl { 35 | 36 | public static final String HEIGHT_KEY = "height-in-meters"; 37 | 38 | @Override 39 | public String getName() { 40 | return "body-height"; 41 | } 42 | 43 | @Override 44 | public Set getRequiredValueGroupKeys() { 45 | return singleton(HEIGHT_KEY); 46 | } 47 | 48 | @Override 49 | public Set getSupportedValueGroupKeys() { 50 | return singleton(HEIGHT_KEY); 51 | } 52 | 53 | @Override 54 | public BodyHeight newMeasure(TimestampedValueGroup valueGroup) { 55 | 56 | return new BodyHeight.Builder(new LengthUnitValue(METER, valueGroup.getValue(HEIGHT_KEY))) 57 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 58 | .build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/BodyFatPercentageDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.BodyFatPercentage; 21 | import org.openmhealth.schema.domain.omh.TypedUnitValue; 22 | import org.springframework.stereotype.Component; 23 | 24 | import java.util.Set; 25 | 26 | import static java.util.Collections.singleton; 27 | import static org.openmhealth.schema.domain.omh.PercentUnit.PERCENT; 28 | 29 | 30 | /** 31 | * @author Emerson Farrugia 32 | */ 33 | @Component 34 | public class BodyFatPercentageDataPointGenerator extends AbstractDataPointGeneratorImpl { 35 | 36 | public static final String FAT_PERCENTAGE_KEY = "percentage"; 37 | 38 | @Override 39 | public String getName() { 40 | return "body-fat-percentage"; 41 | } 42 | 43 | @Override 44 | public Set getRequiredValueGroupKeys() { 45 | return singleton(FAT_PERCENTAGE_KEY); 46 | } 47 | 48 | @Override 49 | public Set getSupportedValueGroupKeys() { 50 | return singleton(FAT_PERCENTAGE_KEY); 51 | } 52 | 53 | @Override 54 | public BodyFatPercentage newMeasure(TimestampedValueGroup valueGroup) { 55 | 56 | return new BodyFatPercentage.Builder(new TypedUnitValue<>(PERCENT, valueGroup.getValue(FAT_PERCENTAGE_KEY))) 57 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 58 | .build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/AmbientTemperatureDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.AmbientTemperature; 21 | import org.openmhealth.schema.domain.omh.TemperatureUnitValue; 22 | import org.springframework.stereotype.Component; 23 | 24 | import java.util.Set; 25 | 26 | import static java.util.Collections.singleton; 27 | import static org.openmhealth.schema.domain.omh.TemperatureUnit.CELSIUS; 28 | 29 | 30 | /** 31 | * @author Emerson Farrugia 32 | */ 33 | @Component 34 | public class AmbientTemperatureDataPointGenerator extends AbstractDataPointGeneratorImpl { 35 | 36 | public static final String TEMPERATURE_KEY = "temperature-in-c"; 37 | 38 | @Override 39 | public String getName() { 40 | return "ambient-temperature"; 41 | } 42 | 43 | @Override 44 | public Set getRequiredValueGroupKeys() { 45 | return singleton(TEMPERATURE_KEY); 46 | } 47 | 48 | @Override 49 | public Set getSupportedValueGroupKeys() { 50 | return singleton(TEMPERATURE_KEY); 51 | } 52 | 53 | @Override 54 | public AmbientTemperature newMeasure(TimestampedValueGroup valueGroup) { 55 | 56 | return new AmbientTemperature.Builder(new TemperatureUnitValue(CELSIUS, valueGroup.getValue(TEMPERATURE_KEY))) 57 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 58 | .build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/BloodGlucoseDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.BloodGlucose; 21 | import org.openmhealth.schema.domain.omh.TypedUnitValue; 22 | import org.springframework.stereotype.Component; 23 | 24 | import java.util.Set; 25 | 26 | import static java.util.Collections.singleton; 27 | import static org.openmhealth.schema.domain.omh.BloodGlucoseUnit.MILLIGRAMS_PER_DECILITER; 28 | 29 | 30 | /** 31 | * @author Emerson Farrugia 32 | */ 33 | @Component 34 | public class BloodGlucoseDataPointGenerator 35 | extends AbstractDataPointGeneratorImpl { 36 | 37 | public static final String GLUCOSE_KEY = "glucose-in-mg-per-dl"; 38 | 39 | @Override 40 | public String getName() { 41 | return "blood-glucose"; 42 | } 43 | 44 | @Override 45 | public Set getSupportedValueGroupKeys() { 46 | return singleton(GLUCOSE_KEY); 47 | } 48 | 49 | @Override 50 | public Set getRequiredValueGroupKeys() { 51 | return singleton(GLUCOSE_KEY); 52 | } 53 | 54 | @Override 55 | public BloodGlucose newMeasure(TimestampedValueGroup valueGroup) { 56 | 57 | // TODO set the specimen source once the SDK is updated to omh:blood-glucose:2.0 58 | return new BloodGlucose.Builder( 59 | new TypedUnitValue<>(MILLIGRAMS_PER_DECILITER, valueGroup.getValue(GLUCOSE_KEY))) 60 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 61 | .build(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/BodyTemperatureDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.BodyTemperature; 21 | import org.openmhealth.schema.domain.omh.TemperatureUnitValue; 22 | import org.springframework.stereotype.Component; 23 | 24 | import java.util.Set; 25 | 26 | import static java.util.Collections.singleton; 27 | import static org.openmhealth.schema.domain.omh.BodyTemperature.MeasurementLocation.ORAL; 28 | import static org.openmhealth.schema.domain.omh.TemperatureUnit.CELSIUS; 29 | 30 | 31 | /** 32 | * @author Emerson Farrugia 33 | */ 34 | @Component 35 | public class BodyTemperatureDataPointGenerator extends AbstractDataPointGeneratorImpl { 36 | 37 | public static final String TEMPERATURE_KEY = "temperature-in-c"; 38 | 39 | @Override 40 | public String getName() { 41 | return "body-temperature"; 42 | } 43 | 44 | @Override 45 | public Set getRequiredValueGroupKeys() { 46 | return singleton(TEMPERATURE_KEY); 47 | } 48 | 49 | @Override 50 | public Set getSupportedValueGroupKeys() { 51 | return singleton(TEMPERATURE_KEY); 52 | } 53 | 54 | @Override 55 | public BodyTemperature newMeasure(TimestampedValueGroup valueGroup) { 56 | 57 | return new BodyTemperature.Builder(new TemperatureUnitValue(CELSIUS, valueGroup.getValue(TEMPERATURE_KEY))) 58 | .setMeasurementLocation(ORAL) 59 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 60 | .build(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/SleepDurationDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.DurationUnitValue; 21 | import org.openmhealth.schema.domain.omh.SleepDuration; 22 | import org.springframework.stereotype.Component; 23 | 24 | import java.util.Set; 25 | 26 | import static java.util.Collections.singleton; 27 | import static org.openmhealth.schema.domain.omh.DurationUnit.HOUR; 28 | import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; 29 | 30 | 31 | /** 32 | * @author Emerson Farrugia 33 | */ 34 | @Component 35 | public class SleepDurationDataPointGenerator 36 | extends AbstractDataPointGeneratorImpl { 37 | 38 | public static final String DURATION_KEY = "duration-in-hours"; 39 | 40 | @Override 41 | public String getName() { 42 | return "sleep-duration"; 43 | } 44 | 45 | @Override 46 | public Set getRequiredValueGroupKeys() { 47 | return singleton(DURATION_KEY); 48 | } 49 | 50 | @Override 51 | public Set getSupportedValueGroupKeys() { 52 | return singleton(DURATION_KEY); 53 | } 54 | 55 | @Override 56 | public SleepDuration newMeasure(TimestampedValueGroup valueGroup) { 57 | 58 | DurationUnitValue duration = new DurationUnitValue(HOUR, valueGroup.getValue(DURATION_KEY)); 59 | 60 | SleepDuration.Builder builder = new SleepDuration.Builder(duration) 61 | .setEffectiveTimeFrame(ofStartDateTimeAndDuration(valueGroup.getTimestamp(), duration)); 62 | 63 | return builder.build(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /backend/src/test/java/org/openmhealth/data/generator/converter/StringToDurationConverterUnitTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.converter; 18 | 19 | import org.testng.annotations.Test; 20 | 21 | import java.time.Duration; 22 | import java.time.format.DateTimeParseException; 23 | 24 | import static org.hamcrest.CoreMatchers.equalTo; 25 | import static org.hamcrest.CoreMatchers.notNullValue; 26 | import static org.hamcrest.CoreMatchers.nullValue; 27 | import static org.hamcrest.MatcherAssert.assertThat; 28 | import static org.testng.Assert.*; 29 | 30 | /** 31 | * @author Emerson Farrugia 32 | */ 33 | public class StringToDurationConverterUnitTests { 34 | 35 | private StringToDurationConverter converter = new StringToDurationConverter(); 36 | 37 | @Test 38 | public void convertShouldReturnNullOnUndefinedSource() throws Exception { 39 | 40 | assertThat(converter.convert(null), nullValue()); 41 | } 42 | 43 | @Test(expectedExceptions = DateTimeParseException.class) 44 | public void convertShouldThrowExceptionOnMalformedSource() throws Exception { 45 | 46 | converter.convert("sbcdo"); 47 | } 48 | 49 | @Test 50 | public void convertShouldReturnCorrectDurationOnPositiveSource() throws Exception { 51 | 52 | Duration duration = converter.convert("PT3H4M"); 53 | 54 | assertThat(duration, notNullValue()); 55 | assertThat(duration, equalTo(Duration.ofMinutes(184))); 56 | } 57 | 58 | @Test 59 | public void convertShouldReturnCorrectDurationOnNegativeSource() throws Exception { 60 | 61 | Duration duration = converter.convert("PT-6H-3M"); 62 | 63 | assertThat(duration, notNullValue()); 64 | assertThat(duration, equalTo(Duration.ofMinutes(-363))); 65 | } 66 | } -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/MinutesModerateActivityDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import com.google.common.collect.Sets; 20 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 21 | import org.openmhealth.schema.domain.omh.DurationUnitValue; 22 | import org.openmhealth.schema.domain.omh.MinutesModerateActivity; 23 | import org.springframework.stereotype.Component; 24 | 25 | import java.util.Set; 26 | 27 | import static org.openmhealth.schema.domain.omh.DurationUnit.MINUTE; 28 | import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; 29 | 30 | 31 | /** 32 | * @author Emerson Farrugia 33 | */ 34 | @Component 35 | public class MinutesModerateActivityDataPointGenerator 36 | extends AbstractDataPointGeneratorImpl { 37 | 38 | public static final String MINUTES_KEY = "minutes"; 39 | 40 | @Override 41 | public String getName() { 42 | return "minutes-moderate-activity"; 43 | } 44 | 45 | @Override 46 | public Set getRequiredValueGroupKeys() { 47 | return Sets.newHashSet(MINUTES_KEY); 48 | } 49 | 50 | @Override 51 | public Set getSupportedValueGroupKeys() { 52 | return Sets.newHashSet(MINUTES_KEY); 53 | } 54 | 55 | @Override 56 | public MinutesModerateActivity newMeasure(TimestampedValueGroup valueGroup) { 57 | 58 | DurationUnitValue duration = new DurationUnitValue(MINUTE, valueGroup.getValue(MINUTES_KEY)); 59 | 60 | MinutesModerateActivity.Builder builder = new MinutesModerateActivity.Builder(duration) 61 | .setEffectiveTimeFrame(ofStartDateTimeAndDuration(valueGroup.getTimestamp(), duration)); 62 | 63 | return builder.build(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/BloodPressureDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import com.google.common.collect.Sets; 20 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 21 | import org.openmhealth.schema.domain.omh.BloodPressure; 22 | import org.openmhealth.schema.domain.omh.DiastolicBloodPressure; 23 | import org.openmhealth.schema.domain.omh.SystolicBloodPressure; 24 | import org.springframework.stereotype.Component; 25 | 26 | import java.util.Set; 27 | 28 | import static org.openmhealth.schema.domain.omh.BloodPressureUnit.MM_OF_MERCURY; 29 | 30 | 31 | /** 32 | * @author Emerson Farrugia 33 | */ 34 | @Component 35 | public class BloodPressureDataPointGenerator 36 | extends AbstractDataPointGeneratorImpl { 37 | 38 | public static final String SYSTOLIC_KEY = "systolic-in-mmhg"; 39 | public static final String DIASTOLIC_KEY = "diastolic-in-mmhg"; 40 | 41 | @Override 42 | public String getName() { 43 | return "blood-pressure"; 44 | } 45 | 46 | @Override 47 | public Set getSupportedValueGroupKeys() { 48 | return Sets.newHashSet(SYSTOLIC_KEY, DIASTOLIC_KEY); 49 | } 50 | 51 | @Override 52 | public Set getRequiredValueGroupKeys() { 53 | return Sets.newHashSet(SYSTOLIC_KEY, DIASTOLIC_KEY); 54 | } 55 | 56 | @Override 57 | public BloodPressure newMeasure(TimestampedValueGroup valueGroup) { 58 | 59 | return new BloodPressure.Builder( 60 | new SystolicBloodPressure(MM_OF_MERCURY, valueGroup.getValue(SYSTOLIC_KEY)), 61 | new DiastolicBloodPressure(MM_OF_MERCURY, valueGroup.getValue(DIASTOLIC_KEY))) 62 | .setEffectiveTimeFrame(valueGroup.getTimestamp()) 63 | .build(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/StepCountDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import com.google.common.collect.Sets; 20 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 21 | import org.openmhealth.schema.domain.omh.DurationUnitValue; 22 | import org.openmhealth.schema.domain.omh.StepCount; 23 | import org.springframework.stereotype.Service; 24 | 25 | import java.util.Set; 26 | 27 | import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; 28 | import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; 29 | 30 | 31 | /** 32 | * @author Emerson Farrugia 33 | */ 34 | @Service 35 | public class StepCountDataPointGenerator extends AbstractDataPointGeneratorImpl { 36 | 37 | public static final String STEPS_PER_MINUTE_KEY = "steps-per-minute"; 38 | public static final String DURATION_KEY = "duration-in-seconds"; 39 | 40 | @Override 41 | public String getName() { 42 | return "step-count"; 43 | } 44 | 45 | @Override 46 | public Set getRequiredValueGroupKeys() { 47 | return Sets.newHashSet(DURATION_KEY, STEPS_PER_MINUTE_KEY); 48 | } 49 | 50 | @Override 51 | public Set getSupportedValueGroupKeys() { 52 | return Sets.newHashSet(DURATION_KEY, STEPS_PER_MINUTE_KEY); 53 | } 54 | 55 | @Override 56 | public StepCount newMeasure(TimestampedValueGroup valueGroup) { 57 | 58 | DurationUnitValue duration = new DurationUnitValue(SECOND, valueGroup.getValue(DURATION_KEY)); 59 | double stepsPerMin = valueGroup.getValue(STEPS_PER_MINUTE_KEY); 60 | 61 | Double stepCount = stepsPerMin * duration.getValue().doubleValue() / 60.0; 62 | 63 | return new StepCount.Builder(stepCount.longValue()) 64 | .setEffectiveTimeFrame(ofStartDateTimeAndDuration(valueGroup.getTimestamp(), duration)) 65 | .build(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/PhysicalActivityDataPointGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import com.google.common.collect.Sets; 20 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 21 | import org.openmhealth.schema.domain.omh.DurationUnitValue; 22 | import org.openmhealth.schema.domain.omh.LengthUnitValue; 23 | import org.openmhealth.schema.domain.omh.PhysicalActivity; 24 | import org.springframework.stereotype.Component; 25 | 26 | import java.util.Set; 27 | 28 | import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; 29 | import static org.openmhealth.schema.domain.omh.LengthUnit.METER; 30 | import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration; 31 | 32 | 33 | /** 34 | * @author Emerson Farrugia 35 | */ 36 | @Component 37 | public class PhysicalActivityDataPointGenerator 38 | extends AbstractDataPointGeneratorImpl { 39 | 40 | public static final String DURATION_KEY = "duration-in-seconds"; 41 | public static final String DISTANCE_KEY = "distance-in-meters"; 42 | 43 | @Override 44 | public String getName() { 45 | return "physical-activity"; 46 | } 47 | 48 | @Override 49 | public Set getRequiredValueGroupKeys() { 50 | return Sets.newHashSet(DURATION_KEY); 51 | } 52 | 53 | @Override 54 | public Set getSupportedValueGroupKeys() { 55 | return Sets.newHashSet(DURATION_KEY, DISTANCE_KEY); 56 | } 57 | 58 | @Override 59 | public PhysicalActivity newMeasure(TimestampedValueGroup valueGroup) { 60 | 61 | DurationUnitValue duration = new DurationUnitValue(SECOND, valueGroup.getValue(DURATION_KEY)); 62 | 63 | PhysicalActivity.Builder builder = new PhysicalActivity.Builder("some activity") 64 | .setEffectiveTimeFrame(ofStartDateTimeAndDuration(valueGroup.getTimestamp(), duration)); 65 | 66 | if (valueGroup.getValue(DISTANCE_KEY) != null) { 67 | builder.setDistance(new LengthUnitValue(METER, valueGroup.getValue(DISTANCE_KEY))); 68 | } 69 | 70 | return builder.build(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/FileSystemDataPointWritingServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import org.openmhealth.schema.domain.omh.DataPoint; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.beans.factory.annotation.Value; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 24 | import org.springframework.context.annotation.Primary; 25 | import org.springframework.stereotype.Service; 26 | 27 | import javax.annotation.PostConstruct; 28 | import java.io.BufferedWriter; 29 | import java.io.FileWriter; 30 | import java.io.IOException; 31 | import java.nio.file.Files; 32 | import java.nio.file.Paths; 33 | 34 | 35 | /** 36 | * @author Emerson Farrugia 37 | */ 38 | @Service 39 | @Primary 40 | @ConditionalOnExpression("'${output.destination}' == 'file'") 41 | public class FileSystemDataPointWritingServiceImpl implements DataPointWritingService { 42 | 43 | @Value("${output.file.filename:output.json}") 44 | private String filename; 45 | 46 | @Value("${output.file.append:true}") 47 | private Boolean append; 48 | 49 | @Autowired 50 | private ObjectMapper objectMapper; 51 | 52 | @PostConstruct 53 | public void clearFile() throws IOException { 54 | 55 | if (!append) { 56 | Files.deleteIfExists(Paths.get(filename)); 57 | } 58 | } 59 | 60 | @Override 61 | public long writeDataPoints(Iterable> dataPoints) throws IOException { 62 | 63 | long written = 0; 64 | 65 | try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true))) { 66 | 67 | for (DataPoint dataPoint : dataPoints) { 68 | // this simplifies direct imports into MongoDB 69 | dataPoint.setAdditionalProperty("id", dataPoint.getHeader().getId()); 70 | 71 | String valueAsString = objectMapper.writeValueAsString(dataPoint); 72 | writer.write(valueAsString); 73 | writer.write("\n"); 74 | written++; 75 | } 76 | } 77 | 78 | return written; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/configuration/DataGenerationSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.configuration; 18 | 19 | import org.openmhealth.data.generator.domain.MeasureGenerationRequest; 20 | import org.springframework.boot.context.properties.ConfigurationProperties; 21 | import org.springframework.stereotype.Component; 22 | 23 | import java.time.Duration; 24 | import java.time.OffsetDateTime; 25 | import java.time.ZoneOffset; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | 30 | /** 31 | * @author Emerson Farrugia 32 | */ 33 | @Component 34 | @ConfigurationProperties("data") 35 | public class DataGenerationSettings { 36 | 37 | private OffsetDateTime startDateTime = OffsetDateTime.of(2014, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC); 38 | private OffsetDateTime endDateTime = OffsetDateTime.of(2015, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC); 39 | private Duration meanInterPointDuration = Duration.ofHours(24); 40 | private Boolean suppressNightTimeMeasures = false; 41 | private List measureGenerationRequests = new ArrayList<>(); 42 | 43 | public OffsetDateTime getStartDateTime() { 44 | return startDateTime; 45 | } 46 | 47 | public void setStartDateTime(OffsetDateTime startDateTime) { 48 | this.startDateTime = startDateTime; 49 | } 50 | 51 | public OffsetDateTime getEndDateTime() { 52 | return endDateTime; 53 | } 54 | 55 | public void setEndDateTime(OffsetDateTime endDateTime) { 56 | this.endDateTime = endDateTime; 57 | } 58 | 59 | public Duration getMeanInterPointDuration() { 60 | return meanInterPointDuration; 61 | } 62 | 63 | public void setMeanInterPointDuration(Duration meanInterPointDuration) { 64 | this.meanInterPointDuration = meanInterPointDuration; 65 | } 66 | 67 | public Boolean isSuppressNightTimeMeasures() { 68 | return suppressNightTimeMeasures; 69 | } 70 | 71 | public void setSuppressNightTimeMeasures(Boolean suppressNightTimeMeasures) { 72 | this.suppressNightTimeMeasures = suppressNightTimeMeasures; 73 | } 74 | 75 | public List getMeasureGenerationRequests() { 76 | return measureGenerationRequests; 77 | } 78 | 79 | public void setMeasureGenerationRequests(List measureGenerationRequests) { 80 | this.measureGenerationRequests = measureGenerationRequests; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /backend/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: "java" 18 | apply plugin: "spring-boot" 19 | 20 | buildscript { 21 | repositories { 22 | mavenLocal() 23 | jcenter() 24 | } 25 | 26 | ext { 27 | springBootVersion = "1.3.3.RELEASE" 28 | } 29 | 30 | dependencies { 31 | classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" 32 | } 33 | } 34 | 35 | repositories { 36 | mavenLocal() 37 | jcenter() 38 | } 39 | 40 | ext { 41 | javaVersion = 1.8 42 | omhSchemaSdkVersion = "1.0.5" 43 | } 44 | 45 | group = "org.openmhealth.generator" 46 | version = "1.1.0" 47 | 48 | jar { 49 | baseName = 'data-generator' 50 | } 51 | 52 | sourceCompatibility = javaVersion 53 | targetCompatibility = javaVersion 54 | 55 | 56 | dependencies { 57 | compile "org.apache.commons:commons-math3:3.5" 58 | compile "com.google.guava:guava:18.0" 59 | compile "com.fasterxml.jackson.core:jackson-annotations" 60 | compile "com.fasterxml.jackson.core:jackson-databind" 61 | compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" 62 | compile "com.fasterxml.jackson.datatype:jackson-datatype-jdk8" 63 | compile "org.openmhealth.schema:omh-schema-sdk:${omhSchemaSdkVersion}" 64 | compile "org.slf4j:slf4j-api" 65 | compile "org.springframework:spring-beans" 66 | compile "org.springframework.boot:spring-boot-autoconfigure" 67 | compile "org.springframework:spring-context" 68 | compile "javax.validation:validation-api:1.1.0.Final" 69 | 70 | testCompile "org.hamcrest:hamcrest-library" 71 | testCompile "org.mockito:mockito-core" 72 | testCompile "org.springframework:spring-test" 73 | testCompile "org.testng:testng:6.8.21" 74 | 75 | runtime "org.hibernate:hibernate-validator" 76 | runtime "javax.el:javax.el-api:3.0.0" 77 | runtime 'org.slf4j:jcl-over-slf4j' 78 | runtime 'org.slf4j:log4j-over-slf4j' 79 | runtime "ch.qos.logback:logback-classic" 80 | runtime "org.yaml:snakeyaml" 81 | } 82 | 83 | test { 84 | useTestNG() 85 | workingDir = '..' 86 | } 87 | 88 | task copyArchiveJarToDockerContext(dependsOn: assemble, type: Copy) { 89 | from 'build/libs' 90 | into 'docker' 91 | include "${jar.archiveName}" 92 | rename { String fileName -> 93 | fileName.replace("${jar.archiveName}", "${jar.baseName}.jar") 94 | } 95 | } 96 | build.dependsOn copyArchiveJarToDockerContext 97 | 98 | task deleteArchiveJarFromDockerContext(type: Delete) { 99 | delete "docker/${jar.baseName}.jar" 100 | } 101 | clean.dependsOn deleteArchiveJarFromDockerContext -------------------------------------------------------------------------------- /backend/src/test/java/org/openmhealth/data/generator/service/TimestampedValueGroupGenerationServiceUnitTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import com.google.common.collect.Iterables; 20 | import org.openmhealth.data.generator.domain.BoundedRandomVariable; 21 | import org.openmhealth.data.generator.domain.BoundedRandomVariableTrend; 22 | import org.openmhealth.data.generator.domain.MeasureGenerationRequest; 23 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 24 | import org.testng.annotations.Test; 25 | 26 | import java.time.Duration; 27 | import java.time.OffsetDateTime; 28 | 29 | import static org.hamcrest.MatcherAssert.assertThat; 30 | import static org.hamcrest.Matchers.greaterThanOrEqualTo; 31 | import static org.hamcrest.Matchers.lessThanOrEqualTo; 32 | 33 | 34 | /** 35 | * @author Emerson Farrugia 36 | */ 37 | public class TimestampedValueGroupGenerationServiceUnitTests { 38 | 39 | private TimestampedValueGroupGenerationService service = new TimestampedValueGroupGenerationServiceImpl(); 40 | 41 | 42 | @Test 43 | public void generateValueGroupsShouldWork() { 44 | 45 | String trendKey = "foo"; 46 | double minimumValue = 50d; 47 | double maximumValue = 90d; 48 | 49 | BoundedRandomVariable randomVariable = new BoundedRandomVariable(1.0, minimumValue, maximumValue); 50 | BoundedRandomVariableTrend randomVariableTrend = new BoundedRandomVariableTrend(randomVariable, 60d, 80d); 51 | 52 | OffsetDateTime startDateTime = OffsetDateTime.now().minusDays(10); 53 | OffsetDateTime endDateTime = OffsetDateTime.now(); 54 | 55 | MeasureGenerationRequest request = new MeasureGenerationRequest(); 56 | request.setStartDateTime(startDateTime); 57 | request.setEndDateTime(endDateTime); 58 | request.setMeanInterPointDuration(Duration.ofHours(6)); 59 | request.addTrend(trendKey, randomVariableTrend); 60 | 61 | Iterable valueGroups; 62 | 63 | do { 64 | valueGroups = service.generateValueGroups(request); 65 | } 66 | while (Iterables.size(valueGroups) == 0); // in the extremely unlikely case of an empty list 67 | 68 | for (TimestampedValueGroup valueGroup : valueGroups) { 69 | 70 | assertThat(valueGroup.getValue(trendKey), greaterThanOrEqualTo(minimumValue)); 71 | assertThat(valueGroup.getValue(trendKey), lessThanOrEqualTo(maximumValue)); 72 | 73 | long effectiveDateTime = valueGroup.getTimestamp().toEpochSecond(); 74 | assertThat(effectiveDateTime, greaterThanOrEqualTo(startDateTime.toEpochSecond())); 75 | assertThat(effectiveDateTime, lessThanOrEqualTo(endDateTime.toEpochSecond())); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/TimestampedValueGroupGenerationServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.apache.commons.math3.distribution.ExponentialDistribution; 20 | import org.openmhealth.data.generator.domain.BoundedRandomVariableTrend; 21 | import org.openmhealth.data.generator.domain.MeasureGenerationRequest; 22 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 23 | import org.springframework.stereotype.Service; 24 | 25 | import java.time.Duration; 26 | import java.time.OffsetDateTime; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | import static java.time.temporal.ChronoUnit.SECONDS; 32 | 33 | 34 | /** 35 | * @author Emerson Farrugia 36 | */ 37 | @Service 38 | public class TimestampedValueGroupGenerationServiceImpl implements TimestampedValueGroupGenerationService { 39 | 40 | public static final int NIGHT_TIME_START_HOUR = 23; 41 | public static final int NIGHT_TIME_END_HOUR = 6; 42 | 43 | 44 | @Override 45 | public Iterable generateValueGroups(MeasureGenerationRequest request) { 46 | 47 | ExponentialDistribution interPointDurationDistribution = 48 | new ExponentialDistribution(request.getMeanInterPointDuration().getSeconds()); 49 | 50 | long totalDurationInS = Duration.between(request.getStartDateTime(), request.getEndDateTime()).getSeconds(); 51 | 52 | OffsetDateTime effectiveDateTime = request.getStartDateTime(); 53 | List timestampedValueGroups = new ArrayList<>(); 54 | 55 | do { 56 | effectiveDateTime = effectiveDateTime.plus((long) interPointDurationDistribution.sample(), SECONDS); 57 | 58 | if (!effectiveDateTime.isBefore(request.getEndDateTime())) { 59 | break; 60 | } 61 | 62 | if (request.isSuppressNightTimeMeasures() != null && request.isSuppressNightTimeMeasures() && 63 | (effectiveDateTime.getHour() >= NIGHT_TIME_START_HOUR || 64 | effectiveDateTime.getHour() < NIGHT_TIME_END_HOUR)) { 65 | continue; 66 | } 67 | 68 | TimestampedValueGroup valueGroup = new TimestampedValueGroup(); 69 | valueGroup.setTimestamp(effectiveDateTime); 70 | 71 | double trendProgressFraction = (double) 72 | Duration.between(request.getStartDateTime(), effectiveDateTime).getSeconds() / totalDurationInS; 73 | 74 | for (Map.Entry trendEntry : request.getTrends().entrySet()) { 75 | 76 | String key = trendEntry.getKey(); 77 | BoundedRandomVariableTrend trend = trendEntry.getValue(); 78 | 79 | double value = trend.nextValue(trendProgressFraction); 80 | valueGroup.setValue(key, value); 81 | } 82 | 83 | timestampedValueGroups.add(valueGroup); 84 | } 85 | while (true); 86 | 87 | return timestampedValueGroups; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/domain/BoundedRandomVariableTrend.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.domain; 18 | 19 | import javax.validation.constraints.NotNull; 20 | 21 | import static com.google.common.base.Preconditions.checkArgument; 22 | import static com.google.common.base.Preconditions.checkNotNull; 23 | 24 | 25 | /** 26 | * A trend wraps a {@link BoundedRandomVariable} in order to provide values whose mean falls along a linear 27 | * interpolation from a start value to an end value. 28 | * 29 | * @author Emerson Farrugia 30 | */ 31 | // TODO refactor this into a container object that wraps a variable and a trend, allowing different variables and 32 | // trend types (linear, polynomial, spline, etc.) with multiple sample points instead of just start and end 33 | public class BoundedRandomVariableTrend { 34 | 35 | private BoundedRandomVariable variable = new BoundedRandomVariable(); 36 | private Double startValue; 37 | private Double endValue; 38 | 39 | public BoundedRandomVariableTrend() { 40 | } 41 | 42 | public BoundedRandomVariableTrend(BoundedRandomVariable variable, Double startValue, Double endValue) { 43 | 44 | checkNotNull(variable); 45 | checkNotNull(startValue); 46 | checkNotNull(endValue); 47 | 48 | this.variable = variable; 49 | this.startValue = startValue; 50 | this.endValue = endValue; 51 | } 52 | 53 | @NotNull 54 | public BoundedRandomVariable getVariable() { 55 | return variable; 56 | } 57 | 58 | public void setVariable(BoundedRandomVariable variable) { 59 | 60 | checkNotNull(variable); 61 | this.variable = variable; 62 | } 63 | 64 | /** 65 | * @return the value the trend starts with 66 | */ 67 | @NotNull 68 | public Double getStartValue() { 69 | return startValue; 70 | } 71 | 72 | public void setStartValue(Double startValue) { 73 | 74 | checkNotNull(startValue); 75 | this.startValue = startValue; 76 | } 77 | 78 | /** 79 | * @return the value the trend ends with 80 | */ 81 | @NotNull 82 | public Double getEndValue() { 83 | return endValue; 84 | } 85 | 86 | public void setEndValue(Double endValue) { 87 | 88 | checkNotNull(endValue); 89 | this.endValue = endValue; 90 | } 91 | 92 | /** 93 | * @param fraction a fraction of the range between the start value and the end value 94 | * @return the linear interpolation of the value at that fraction 95 | */ 96 | public Double interpolate(Double fraction) { 97 | 98 | checkArgument(fraction >= 0); 99 | checkArgument(fraction <= 1); 100 | 101 | return startValue + (endValue - startValue) * fraction; 102 | } 103 | 104 | /** 105 | * @param fraction a fraction of the range between the start value and the end value 106 | * @return a value generated by the bounded random variable when its mean is set to the value of the trend at the 107 | * given fraction 108 | */ 109 | public Double nextValue(Double fraction) { 110 | 111 | Double mean = interpolate(fraction); 112 | return variable.nextValue(mean); 113 | } 114 | 115 | // TODO remove these setters on a refactor, currently needed by SnakeYaml to support flat structure 116 | public void setStandardDeviation(Double standardDeviation) { 117 | variable.setStandardDeviation(standardDeviation); 118 | } 119 | 120 | public void setMinimumValue(Double minimumValue) { 121 | variable.setMinimumValue(minimumValue); 122 | } 123 | 124 | public void setMaximumValue(Double maximumValue) { 125 | variable.setMaximumValue(maximumValue); 126 | } 127 | 128 | @Override 129 | public String toString() { 130 | 131 | final StringBuilder sb = new StringBuilder("BoundedRandomVariableTrend{"); 132 | 133 | sb.append("variable=").append(variable); 134 | sb.append(", startValue=").append(startValue); 135 | sb.append(", endValue=").append(endValue); 136 | sb.append('}'); 137 | 138 | return sb.toString(); 139 | } 140 | } 141 | 142 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/domain/MeasureGenerationRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.domain; 18 | 19 | import javax.validation.Valid; 20 | import javax.validation.constraints.NotNull; 21 | import java.time.Duration; 22 | import java.time.OffsetDateTime; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | 27 | /** 28 | * A request to generate measures. 29 | * 30 | * @author Emerson Farrugia 31 | */ 32 | public class MeasureGenerationRequest { 33 | 34 | private String generatorName; 35 | private OffsetDateTime startDateTime; 36 | private OffsetDateTime endDateTime; 37 | private Duration meanInterPointDuration; 38 | private Boolean suppressNightTimeMeasures; 39 | private Map trends = new HashMap<>(); 40 | 41 | /** 42 | * @return the name of the measure generator to use 43 | */ 44 | @NotNull 45 | public String getGeneratorName() { 46 | return generatorName; 47 | } 48 | 49 | public void setGeneratorName(String generatorName) { 50 | this.generatorName = generatorName; 51 | } 52 | 53 | /** 54 | * An alias for {@link #setGeneratorName(String)} used by SnakeYAML. 55 | */ 56 | public void setGenerator(String generatorName) { 57 | setGeneratorName(generatorName); 58 | } 59 | 60 | /** 61 | * @return the earliest date time of the measures to generate. If the measures have time interval time frames, 62 | * the earliest time interval start time will be no earlier than this date time. 63 | */ 64 | @NotNull 65 | public OffsetDateTime getStartDateTime() { 66 | return startDateTime; 67 | } 68 | 69 | public void setStartDateTime(OffsetDateTime startDateTime) { 70 | this.startDateTime = startDateTime; 71 | } 72 | 73 | /** 74 | * @return the latest date time of the measures to generate. If the measures have time interval time frames, 75 | * the latest time interval start time will be no later than this date time. 76 | */ 77 | @NotNull 78 | public OffsetDateTime getEndDateTime() { 79 | return endDateTime; 80 | } 81 | 82 | public void setEndDateTime(OffsetDateTime endDateTime) { 83 | this.endDateTime = endDateTime; 84 | } 85 | 86 | /** 87 | * @return the mean duration between the effective time frames of consecutive measures 88 | */ 89 | @NotNull 90 | public Duration getMeanInterPointDuration() { 91 | return meanInterPointDuration; 92 | } 93 | 94 | public void setMeanInterPointDuration(Duration meanInterPointDuration) { 95 | this.meanInterPointDuration = meanInterPointDuration; 96 | } 97 | 98 | /** 99 | * @return true if measures having effective time frames at night should be suppressed, or false otherwise 100 | */ 101 | @NotNull 102 | public Boolean isSuppressNightTimeMeasures() { 103 | return suppressNightTimeMeasures; 104 | } 105 | 106 | public void setSuppressNightTimeMeasures(Boolean suppressNightTimeMeasures) { 107 | this.suppressNightTimeMeasures = suppressNightTimeMeasures; 108 | } 109 | 110 | /** 111 | * @return a map of trends to be generated 112 | */ 113 | @Valid 114 | @NotNull 115 | public Map getTrends() { 116 | return trends; 117 | } 118 | 119 | public void setTrends(Map trends) { 120 | this.trends = trends; 121 | } 122 | 123 | public void addTrend(String key, BoundedRandomVariableTrend trend) { 124 | this.trends.put(key, trend); 125 | } 126 | 127 | @Override 128 | public String toString() { 129 | 130 | final StringBuilder sb = new StringBuilder("MeasureGenerationRequest{"); 131 | 132 | sb.append("generatorName='").append(generatorName).append('\''); 133 | sb.append(", startDateTime=").append(startDateTime); 134 | sb.append(", endDateTime=").append(endDateTime); 135 | sb.append(", meanInterPointDuration=").append(meanInterPointDuration); 136 | sb.append(", suppressNightTimeMeasures=").append(suppressNightTimeMeasures); 137 | sb.append(", trends=").append(trends); 138 | sb.append('}'); 139 | 140 | return sb.toString(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/domain/BoundedRandomVariable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.domain; 18 | 19 | import javax.validation.constraints.Min; 20 | import javax.validation.constraints.NotNull; 21 | import java.security.SecureRandom; 22 | import java.util.Random; 23 | 24 | import static com.google.common.base.Preconditions.checkArgument; 25 | import static com.google.common.base.Preconditions.checkNotNull; 26 | 27 | 28 | /** 29 | * A random variable that models normally-distributed real values. If limits are specified, returned values will 30 | * fall between the specified bounds. 31 | * 32 | * @author Emerson Farrugia 33 | */ 34 | // TODO refactor this into a container object that wraps a variable and a trend 35 | public class BoundedRandomVariable { 36 | 37 | private static final Random prng = new SecureRandom(); 38 | 39 | private Double variance = 0.0; 40 | private Double standardDeviation = 0.0; 41 | private Double minimumValue; 42 | private Double maximumValue; 43 | 44 | 45 | public BoundedRandomVariable() { 46 | } 47 | 48 | public BoundedRandomVariable(Double standardDeviation) { 49 | 50 | checkNotNull(standardDeviation); 51 | setStandardDeviation(standardDeviation); 52 | } 53 | 54 | public BoundedRandomVariable(Double standardDeviation, Double minimumValue, Double maximumValue) { 55 | 56 | checkNotNull(standardDeviation); 57 | checkNotNull(minimumValue); 58 | checkNotNull(maximumValue); 59 | checkArgument(minimumValue <= maximumValue); 60 | 61 | setStandardDeviation(standardDeviation); 62 | setMinimumValue(minimumValue); 63 | setMaximumValue(maximumValue); 64 | } 65 | 66 | @NotNull 67 | @Min(0) 68 | public Double getVariance() { 69 | return variance; 70 | } 71 | 72 | public void setVariance(Double variance) { 73 | 74 | checkNotNull(variance); 75 | checkArgument(variance >= 0); 76 | 77 | this.variance = variance; 78 | this.standardDeviation = Math.sqrt(variance); 79 | } 80 | 81 | @NotNull 82 | @Min(0) 83 | public Double getStandardDeviation() { 84 | return standardDeviation; 85 | } 86 | 87 | public void setStandardDeviation(Double standardDeviation) { 88 | 89 | checkNotNull(standardDeviation); 90 | checkArgument(standardDeviation >= 0); 91 | 92 | this.standardDeviation = standardDeviation; 93 | this.variance = Math.pow(standardDeviation, 2.0); 94 | } 95 | 96 | /** 97 | * @return a lower bound on the values that can be generated 98 | */ 99 | public Double getMinimumValue() { 100 | return minimumValue; 101 | } 102 | 103 | public void setMinimumValue(Double minimumValue) { 104 | 105 | checkArgument(minimumValue == null || maximumValue == null || minimumValue <= maximumValue); 106 | 107 | this.minimumValue = minimumValue; 108 | } 109 | 110 | /** 111 | * @return an upper bound on the values that can be generated 112 | */ 113 | public Double getMaximumValue() { 114 | return maximumValue; 115 | } 116 | 117 | public void setMaximumValue(Double maximumValue) { 118 | 119 | checkArgument(minimumValue == null || maximumValue == null || minimumValue <= maximumValue); 120 | 121 | this.maximumValue = maximumValue; 122 | } 123 | 124 | /** 125 | * @param mean the mean of the random variable 126 | * @return the next value generated by the random variable 127 | */ 128 | public Double nextValue(Double mean) { 129 | 130 | Double nextValue; 131 | 132 | do { 133 | nextValue = prng.nextGaussian() * standardDeviation + mean; 134 | 135 | if ((minimumValue == null || nextValue >= minimumValue) && 136 | (maximumValue == null || nextValue <= maximumValue)) { 137 | break; 138 | } 139 | } 140 | while (true); 141 | 142 | return nextValue; 143 | } 144 | 145 | @Override 146 | public String toString() { 147 | 148 | final StringBuilder sb = new StringBuilder("BoundedRandomVariable{"); 149 | 150 | sb.append("variance=").append(variance); 151 | sb.append(", standardDeviation=").append(standardDeviation); 152 | sb.append(", minimumValue=").append(minimumValue); 153 | sb.append(", maximumValue=").append(maximumValue); 154 | sb.append('}'); 155 | 156 | return sb.toString(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/service/AbstractDataPointGeneratorImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator.service; 18 | 19 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 20 | import org.openmhealth.schema.domain.omh.*; 21 | import org.springframework.beans.factory.annotation.Value; 22 | 23 | import java.time.OffsetDateTime; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.function.BiFunction; 27 | 28 | import static java.util.UUID.randomUUID; 29 | import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; 30 | 31 | 32 | /** 33 | * @author Emerson Farrugia 34 | */ 35 | public abstract class AbstractDataPointGeneratorImpl 36 | implements DataPointGenerator { 37 | 38 | @Value("${data.header.user-id:some-user}") 39 | private String userId; 40 | 41 | @Value("${data.header.acquisition-provenance.source-name:generator}") 42 | private String sourceName; 43 | 44 | 45 | @Override 46 | public Iterable> generateDataPoints(Iterable valueGroups) { 47 | 48 | List> dataPoints = new ArrayList<>(); 49 | 50 | for (TimestampedValueGroup timestampedValueGroup : valueGroups) { 51 | dataPoints.add(newDataPoint(newMeasure(timestampedValueGroup))); 52 | } 53 | 54 | return dataPoints; 55 | } 56 | 57 | /** 58 | * @param valueGroup a group of values 59 | * @return a measure corresponding to the specified values 60 | */ 61 | public abstract T newMeasure(TimestampedValueGroup valueGroup); 62 | 63 | /** 64 | * @param measure a measure 65 | * @return a data point corresponding to the specified measure 66 | */ 67 | public DataPoint newDataPoint(T measure) { 68 | 69 | TimeInterval effectiveTimeInterval = measure.getEffectiveTimeFrame().getTimeInterval(); 70 | OffsetDateTime effectiveEndDateTime; 71 | 72 | if (effectiveTimeInterval != null) { 73 | if (effectiveTimeInterval.getEndDateTime() != null) { 74 | effectiveEndDateTime = effectiveTimeInterval.getEndDateTime(); 75 | } 76 | else { 77 | // use the duration to calculate the end date time of the interval 78 | if (effectiveTimeInterval.getDuration() != null) { 79 | 80 | BiFunction plusFunction; 81 | 82 | switch (effectiveTimeInterval.getDuration().getTypedUnit()) { 83 | 84 | case SECOND: 85 | plusFunction = OffsetDateTime::plusSeconds; 86 | break; 87 | case MINUTE: 88 | plusFunction = OffsetDateTime::plusMinutes; 89 | break; 90 | case HOUR: 91 | plusFunction = OffsetDateTime::plusHours; 92 | break; 93 | case DAY: 94 | plusFunction = OffsetDateTime::plusDays; 95 | break; 96 | case WEEK: 97 | plusFunction = OffsetDateTime::plusWeeks; 98 | break; 99 | case MONTH: 100 | plusFunction = OffsetDateTime::plusMonths; 101 | break; 102 | case YEAR: 103 | plusFunction = OffsetDateTime::plusYears; 104 | break; 105 | default: 106 | throw new IllegalStateException("A time interval duration type isn't supported."); 107 | } 108 | 109 | effectiveEndDateTime = plusFunction.apply(effectiveTimeInterval.getStartDateTime(), 110 | effectiveTimeInterval.getDuration().getValue().longValue()); 111 | } 112 | else { 113 | throw new IllegalStateException("An end date time can't be calculated without a duration."); 114 | } 115 | } 116 | } 117 | else { // if this is a point in time measure 118 | effectiveEndDateTime = measure.getEffectiveTimeFrame().getDateTime(); 119 | } 120 | 121 | DataPointAcquisitionProvenance acquisitionProvenance = 122 | new DataPointAcquisitionProvenance.Builder(sourceName) 123 | .setModality(SENSED) 124 | .setSourceCreationDateTime(effectiveEndDateTime) 125 | .build(); 126 | 127 | DataPointHeader header = 128 | new DataPointHeader.Builder(randomUUID().toString(), measure.getSchemaId(), 129 | effectiveEndDateTime.plusMinutes(1)) 130 | .setAcquisitionProvenance(acquisitionProvenance) 131 | .setUserId(userId) 132 | .build(); 133 | 134 | return new DataPoint<>(header, measure); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /backend/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | output: 2 | # whether to write data points to the "console" or to a "file", defaults to "console" 3 | destination: console 4 | file: 5 | # the file to write the data points to, defaults to "output.json" 6 | filename: output.json 7 | # true if the file should be appended to, false if it should be overwritten, defaults to true 8 | append: true 9 | 10 | data: 11 | header: 12 | # the user to associate the data points with, defaults to "some-user" 13 | user-id: some-user 14 | 15 | acquisition-provenance: 16 | # the name of the source of the data points, defaults to "generator" 17 | source-name: generator 18 | 19 | # data is generated according to these optional default values unless overridden in the measures 20 | # the earliest date time of the measures to generate, defaults to January 1st, 2014 at noon UTC 21 | start-date-time: 2014-01-01T12:00:00Z 22 | 23 | # the latest date time of the measures to generate, defaults to January 1st, 2015 at noon UTC 24 | end-date-time: 2015-01-01T12:00:00Z 25 | 26 | # the mean duration between the effective time frames of consecutive measures, defaults to 24 hours 27 | # this is specified in ISO8601 duration format, see https://www.ietf.org/rfc/rfc3339.txt page 12 28 | mean-inter-point-duration: PT24h 29 | 30 | # true if measures having effective time frames at night should be suppressed, defaults to false 31 | # a measure is considered to occur at night if its effective time frame is after 23:00 or before 6:00 32 | suppress-night-time-measures: false 33 | 34 | # a list of measure generation requests, with the following structure 35 | # 36 | # - generator: body-weight # the name of the measure generator to use, as defined by the generator 37 | # trends: # a map of linear trends and trend definitions 38 | # ? weight-in-kg # a trend key, as defined by the generator 39 | # : start-date-time: ... # override for the defaults listed above 40 | # end-date-time: ... 41 | # mean-inter-point-duration: ... 42 | # suppress-night-time-measures: ... 43 | # start-value: 55 # the value the trend starts with 44 | # end-value: 60 # the value the trend ends with 45 | # minimum-value: 50 # a lower bound on the value, default none 46 | # maximum-value: 65 # an upper bound on the value, default none 47 | # standard-deviation: 0.1 # the standard deviation of the value from the interpolated mean, default 0 48 | # 49 | # see the documentation at https://github.com/openmhealth/sample-data-generator for more information 50 | # by default, no data is generated; uncomment and modify the following configuration as needed 51 | 52 | # measure-generation-requests: 53 | # - generator: blood-pressure 54 | # trends: 55 | # ? systolic-in-mmhg 56 | # : start-value: 110 57 | # end-value: 125 58 | # minimum-value: 100 59 | # maximum-value: 140 60 | # standard-deviation: 3 61 | # ? diastolic-in-mmhg 62 | # : start-value: 70 63 | # end-value: 80 64 | # minimum-value: 60 65 | # maximum-value: 90 66 | # standard-deviation: 3 67 | # 68 | # - generator: body-weight 69 | # trends: 70 | # ? weight-in-kg 71 | # : start-value: 55 72 | # end-value: 60 73 | # minimum-value: 50 74 | # maximum-value: 65 75 | # standard-deviation: 0.1 76 | # 77 | # - generator: body-height 78 | # trends: 79 | # ? height-in-meters 80 | # : start-value: 1.75 81 | # end-value: 1.75 82 | # minimum-value: 1.745 83 | # maximum-value: 1.755 84 | # standard-deviation: 0 85 | # 86 | # - generator: step-count 87 | # mean-inter-point-duration: PT60m 88 | # suppress-night-time-measures: true 89 | # trends: 90 | # ? duration-in-seconds 91 | # : start-value: 300 92 | # end-value: 150 93 | # minimum-value: 10 94 | # maximum-value: 1800 95 | # standard-deviation: 50 96 | # ? steps-per-minute 97 | # : start-value: 90 98 | # end-value: 90 99 | # minimum-value: 30 100 | # maximum-value: 125 101 | # standard-deviation: 15 102 | # 103 | # - generator: physical-activity 104 | # trends: 105 | # ? duration-in-seconds 106 | # : start-value: 1800 107 | # end-value: 1800 108 | # minimum-value: 300 109 | # maximum-value: 2400 110 | # standard-deviation: 600 111 | # ? distance-in-meters 112 | # : start-value: 5000 113 | # end-value: 5000 114 | # minimum-value: 500 115 | # maximum-value: 20000 116 | # standard-deviation: 1000 117 | # 118 | # - generator: heart-rate 119 | # trends: 120 | # ? rate-in-beats-per-minute 121 | # : start-value: 80 122 | # end-value: 80 123 | # minimum-value: 60 124 | # maximum-value: 200 125 | # standard-deviation: 10 126 | # 127 | # - generator: minutes-moderate-activity 128 | # trends: 129 | # ? minutes 130 | # : start-value: 30 131 | # end-value: 30 132 | # minimum-value: 5 133 | # maximum-value: 120 134 | # standard-deviation: 20 135 | # 136 | # - generator: body-fat-percentage 137 | # trends: 138 | # ? percentage 139 | # : start-value: 30 140 | # end-value: 30 141 | # minimum-value: 28 142 | # maximum-value: 32 143 | # standard-deviation: 0.25 144 | # 145 | # - generator: ambient-temperature 146 | # trends: 147 | # ? temperature-in-c 148 | # : start-value: 22 149 | # end-value: 22 150 | # minimum-value: 18 151 | # maximum-value: 26 152 | # standard-deviation: 1 153 | # 154 | # - generator: blood-glucose 155 | # trends: 156 | # ? glucose-in-mg-per-dl 157 | # : start-value: 90 158 | # end-value: 100 159 | # minimum-value: 85 160 | # maximum-value: 140 161 | # standard-deviation: 5 162 | # 163 | # - generator: body-temperature 164 | # trends: 165 | # ? temperature-in-c 166 | # : start-value: 36.8 167 | # end-value: 36.8 168 | # minimum-value: 36.4 169 | # maximum-value: 38 170 | # standard-deviation: 0.3 171 | # 172 | # - generator: sleep-duration 173 | # trends: 174 | # ? duration-in-hours 175 | # : start-value: 8 176 | # end-value: 8 177 | # minimum-value: 4 178 | # maximum-value: 12 179 | # standard-deviation: 0.75 180 | -------------------------------------------------------------------------------- /backend/src/main/java/org/openmhealth/data/generator/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Open mHealth 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.openmhealth.data.generator; 18 | 19 | import com.google.common.base.Joiner; 20 | import com.google.common.collect.Sets; 21 | import org.openmhealth.data.generator.configuration.DataGenerationSettings; 22 | import org.openmhealth.data.generator.domain.MeasureGenerationRequest; 23 | import org.openmhealth.data.generator.domain.TimestampedValueGroup; 24 | import org.openmhealth.data.generator.service.DataPointGenerator; 25 | import org.openmhealth.data.generator.service.DataPointWritingService; 26 | import org.openmhealth.data.generator.service.TimestampedValueGroupGenerationService; 27 | import org.openmhealth.schema.domain.omh.DataPoint; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.boot.SpringApplication; 32 | import org.springframework.boot.autoconfigure.SpringBootApplication; 33 | import org.springframework.context.ConfigurableApplicationContext; 34 | 35 | import javax.annotation.PostConstruct; 36 | import javax.validation.ConstraintViolation; 37 | import javax.validation.Validator; 38 | import java.util.*; 39 | 40 | 41 | /** 42 | * This application loads a data generation fixture from application.yml and creates data points according to that 43 | * fixture. The data points are then either written to the console or to a file, as configured. 44 | * 45 | * @author Emerson Farrugia 46 | */ 47 | @SpringBootApplication 48 | public class Application { 49 | 50 | private static final Logger log = LoggerFactory.getLogger(Application.class); 51 | 52 | @Autowired 53 | private DataGenerationSettings dataGenerationSettings; 54 | 55 | @Autowired 56 | private Validator validator; 57 | 58 | @Autowired 59 | private List dataPointGenerators = new ArrayList<>(); 60 | 61 | @Autowired 62 | private TimestampedValueGroupGenerationService valueGroupGenerationService; 63 | 64 | @Autowired 65 | private DataPointWritingService dataPointWritingService; 66 | 67 | private Map> dataPointGeneratorMap = new HashMap<>(); 68 | 69 | 70 | public static void main(String[] args) throws Exception { 71 | 72 | ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args); 73 | 74 | Application application = applicationContext.getBean(Application.class); 75 | application.run(); 76 | } 77 | 78 | @PostConstruct 79 | public void initializeDataPointGenerators() { 80 | 81 | for (DataPointGenerator generator : dataPointGenerators) { 82 | dataPointGeneratorMap.put(generator.getName(), generator); 83 | } 84 | } 85 | 86 | public void run() throws Exception { 87 | 88 | setMeasureGenerationRequestDefaults(); 89 | 90 | if (!areMeasureGenerationRequestsValid()) { 91 | return; 92 | } 93 | 94 | long totalWritten = 0; 95 | 96 | for (MeasureGenerationRequest request : dataGenerationSettings.getMeasureGenerationRequests()) { 97 | 98 | Iterable valueGroups = valueGroupGenerationService.generateValueGroups(request); 99 | DataPointGenerator dataPointGenerator = dataPointGeneratorMap.get(request.getGeneratorName()); 100 | 101 | Iterable> dataPoints = dataPointGenerator.generateDataPoints(valueGroups); 102 | 103 | long written = dataPointWritingService.writeDataPoints(dataPoints); 104 | totalWritten += written; 105 | 106 | log.info("The '{}' generator has written {} data point(s).", dataPointGenerator.getName(), written); 107 | } 108 | 109 | log.info("A total of {} data point(s) have been written.", totalWritten); 110 | } 111 | 112 | 113 | private void setMeasureGenerationRequestDefaults() { 114 | 115 | for (MeasureGenerationRequest request : dataGenerationSettings.getMeasureGenerationRequests()) { 116 | 117 | if (request.getStartDateTime() == null) { 118 | request.setStartDateTime(dataGenerationSettings.getStartDateTime()); 119 | } 120 | 121 | if (request.getEndDateTime() == null) { 122 | request.setEndDateTime(dataGenerationSettings.getEndDateTime()); 123 | } 124 | 125 | if (request.getMeanInterPointDuration() == null) { 126 | request.setMeanInterPointDuration(dataGenerationSettings.getMeanInterPointDuration()); 127 | } 128 | 129 | if (request.isSuppressNightTimeMeasures() == null) { 130 | request.setSuppressNightTimeMeasures(dataGenerationSettings.isSuppressNightTimeMeasures()); 131 | } 132 | } 133 | } 134 | 135 | /** 136 | * @return true if the requests are valid, false otherwise 137 | */ 138 | private boolean areMeasureGenerationRequestsValid() { 139 | 140 | List requests = dataGenerationSettings.getMeasureGenerationRequests(); 141 | Joiner joiner = Joiner.on(", "); 142 | 143 | for (int i = 0; i < requests.size(); i++) { 144 | MeasureGenerationRequest request = requests.get(i); 145 | 146 | Set> constraintViolations = validator.validate(request); 147 | 148 | if (!constraintViolations.isEmpty()) { 149 | log.error("The measure generation request with index {} is not valid.", i); 150 | log.error(request.toString()); 151 | log.error(constraintViolations.toString()); 152 | return false; 153 | } 154 | 155 | if (!dataPointGeneratorMap.containsKey(request.getGeneratorName())) { 156 | log.error("The data generator '{}' in request {} doesn't exist.", request.getGeneratorName(), i); 157 | log.error(request.toString()); 158 | log.error("The allowed data generators are: {}", joiner.join(dataPointGeneratorMap.keySet())); 159 | return false; 160 | } 161 | 162 | DataPointGenerator generator = dataPointGeneratorMap.get(request.getGeneratorName()); 163 | 164 | Set specifiedTrendKeys = request.getTrends().keySet(); 165 | Set requiredTrendKeys = generator.getRequiredValueGroupKeys(); 166 | 167 | if (!specifiedTrendKeys.containsAll(requiredTrendKeys)) { 168 | log.error("Request {} for generator '{}' is missing required trend keys.", i, generator.getName()); 169 | log.error("The generator requires the following missing keys: {}.", 170 | joiner.join(Sets.difference(requiredTrendKeys, specifiedTrendKeys))); 171 | return false; 172 | } 173 | 174 | Set supportedTrendKeys = generator.getSupportedValueGroupKeys(); 175 | 176 | if (!supportedTrendKeys.containsAll(specifiedTrendKeys)) { 177 | log.warn("Request {} for generator '{}' specifies unsupported trend keys.", i, generator.getName()); 178 | log.warn("The generator supports the following keys: {}.", joiner.join(supportedTrendKeys)); 179 | log.warn("The following keys are being ignored: {}.", 180 | joiner.join(Sets.difference(specifiedTrendKeys, supportedTrendKeys))); 181 | } 182 | } 183 | 184 | return true; 185 | } 186 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open mHealth Sample Data Generator [![Build Status](https://travis-ci.org/openmhealth/sample-data-generator.svg?branch=master)](https://travis-ci.org/openmhealth/sample-data-generator) 2 | 3 | When people become familiar with [Open mHealth](http://www.openmhealth.org/), they often ask if there's a data set they can test against, especially 4 | one containing data matching the data types, time scales, and trends they're interested in. This project is meant to help all 5 | of those people generate the specific data sets they need. 6 | 7 | The data generator is a command-line tool that creates Open mHealth [data points](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_data-point) 8 | for different kinds of measures. It reads a [configuration file](#configuration) to understand what data it should create, and writes that data 9 | out to the console or to a file. You can read a more detailed overview in [our blog post](http://www.openmhealth.org/years-of-health-data-yours-in-minutes/). 10 | 11 | ### Requirements 12 | 13 | - If you want to run the generator using Docker, you'll need [Docker](https://docs.docker.com/installation/#installation/). 14 | - If you want to run the generator natively, you'll need a Java 8 JRE. 15 | - If you want to build or modify the code, you'll need a Java 8 SDK. 16 | 17 | ### Installation 18 | 19 | To install the generator using Docker, open a terminal and download the data generator image by running 20 | 21 | `docker pull openmhealth/omh-sample-data-generator:latest` 22 | 23 | If you don't want to use Docker but don't want to build the binary, download the `data-generator-x.y.z.jar` JAR file from 24 | the [latest release](https://github.com/openmhealth/sample-data-generator/releases) on GitHub. 25 | You'll need a Java JRE to run it. 26 | 27 | If you want to modify and build the code, open a terminal, clone this repository and build the generator by running 28 | 29 | `./gradlew build` 30 | 31 | The assembled JAR file will be created in the `backend/build/libs` directory. 32 | 33 | ### Configuration 34 | 35 | To configure the data generator, you'll modify a [YAML](https://en.wikipedia.org/wiki/YAML) configuration file called 36 | `application.yml`. If you haven't created a configuration file yet, the quickest way to get started is to copy the 37 | [default configuration file](backend/src/main/resources/application.yml) (it's easier to copy if you 38 | click the 'Raw' button on that page.) 39 | 40 | > You can save the configuration file anywhere, unless you plan to run the generator using Docker and are on Mac OS X or Windows. 41 | In that case, save the configuration somewhere under your `/Users` or `C:\Users` directory. This is due to a [restriction](https://docs.docker.com/userguide/dockervolumes/) 42 | on the directories the Docker daemon has access to when mounting volumes. 43 | 44 | There's a big section below on the configuration file, but first let's make sure you can run the generator. 45 | 46 | ### Running 47 | 48 | To run the generator using Docker, and assuming you're in the same directory as the `application.yml` configuration 49 | file, run 50 | 51 | ``docker run --rm -v `pwd`:/opt/omh-sample-data-generator/mount openmhealth/omh-sample-data-generator:latest`` 52 | 53 | in a terminal. If you're not in the same directory as the configuration file, replace `` `pwd` `` with the directory 54 | the configuration file is in. 55 | 56 | Alternatively, to run the generator natively, navigate to the directory that contains the configuration file and 57 | run 58 | 59 | `java -jar /path/to/data-generator-x.y.z.jar` 60 | 61 | In either case, you should see output that looks something like 62 | 63 | ``` 64 | Starting Application on machine with PID 15861 (/Users/foo/data-generator-x.y.z.jar started by foo in /Users/foo) 65 | Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6e8d6976: startup date [Sat Jul 11 16:09:24 CEST 2015]; root of context hierarchy 66 | Registering beans for JMX exposure on startup 67 | Started Application in 2.056 seconds (JVM running for 2.998) 68 | A total of 0 data point(s) have been written. 69 | Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6e8d6976: startup date [Sat Jul 11 16:09:24 CEST 2015]; root of context hierarchy 70 | Unregistering JMX-exposed beans on shutdown 71 | ``` 72 | 73 | By default, no data points will be generated. Now that you can run the generator, read on to learn how to configure it 74 | to create some data. 75 | 76 | ### Configuration, take two 77 | 78 | The generator configuration file is written in YAML. If you're new to YAML, just keep in mind that indentation 79 | matters and to use spaces, not tabs. 80 | 81 | The configuration file has keys that divide it into three sections 82 | 83 | - an [output settings](#output_settings) section, which controls what the generator does with the data points it creates 84 | - a [header settings](#header_settings) section, which sets certain header fields in the generated data points 85 | - a [measure generation settings](#measure_generation_settings) section, which specifies the data to generate 86 | 87 | A simple commented configuration file might looks like this 88 | 89 | ```yaml 90 | # output settings 91 | # meaning: Write any generated data points to the console. 92 | output: 93 | destination: console 94 | 95 | data: 96 | # header settings 97 | # meaning: Set the `header.user-id` property of the data point to the username `joe`. 98 | header: 99 | user-id: joe 100 | 101 | # measure generation settings 102 | measure-generation-requests: 103 | 104 | # meaning: A measure generator called `body-weight` should create a year's worth of data 105 | # for 2014, with data points 24 hours apart, or about 365 data points in total. The 106 | # weight should trend from 55kg at the beginning of the year to 60kg at the end of the year. 107 | - generator: body-weight 108 | start-date-time: 2014-01-01T12:00:00Z 109 | end-date-time: 2015-01-01T12:00:00Z 110 | mean-inter-point-duration: PT24h 111 | trends: 112 | ? weight-in-kg 113 | : start-value: 55 114 | end-value: 60 115 | ``` 116 | 117 | This example is significantly shorter than the default configuration file because defaults are being used. In general, 118 | if any key in the default configuration file has a "defaults to" comment, that key can be omitted, and the generator will 119 | set the value of that key to the stated default. 120 | 121 | The following paragraphs explain what each group of settings does. The default configuration file is also heavily 122 | documented, but we recommend you first read the following sections before diving in. 123 | 124 | #### Output settings 125 | 126 | The `output` key controls what the generator does with the data points it creates. The complete 127 | settings are as follows 128 | 129 | ```yaml 130 | output: 131 | # whether to write data points to the "console" or to a "file", defaults to "console" 132 | destination: console 133 | file: 134 | # the file to write the data points to, defaults to "output.json" 135 | filename: output.json 136 | # true if the file should be appended to, false if it should be overwritten, defaults to true 137 | append: true 138 | ``` 139 | 140 | The `file` key is ignored if the destination is set to `console`. 141 | 142 | The `filename` key supports both absolute paths and relative paths. If you're writing to a file and running the 143 | generator in Docker, however, you should use a simple filename in the `filename` key. The file will be written to the 144 | same directory that contains your configuration file. 145 | 146 | The generator writes one data point per line, with no separators. When writing to a file, this format makes it easy 147 | to add import the file into a MongoDB collection using the command 148 | 149 | - `mongoimport -d some_database -c some_collection --file output.json` 150 | 151 | 152 | #### Data point header settings 153 | 154 | The `data.header` key gives you a way to tune some of the operational data in the data points. The complete 155 | settings are as follows 156 | 157 | ```yaml 158 | data: 159 | header: 160 | # the user to associate the data points with, defaults to "some-user" 161 | user-id: some-user 162 | 163 | acquisition-provenance: 164 | # the name of the source of the data points, defaults to "generator" 165 | source-name: generator 166 | ``` 167 | 168 | At this point, only the user and source name settings are available. We'll add more settings based on demand. 169 | 170 | #### Measure generation settings 171 | 172 | The data generator can create data points for different measures. The measures which are supported so far are 173 | 174 | * [ambient temperature](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_ambient-temperature) 175 | * [blood glucose](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-glucose) 176 | * [blood pressure](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-pressure) 177 | * [body fat percentage](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-fat-percentage) 178 | * [body height](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-height) 179 | * [body temperature](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-temperature) 180 | * [body weight](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight) 181 | * [heart rate](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate) 182 | * [minutes of moderate activity](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_minutes-moderate-activity) 183 | * [physical activity](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_physical-activity) 184 | * [sleep duration](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration) 185 | * [step count](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count) 186 | 187 | To create data points for a specific measure, the data generator uses a *measure generator* that is capable of 188 | generating that type of measure. For example, to create body weight data points, the data generator uses a body 189 | weight measure generator. 190 | 191 | A measure generator needs at least one value to generate a measure. For example, to create a body weight measure, 192 | the generator needs a mass value. You may also want the generator to use different values in the different instances it 193 | creates. This is achieved using *trends*. 194 | 195 | ##### Trends 196 | 197 | A trend represents the changing of a value over time. The trend has a *start timestamp* and *start value*, and an *end 198 | timestamp* and *end value*. Using linear interpolation, a generator can compute the value at any time between 199 | the start time and the end time. For example, if the start timestamp is midnight January 1st, 2015 with a weight of 60kg 200 | and the end timestamp is March 1st, 2015 at midnight with a weight of 65kg, the generator will interpolate the weight 201 | in the beginning of February to be 62.5kg. 202 | 203 | Although a generator can compute the value at any point along the trend, you need to tell it which points to generate data for. 204 | This is achieved using a *mean inter-point duration*. The duration you specify in the configuration file is fed to 205 | an exponential distribution to pick timestamps along the trend. A mean inter-point duration of `PT6h`, expressed in an 206 | ISO 8601 duration format, tells the generator to create a data point every 6 hours on average along the trend. 207 | 208 | The configuration for the above example looks like this 209 | 210 | ```yaml 211 | measure-generation-requests: 212 | - generator: body-weight 213 | start-date-time: 2015-01-01T12:00:00Z 214 | end-date-time: 2015-03-01T12:00:00Z 215 | mean-inter-point-duration: PT6h 216 | trends: 217 | ? weight-in-kg 218 | : start-value: 60 219 | end-value: 65 220 | ``` 221 | 222 | In the example, the name of the measure generator is `body-weight`. This is defined by the 223 | [measure generator](backend/src/main/java/org/openmhealth/data/generator/service/BodyWeightDataPointGenerator.java) 224 | included in the data generator. It is possible to create different generators for the same measure, and you would 225 | differentiate between generators by name. Each measure generator defines the trends it needs, and the `body-weight` measure 226 | generator uses a trend called `weight-in-kg`. The data generator will warn you if you use unrecognized keys, or fail 227 | to provide required keys. The full list of included measure generators and their keys is available in 228 | [Appendix A](#appendix-a-measure-generators). 229 | 230 | When executed, this configuration generates about 240 data points (60 days times 4 data points per day), where a 231 | data point looks like this: 232 | 233 | ```json 234 | { 235 | "header": { 236 | "id": "423c4b46-15ac-438b-9734-f8556cb94b6a", 237 | "creation_date_time": "2015-01-01T15:34:25Z", 238 | "acquisition_provenance": { 239 | "source_name": "generator", 240 | "source_creation_date_time": "2015-01-01T15:33:25Z", 241 | "modality": "sensed" 242 | }, 243 | "user_id": "some-user", 244 | "schema_id": { 245 | "namespace": "omh", 246 | "name": "body-weight", 247 | "version": "1.0" 248 | } 249 | }, 250 | "body": { 251 | "effective_time_frame": { 252 | "date_time": "2015-01-01T15:33:25Z" 253 | }, 254 | "body_weight": { 255 | "unit": "kg", 256 | "value": 60.01255983207784 257 | } 258 | }, 259 | "id": "423c4b46-15ac-438b-9734-f8556cb94b6a" 260 | } 261 | ``` 262 | 263 | A graph of the generated data looks like this: 264 | 265 | ![Body weight no variance](resources/images/body-weight-no-variance.png?raw=true "Weight trend") 266 | 267 | > These graphs are generated using an interactive chart component in our [web visualizations library](https://github.com/openmhealth/web-visualizations). 268 | 269 | A closer view of a few days in January looks like this: 270 | 271 | ![Body weight no variance (zoomed)](resources/images/body-weight-no-variance-zoomed.png?raw=true "Weight trend (zoomed)") 272 | 273 | There are a couple of things to note from this graph. The first is that the data points aren't evenly spaced in time. This is 274 | caused by the configured inter-point duration being a *mean*. Some points will naturally be nearer each other and some farther 275 | apart as the exponential distribution is sampled to come up with effective time frames. This variability is typically desired as 276 | it more closely represents real world data. The second thing to note is that there's no variability off the trend itself, 277 | i.e. the weights follow the trend perfectly, which is not representative of real world data. 278 | 279 | ##### Standard deviation 280 | 281 | To add variability to the values, you can specify 282 | a *standard deviation* when configuring a trend. Once the generator interpolates a value at a specific point in time, 283 | it treats that value as the mean of a Gaussian distribution with the specified standard deviation. The random variable 284 | is then sampled to come up with the value to set. 285 | 286 | If we add a standard deviation to our example configuration as follows: 287 | 288 | ```yaml 289 | measure-generation-requests: 290 | - generator: body-weight 291 | start-date-time: 2015-01-01T12:00:00Z 292 | end-date-time: 2015-03-01T12:00:00Z 293 | mean-inter-point-duration: PT6h 294 | trends: 295 | ? weight-in-kg 296 | : start-value: 60 297 | end-value: 65 298 | standard-deviation: 0.25 299 | ``` 300 | 301 | A graph of the generated data looks like this: 302 | 303 | ![Body weight with variance](resources/images/body-weight-with-variance.png?raw=true "Weight trend with variance") 304 | 305 | And a closer view of a few days in January now looks like this: 306 | 307 | ![Body weight with variance (zoomed)](resources/images/body-weight-with-variance-zoomed.png?raw=true "Weight trend with variance (zoomed)") 308 | 309 | ##### Limits 310 | 311 | When using the `standard-deviation` key, some generated values will be far away from the trend, possibly so far away 312 | as to be undesirable or outright invalid, such as negative weights. To mitigate this, the configuration supports 313 | *minimum value* and *maximum value* keys. For example: 314 | 315 | ```yaml 316 | measure-generation-requests: 317 | - generator: body-weight 318 | start-date-time: 2015-01-01T12:00:00Z 319 | end-date-time: 2015-03-01T12:00:00Z 320 | mean-inter-point-duration: PT6h 321 | trends: 322 | ? weight-in-kg 323 | : start-value: 60 324 | end-value: 65 325 | standard-deviation: 0.25 326 | minimum-value: 59 327 | maximum-value: 66 328 | ``` 329 | 330 | All generated values will fall within these bounds. 331 | 332 | ##### Night time measure suppression 333 | 334 | You may want to suppress the generation of measures that occur at night, typically when modelling self-reported data. 335 | The generator has a *suppress-night-time-measures* key that skips data points whose effective time frames 336 | falls between 11pm and 6am. Our example configuration would look like 337 | 338 | ```yaml 339 | measure-generation-requests: 340 | - generator: body-weight 341 | start-date-time: 2015-01-01T12:00:00Z 342 | end-date-time: 2015-03-01T12:00:00Z 343 | mean-inter-point-duration: PT6h 344 | suppress-night-time-measures: true 345 | trends: 346 | ? weight-in-kg 347 | : start-value: 60 348 | end-value: 65 349 | standard-deviation: 0.25 350 | minimum-value: 59 351 | maximum-value: 66 352 | ``` 353 | 354 | Our closer view of those few days in January now looks like this: 355 | 356 | ![Body weight without night time measures](resources/images/body-weight-without-night-time-measures.png?raw=true "Weight trend without night time measures") 357 | 358 | As you can see, the data points no longer have effective time frames at night. 359 | 360 | ##### Multi-trend measures 361 | 362 | Some measure generators build measures by combining multiple trends. To define these trends, simply add more 363 | trend definitions to the `trends` key using the question-mark-colon notation. For example, to create blood pressure measures, 364 | specify both `systolic-in-mmhg` and `diastolic-in-mmhg` trends to the `blood-pressure` generator as 365 | follows: 366 | 367 | ```yaml 368 | measure-generation-requests: 369 | - generator: blood-pressure 370 | start-date-time: 2015-01-01T12:00:00Z 371 | end-date-time: 2015-03-01T12:00:00Z 372 | mean-inter-point-duration: PT12h 373 | suppress-night-time-measures: true 374 | trends: 375 | ? systolic-in-mmhg 376 | : start-value: 110 377 | end-value: 125 378 | minimum-value: 100 379 | maximum-value: 140 380 | standard-deviation: 3 381 | ? diastolic-in-mmhg 382 | : start-value: 70 383 | end-value: 80 384 | minimum-value: 60 385 | maximum-value: 90 386 | standard-deviation: 3 387 | ``` 388 | 389 | A graph of the generated data looks like this: 390 | 391 | ![Blood pressure](resources/images/blood-pressure-with-variance.png?raw=true "Blood pressure") 392 | 393 | ##### Avoiding repetition 394 | 395 | A single configuration file can create different types of measures. Simply concatenate measure generation requests in 396 | the configuration file, and the data generator will generate all configured measures. For example: 397 | 398 | ```yaml 399 | measure-generation-requests: 400 | - generator: blood-pressure 401 | start-date-time: 2015-01-01T12:00:00Z 402 | end-date-time: 2015-03-01T12:00:00Z 403 | mean-inter-point-duration: PT12h 404 | suppress-night-time-measures: true 405 | trends: 406 | ? systolic-in-mmhg 407 | : start-value: 110 408 | end-value: 125 409 | minimum-value: 100 410 | maximum-value: 140 411 | standard-deviation: 3 412 | ? diastolic-in-mmhg 413 | : start-value: 70 414 | end-value: 80 415 | minimum-value: 60 416 | maximum-value: 90 417 | standard-deviation: 3 418 | 419 | - generator: body-weight 420 | start-date-time: 2015-01-01T12:00:00Z 421 | end-date-time: 2015-03-01T12:00:00Z 422 | mean-inter-point-duration: PT6h 423 | suppress-night-time-measures: true 424 | trends: 425 | ? weight-in-kg 426 | : start-value: 55 427 | end-value: 60 428 | minimum-value: 50 429 | maximum-value: 65 430 | standard-deviation: 0.1 431 | ``` 432 | 433 | In the above example, some measure generator settings are repeated. To avoid this repetition, 434 | you can also place common settings under the `data` key, and only override them per generator as necessary. The above 435 | settings would then look like this. 436 | 437 | ```yaml 438 | data: 439 | start-date-time: 2015-01-01T12:00:00Z # defaults to January 1st, 2014 at noon UTC 440 | end-date-time: 2015-03-01T12:00:00Z # defaults to January 1st, 2015 at noon UTC 441 | suppress-night-time-measures: true # defaults to false 442 | 443 | measure-generation-requests: 444 | - generator: blood-pressure 445 | mean-inter-point-duration: PT12h # defaults to PT24h 446 | trends: 447 | ? systolic-in-mmhg 448 | : start-value: 110 449 | end-value: 125 450 | minimum-value: 100 451 | maximum-value: 140 452 | standard-deviation: 3 453 | ? diastolic-in-mmhg 454 | : start-value: 70 455 | end-value: 80 456 | minimum-value: 60 457 | maximum-value: 90 458 | standard-deviation: 3 459 | 460 | - generator: body-weight 461 | mean-inter-point-duration: PT6h 462 | trends: 463 | ? weight-in-kg 464 | : start-value: 55 465 | end-value: 60 466 | minimum-value: 50 467 | maximum-value: 65 468 | standard-deviation: 0.1 469 | ``` 470 | 471 | If the generator settings are omitted, the defaults in the comments take effect. 472 | 473 | You can add as many measure generator requests in a configuration file as you want, including 474 | requests for the same measure generator. This lets you assemble data sets that include more complex trends. 475 | 476 | ### Contributing 477 | 478 | To contribute to this repository 479 | 480 | 1. Open an [issue](https://github.com/openmhealth/sample-data-generator/issues) to let us know what you're going to work on. 481 | 1. This lets us give you feedback early and lets us put you in touch with people who can help. 482 | 2. Fork this repository. 483 | 3. Create your feature branch from the `develop` branch. 484 | 4. Commit and push your changes to your fork. 485 | 5. Create a pull request. 486 | 487 | 488 | ### Appendix A. Measure generators 489 | 490 | The following table shows the currently included measure generators and their trend keys. For 491 | more information, take a look at the code or ask us a [question](https://github.com/openmhealth/sample-data-generator/issues). 492 | The default configuration file also includes a sample configuration for each measure generator. 493 | 494 | |name|Open mHealth measure schema|supported trend keys|required trend keys| 495 | |----|---------------------------|--------------------|-------------------| 496 | |[ambient-temperature](backend/src/main/java/org/openmhealth/data/generator/service/AmbientTemperatureDataPointGenerator.java)|[omh:blood-pressure](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_ambient-temperature)|temperature-in-c| 497 | |[blood-glucose](backend/src/main/java/org/openmhealth/data/generator/service/BloodGlucoseDataPointGenerator.java)|[omh:blood-glucose](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-glucose)|glucose-in-mg-per-dl|same| 498 | |[blood-pressure](backend/src/main/java/org/openmhealth/data/generator/service/BloodPressureDataPointGenerator.java)|[omh:blood-pressure](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-pressure)|systolic-in-mmhg, diastolic-in-mmhg|same| 499 | |[body-fat-percentage](backend/src/main/java/org/openmhealth/data/generator/service/BodyFatPercentageDataPointGenerator.java)|[omh:body-fat-percentage](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-fat-percentage)|percentage|same| 500 | |[body-height](backend/src/main/java/org/openmhealth/data/generator/service/BodyHeightDataPointGenerator.java)|[omh:body-height](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-height)|height-in-meters|same| 501 | |[body-temperature](backend/src/main/java/org/openmhealth/data/generator/service/BodyTemperatureDataPointGenerator.java)|[omh:body-temperature](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-temperature)|temperature-in-c|same| 502 | |[body-weight](backend/src/main/java/org/openmhealth/data/generator/service/BodyWeightDataPointGenerator.java)|[omh:body-weight](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight)|weight-in-kg|same| 503 | |[heart-rate](backend/src/main/java/org/openmhealth/data/generator/service/HeartRateDataPointGenerator.java)|[omh:heart-rate](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate)|rate-in-beats-per-minute|same| 504 | |[minutes-moderate-activity](backend/src/main/java/org/openmhealth/data/generator/service/MinutesModerateActivityDataPointGenerator.java)|[omh:minutes-moderate-activity](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_minutes-moderate-activity)|minutes|same| 505 | |[physical-activity](backend/src/main/java/org/openmhealth/data/generator/service/PhysicalActivityDataPointGenerator.java)|[omh:physical-activity](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_physical-activity)|duration-in-seconds, distance-in-meters|duration-in-seconds| 506 | |[sleep-duration](backend/src/main/java/org/openmhealth/data/generator/service/SleepDurationDataPointGenerator.java)|[omh:sleep-duration](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration)|duration-in-hours|same| 507 | |[step-count](backend/src/main/java/org/openmhealth/data/generator/service/StepCountDataPointGenerator.java)|[omh:step-count](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count)|steps-per-minute, duration-in-seconds|same| 508 | 509 | 510 | Measure generators are short and quite easy to write. Feel free to [contribute](#contributing) others. 511 | --------------------------------------------------------------------------------