<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/TimestampedValueGroupGenerationService.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import org.openmhealth.data.generator.domain.MeasureGenerationRequest;
import org.openmhealth.data.generator.domain.TimestampedValueGroup;


/**
 * A service that generates timestamped value groups.
 *
 * @author <NAME>
 */
public interface TimestampedValueGroupGenerationService {

    /**
     * @param request a request to generate measures
     * @return a list of timestamped value groups from which measures can be built
     */
    Iterable<TimestampedValueGroup> generateValueGroups(MeasureGenerationRequest request);
}


<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/BloodGlucoseDataPointGenerator.java<|end_filename|>
/*
 * Copyright 2016 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import org.openmhealth.data.generator.domain.TimestampedValueGroup;
import org.openmhealth.schema.domain.omh.BloodGlucose;
import org.openmhealth.schema.domain.omh.TypedUnitValue;
import org.springframework.stereotype.Component;

import java.util.Set;

import static java.util.Collections.singleton;
import static org.openmhealth.schema.domain.omh.BloodGlucoseUnit.MILLIGRAMS_PER_DECILITER;


/**
 * @author <NAME>
 */
@Component
public class BloodGlucoseDataPointGenerator
        extends AbstractDataPointGeneratorImpl<BloodGlucose> {

    public static final String GLUCOSE_KEY = "glucose-in-mg-per-dl";

    @Override
    public String getName() {
        return "blood-glucose";
    }

    @Override
    public Set<String> getSupportedValueGroupKeys() {
        return singleton(GLUCOSE_KEY);
    }

    @Override
    public Set<String> getRequiredValueGroupKeys() {
        return singleton(GLUCOSE_KEY);
    }

    @Override
    public BloodGlucose newMeasure(TimestampedValueGroup valueGroup) {

        // TODO set the specimen source once the SDK is updated to omh:blood-glucose:2.0
        return new BloodGlucose.Builder(
                new TypedUnitValue<>(MILLIGRAMS_PER_DECILITER, valueGroup.getValue(GLUCOSE_KEY)))
                .setEffectiveTimeFrame(valueGroup.getTimestamp())
                .build();
    }
}


<|start_filename|>backend/docker/Dockerfile<|end_filename|>
FROM java:openjdk-8-jre
MAINTAINER <NAME> <<EMAIL>>

ENV BASE_DIR /opt/omh-sample-data-generator
ADD data-generator.jar $BASE_DIR/

# the configuration file will be read from a directory called 'mount', and output will be written to it
RUN mkdir -p $BASE_DIR/mount
WORKDIR $BASE_DIR/mount

CMD /usr/bin/java -jar $BASE_DIR/data-generator.jar


<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/TimestampedValueGroupGenerationServiceImpl.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import org.apache.commons.math3.distribution.ExponentialDistribution;
import org.openmhealth.data.generator.domain.BoundedRandomVariableTrend;
import org.openmhealth.data.generator.domain.MeasureGenerationRequest;
import org.openmhealth.data.generator.domain.TimestampedValueGroup;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static java.time.temporal.ChronoUnit.SECONDS;


/**
 * @author <NAME>
 */
@Service
public class TimestampedValueGroupGenerationServiceImpl implements TimestampedValueGroupGenerationService {

    public static final int NIGHT_TIME_START_HOUR = 23;
    public static final int NIGHT_TIME_END_HOUR = 6;


    @Override
    public Iterable<TimestampedValueGroup> generateValueGroups(MeasureGenerationRequest request) {

        ExponentialDistribution interPointDurationDistribution =
                new ExponentialDistribution(request.getMeanInterPointDuration().getSeconds());

        long totalDurationInS = Duration.between(request.getStartDateTime(), request.getEndDateTime()).getSeconds();

        OffsetDateTime effectiveDateTime = request.getStartDateTime();
        List<TimestampedValueGroup> timestampedValueGroups = new ArrayList<>();

        do {
            effectiveDateTime = effectiveDateTime.plus((long) interPointDurationDistribution.sample(), SECONDS);

            if (!effectiveDateTime.isBefore(request.getEndDateTime())) {
                break;
            }

            if (request.isSuppressNightTimeMeasures() != null && request.isSuppressNightTimeMeasures() &&
                    (effectiveDateTime.getHour() >= NIGHT_TIME_START_HOUR ||
                            effectiveDateTime.getHour() < NIGHT_TIME_END_HOUR)) {
                continue;
            }

            TimestampedValueGroup valueGroup = new TimestampedValueGroup();
            valueGroup.setTimestamp(effectiveDateTime);

            double trendProgressFraction = (double)
                    Duration.between(request.getStartDateTime(), effectiveDateTime).getSeconds() / totalDurationInS;

            for (Map.Entry<String, BoundedRandomVariableTrend> trendEntry : request.getTrends().entrySet()) {

                String key = trendEntry.getKey();
                BoundedRandomVariableTrend trend = trendEntry.getValue();

                double value = trend.nextValue(trendProgressFraction);
                valueGroup.setValue(key, value);
            }

            timestampedValueGroups.add(valueGroup);
        }
        while (true);

        return timestampedValueGroups;
    }
}


