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 extends DataPoint>> 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 extends DataPoint>> 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 [](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 | 
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 | ")
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 | 
304 |
305 | And a closer view of a few days in January now looks like this:
306 |
307 | ")
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 | 
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 | 
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 |
--------------------------------------------------------------------------------