<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/DataPointGenerator.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import org.openmhealth.data.generator.domain.TimestampedValueGroup;
import org.openmhealth.schema.domain.omh.DataPoint;
import org.openmhealth.schema.domain.omh.Measure;

import java.util.Set;


/**
 * @author <NAME>
 */
public interface DataPointGenerator<T extends Measure> {

    /**
     * @return the name of this generator
     */
    String getName();

    /**
     * @return the set of value group keys required by this generator
     */
    Set<String> getRequiredValueGroupKeys();

    /**
     * @return the set of value group keys supported by this generator
     */
    Set<String> getSupportedValueGroupKeys();

    /**
     * @param valueGroups a list of value groups, where each value group corresponds to a data point
     * @return the list of generated data points
     */
    Iterable<DataPoint<T>> generateDataPoints(Iterable<TimestampedValueGroup> valueGroups);
}


<|start_filename|>backend/src/test/java/org/openmhealth/data/generator/converter/StringToDurationConverterUnitTests.java<|end_filename|>
/*
 * Copyright 2015 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.converter;

import org.testng.annotations.Test;

import java.time.Duration;
import java.time.format.DateTimeParseException;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.testng.Assert.*;

/**
 * @author <NAME>
 */
public class StringToDurationConverterUnitTests {

    private StringToDurationConverter converter = new StringToDurationConverter();

    @Test
    public void convertShouldReturnNullOnUndefinedSource() throws Exception {

        assertThat(converter.convert(null), nullValue());
    }

    @Test(expectedExceptions = DateTimeParseException.class)
    public void convertShouldThrowExceptionOnMalformedSource() throws Exception {

        converter.convert("sbcdo");
    }

    @Test
    public void convertShouldReturnCorrectDurationOnPositiveSource() throws Exception {

        Duration duration = converter.convert("PT3H4M");

        assertThat(duration, notNullValue());
        assertThat(duration, equalTo(Duration.ofMinutes(184)));
    }

    @Test
    public void convertShouldReturnCorrectDurationOnNegativeSource() throws Exception {

        Duration duration = converter.convert("PT-6H-3M");

        assertThat(duration, notNullValue());
        assertThat(duration, equalTo(Duration.ofMinutes(-363)));
    }
}

<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/AmbientTemperatureDataPointGenerator.java<|end_filename|>
/*
 * Copyright 2016 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import org.openmhealth.data.generator.domain.TimestampedValueGroup;
import org.openmhealth.schema.domain.omh.AmbientTemperature;
import org.openmhealth.schema.domain.omh.TemperatureUnitValue;
import org.springframework.stereotype.Component;

import java.util.Set;

import static java.util.Collections.singleton;
import static org.openmhealth.schema.domain.omh.TemperatureUnit.CELSIUS;


/**
 * @author <NAME>
 */
@Component
public class AmbientTemperatureDataPointGenerator extends AbstractDataPointGeneratorImpl<AmbientTemperature> {

    public static final String TEMPERATURE_KEY = "temperature-in-c";

    @Override
    public String getName() {
        return "ambient-temperature";
    }

    @Override
    public Set<String> getRequiredValueGroupKeys() {
        return singleton(TEMPERATURE_KEY);
    }

    @Override
    public Set<String> getSupportedValueGroupKeys() {
        return singleton(TEMPERATURE_KEY);
    }

    @Override
    public AmbientTemperature newMeasure(TimestampedValueGroup valueGroup) {

        return new AmbientTemperature.Builder(new TemperatureUnitValue(CELSIUS, valueGroup.getValue(TEMPERATURE_KEY)))
                .setEffectiveTimeFrame(valueGroup.getTimestamp())
                .build();
    }
}


<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/domain/BoundedRandomVariableTrend.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.domain;

import javax.validation.constraints.NotNull;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;


/**
 * A trend wraps a {@link BoundedRandomVariable} in order to provide values whose mean falls along a linear
 * interpolation from a start value to an end value.
 *
 * @author <NAME>
 */
// TODO refactor this into a container object that wraps a variable and a trend, allowing different variables and
// trend types (linear, polynomial, spline, etc.) with multiple sample points instead of just start and end
public class BoundedRandomVariableTrend {

    private BoundedRandomVariable variable = new BoundedRandomVariable();
    private Double startValue;
    private Double endValue;

    public BoundedRandomVariableTrend() {
    }

    public BoundedRandomVariableTrend(BoundedRandomVariable variable, Double startValue, Double endValue) {

        checkNotNull(variable);
        checkNotNull(startValue);
        checkNotNull(endValue);

        this.variable = variable;
        this.startValue = startValue;
        this.endValue = endValue;
    }

    @NotNull
    public BoundedRandomVariable getVariable() {
        return variable;
    }

    public void setVariable(BoundedRandomVariable variable) {

        checkNotNull(variable);
        this.variable = variable;
    }

    /**
     * @return the value the trend starts with
     */
    @NotNull
    public Double getStartValue() {
        return startValue;
    }

    public void setStartValue(Double startValue) {

        checkNotNull(startValue);
        this.startValue = startValue;
    }

    /**
     * @return the value the trend ends with
     */
    @NotNull
    public Double getEndValue() {
        return endValue;
    }

    public void setEndValue(Double endValue) {

        checkNotNull(endValue);
        this.endValue = endValue;
    }

    /**
     * @param fraction a fraction of the range between the start value and the end value
     * @return the linear interpolation of the value at that fraction
     */
    public Double interpolate(Double fraction) {

        checkArgument(fraction >= 0);
        checkArgument(fraction <= 1);

        return startValue + (endValue - startValue) * fraction;
    }

    /**
     * @param fraction a fraction of the range between the start value and the end value
     * @return a value generated by the bounded random variable when its mean is set to the value of the trend at the
     * given fraction
     */
    public Double nextValue(Double fraction) {

        Double mean = interpolate(fraction);
        return variable.nextValue(mean);
    }

    // TODO remove these setters on a refactor, currently needed by SnakeYaml to support flat structure
    public void setStandardDeviation(Double standardDeviation) {
        variable.setStandardDeviation(standardDeviation);
    }

    public void setMinimumValue(Double minimumValue) {
        variable.setMinimumValue(minimumValue);
    }

    public void setMaximumValue(Double maximumValue) {
        variable.setMaximumValue(maximumValue);
    }

    @Override
    public String toString() {

        final StringBuilder sb = new StringBuilder("BoundedRandomVariableTrend{");

        sb.append("variable=").append(variable);
        sb.append(", startValue=").append(startValue);
        sb.append(", endValue=").append(endValue);
        sb.append('}');

        return sb.toString();
    }
}



<|start_filename|>backend/src/test/java/org/openmhealth/data/generator/service/TimestampedValueGroupGenerationServiceUnitTests.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import com.google.common.collect.Iterables;
import org.openmhealth.data.generator.domain.BoundedRandomVariable;
import org.openmhealth.data.generator.domain.BoundedRandomVariableTrend;
import org.openmhealth.data.generator.domain.MeasureGenerationRequest;
import org.openmhealth.data.generator.domain.TimestampedValueGroup;
import org.testng.annotations.Test;

import java.time.Duration;
import java.time.OffsetDateTime;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThanOrEqualTo;


/**
 * @author <NAME>
 */
public class TimestampedValueGroupGenerationServiceUnitTests {

    private TimestampedValueGroupGenerationService service = new TimestampedValueGroupGenerationServiceImpl();


    @Test
    public void generateValueGroupsShouldWork() {

        String trendKey = "foo";
        double minimumValue = 50d;
        double maximumValue = 90d;

        BoundedRandomVariable randomVariable = new BoundedRandomVariable(1.0, minimumValue, maximumValue);
        BoundedRandomVariableTrend randomVariableTrend = new BoundedRandomVariableTrend(randomVariable, 60d, 80d);

        OffsetDateTime startDateTime = OffsetDateTime.now().minusDays(10);
        OffsetDateTime endDateTime = OffsetDateTime.now();

        MeasureGenerationRequest request = new MeasureGenerationRequest();
        request.setStartDateTime(startDateTime);
        request.setEndDateTime(endDateTime);
        request.setMeanInterPointDuration(Duration.ofHours(6));
        request.addTrend(trendKey, randomVariableTrend);

        Iterable<TimestampedValueGroup> valueGroups;

        do {
            valueGroups = service.generateValueGroups(request);
        }
        while (Iterables.size(valueGroups) == 0); // in the extremely unlikely case of an empty list

        for (TimestampedValueGroup valueGroup : valueGroups) {

            assertThat(valueGroup.getValue(trendKey), greaterThanOrEqualTo(minimumValue));
            assertThat(valueGroup.getValue(trendKey), lessThanOrEqualTo(maximumValue));

            long effectiveDateTime = valueGroup.getTimestamp().toEpochSecond();
            assertThat(effectiveDateTime, greaterThanOrEqualTo(startDateTime.toEpochSecond()));
            assertThat(effectiveDateTime, lessThanOrEqualTo(endDateTime.toEpochSecond()));
        }
    }
}

<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/PhysicalActivityDataPointGenerator.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import com.google.common.collect.Sets;
import org.openmhealth.data.generator.domain.TimestampedValueGroup;
import org.openmhealth.schema.domain.omh.DurationUnitValue;
import org.openmhealth.schema.domain.omh.LengthUnitValue;
import org.openmhealth.schema.domain.omh.PhysicalActivity;
import org.springframework.stereotype.Component;

import java.util.Set;

import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND;
import static org.openmhealth.schema.domain.omh.LengthUnit.METER;
import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndDuration;


/**
 * @author <NAME>
 */
@Component
public class PhysicalActivityDataPointGenerator
        extends AbstractDataPointGeneratorImpl<PhysicalActivity> {

    public static final String DURATION_KEY = "duration-in-seconds";
    public static final String DISTANCE_KEY = "distance-in-meters";

    @Override
    public String getName() {
        return "physical-activity";
    }

    @Override
    public Set<String> getRequiredValueGroupKeys() {
        return Sets.newHashSet(DURATION_KEY);
    }

    @Override
    public Set<String> getSupportedValueGroupKeys() {
        return Sets.newHashSet(DURATION_KEY, DISTANCE_KEY);
    }

    @Override
    public PhysicalActivity newMeasure(TimestampedValueGroup valueGroup) {

        DurationUnitValue duration = new DurationUnitValue(SECOND, valueGroup.getValue(DURATION_KEY));

        PhysicalActivity.Builder builder = new PhysicalActivity.Builder("some activity")
                .setEffectiveTimeFrame(ofStartDateTimeAndDuration(valueGroup.getTimestamp(), duration));

        if (valueGroup.getValue(DISTANCE_KEY) != null) {
            builder.setDistance(new LengthUnitValue(METER, valueGroup.getValue(DISTANCE_KEY)));
        }

        return builder.build();
    }
}


<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/DataPointWritingService.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import org.openmhealth.schema.domain.omh.DataPoint;


/**
 * @author <NAME>
 */
public interface DataPointWritingService {

    /**
     * @param dataPoints the data points to write
     * @return the number of data points that have been written
     * @throws Exception if an error occurred while writing data points
     */
    long writeDataPoints(Iterable<? extends DataPoint<?>> dataPoints) throws Exception;
}


<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/service/BloodPressureDataPointGenerator.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.service;

import com.google.common.collect.Sets;
import org.openmhealth.data.generator.domain.TimestampedValueGroup;
import org.openmhealth.schema.domain.omh.BloodPressure;
import org.openmhealth.schema.domain.omh.DiastolicBloodPressure;
import org.openmhealth.schema.domain.omh.SystolicBloodPressure;
import org.springframework.stereotype.Component;

import java.util.Set;

import static org.openmhealth.schema.domain.omh.BloodPressureUnit.MM_OF_MERCURY;


/**
 * @author <NAME>
 */
@Component
public class BloodPressureDataPointGenerator
        extends AbstractDataPointGeneratorImpl<BloodPressure> {

    public static final String SYSTOLIC_KEY = "systolic-in-mmhg";
    public static final String DIASTOLIC_KEY = "diastolic-in-mmhg";

    @Override
    public String getName() {
        return "blood-pressure";
    }

    @Override
    public Set<String> getSupportedValueGroupKeys() {
        return Sets.newHashSet(SYSTOLIC_KEY, DIASTOLIC_KEY);
    }

    @Override
    public Set<String> getRequiredValueGroupKeys() {
        return Sets.newHashSet(SYSTOLIC_KEY, DIASTOLIC_KEY);
    }

    @Override
    public BloodPressure newMeasure(TimestampedValueGroup valueGroup) {

        return new BloodPressure.Builder(
                new SystolicBloodPressure(MM_OF_MERCURY, valueGroup.getValue(SYSTOLIC_KEY)),
                new DiastolicBloodPressure(MM_OF_MERCURY, valueGroup.getValue(DIASTOLIC_KEY)))
                .setEffectiveTimeFrame(valueGroup.getTimestamp())
                .build();
    }
}


<|start_filename|>backend/src/main/java/org/openmhealth/data/generator/domain/BoundedRandomVariable.java<|end_filename|>
/*
 * Copyright 2014 Open mHealth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openmhealth.data.generator.domain;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.security.SecureRandom;
import java.util.Random;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;


/**
 * A random variable that models normally-distributed real values. If limits are specified, returned values will
 * fall between the specified bounds.
 *
 * @author <NAME>
 */
// TODO refactor this into a container object that wraps a variable and a trend
public class BoundedRandomVariable {

    private static final Random prng = new SecureRandom();

    private Double variance = 0.0;
    private Double standardDeviation = 0.0;
    private Double minimumValue;
    private Double maximumValue;


    public BoundedRandomVariable() {
    }

    public BoundedRandomVariable(Double standardDeviation) {

        checkNotNull(standardDeviation);
        setStandardDeviation(standardDeviation);
    }

    public BoundedRandomVariable(Double standardDeviation, Double minimumValue, Double maximumValue) {

        checkNotNull(standardDeviation);
        checkNotNull(minimumValue);
        checkNotNull(maximumValue);
        checkArgument(minimumValue <= maximumValue);

        setStandardDeviation(standardDeviation);
        setMinimumValue(minimumValue);
        setMaximumValue(maximumValue);
    }

    @NotNull
    @Min(0)
    public Double getVariance() {
        return variance;
    }

    public void setVariance(Double variance) {

        checkNotNull(variance);
        checkArgument(variance >= 0);

        this.variance = variance;
        this.standardDeviation = Math.sqrt(variance);
    }

    @NotNull
    @Min(0)
    public Double getStandardDeviation() {
        return standardDeviation;
    }

    public void setStandardDeviation(Double standardDeviation) {

        checkNotNull(standardDeviation);
        checkArgument(standardDeviation >= 0);

        this.standardDeviation = standardDeviation;
        this.variance = Math.pow(standardDeviation, 2.0);
    }

    /**
     * @return a lower bound on the values that can be generated
     */
    public Double getMinimumValue() {
        return minimumValue;
    }

    public void setMinimumValue(Double minimumValue) {

        checkArgument(minimumValue == null || maximumValue == null || minimumValue <= maximumValue);

        this.minimumValue = minimumValue;
    }

    /**
     * @return an upper bound on the values that can be generated
     */
    public Double getMaximumValue() {
        return maximumValue;
    }

    public void setMaximumValue(Double maximumValue) {

        checkArgument(minimumValue == null || maximumValue == null || minimumValue <= maximumValue);

        this.maximumValue = maximumValue;
    }

    /**
     * @param mean the mean of the random variable
     * @return the next value generated by the random variable
     */
    public Double nextValue(Double mean) {

        Double nextValue;

        do {
            nextValue = prng.nextGaussian() * standardDeviation + mean;

            if ((minimumValue == null || nextValue >= minimumValue) &&
                    (maximumValue == null || nextValue <= maximumValue)) {
                break;
            }
        }
        while (true);

        return nextValue;
    }

    @Override
    public String toString() {

        final StringBuilder sb = new StringBuilder("BoundedRandomVariable{");

        sb.append("variance=").append(variance);
        sb.append(", standardDeviation=").append(standardDeviation);
        sb.append(", minimumValue=").append(minimumValue);
        sb.append(", maximumValue=").append(maximumValue);
        sb.append('}');

        return sb.toString();
    }
}
