Java tutorial
/* * Copyright 2015 Open mHealth * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openmhealth.shim.misfit.mapper; import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.DataPoint; import org.openmhealth.schema.domain.omh.DurationUnitValue; import org.openmhealth.schema.domain.omh.SleepDuration; import org.openmhealth.shim.common.mapper.JsonNodeMappingException; import java.time.Duration; import java.time.OffsetDateTime; import java.util.Optional; import static java.lang.String.format; import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; import static org.openmhealth.schema.domain.omh.TimeInterval.ofStartDateTimeAndEndDateTime; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.*; /** * A mapper from Misfit Resource API /activity/sleeps responses to {@link SleepDuration} objects. This mapper * currently creates a single data point per sleep node in the response, subtracting the duration of awake segments * from the sleep duration. It's also possible to create a single data point per sleep segment, which would help * preserve the granularity of the original data. This mapper may be updated to return a data point per segment in the * future. * * @author Emerson Farrugia * @see <a href="https://build.misfit.com/docs/references#APIReferences-Sleep">API documentation</a> */ public class MisfitSleepDurationDataPointMapper extends MisfitDataPointMapper<SleepDuration> { public static final int AWAKE_SEGMENT_TYPE = 1; @Override protected String getListNodeName() { return "sleeps"; } @Override public Optional<DataPoint<SleepDuration>> asDataPoint(JsonNode sleepNode) { // The sleep details array contains segments corresponding to whether the user was awake, sleeping lightly, // or sleeping restfully for the duration of that segment. To discount the awake segments, we have to deduct // their duration from the total sleep duration. JsonNode sleepDetailsNode = asRequiredNode(sleepNode, "sleepDetails"); long awakeDurationInSec = 0; OffsetDateTime previousSegmentStartDateTime = null; Long previousSegmentType = null; for (JsonNode sleepDetailSegmentNode : sleepDetailsNode) { OffsetDateTime startDateTime = asRequiredOffsetDateTime(sleepDetailSegmentNode, "datetime"); Long value = asRequiredLong(sleepDetailSegmentNode, "value"); // if the user was awake, add it to the awake tally if (previousSegmentType != null && previousSegmentType == AWAKE_SEGMENT_TYPE) { awakeDurationInSec += Duration.between(previousSegmentStartDateTime, startDateTime).getSeconds(); } previousSegmentStartDateTime = startDateTime; previousSegmentType = value; } // checking if the segment array is empty this way avoids compiler confusion later if (previousSegmentType == null) { throw new JsonNodeMappingException( format("The Misfit sleep node '%s' has no sleep details.", sleepNode)); } // to calculate the duration of last segment, first determine the overall end time OffsetDateTime startDateTime = asRequiredOffsetDateTime(sleepNode, "startTime"); Long totalDurationInSec = asRequiredLong(sleepNode, "duration"); OffsetDateTime endDateTime = startDateTime.plusSeconds(totalDurationInSec); if (previousSegmentType == AWAKE_SEGMENT_TYPE) { awakeDurationInSec += Duration.between(previousSegmentStartDateTime, endDateTime).getSeconds(); } Long sleepDurationInSec = totalDurationInSec - awakeDurationInSec; if (sleepDurationInSec == 0) { return Optional.empty(); } SleepDuration measure = new SleepDuration.Builder(new DurationUnitValue(SECOND, sleepDurationInSec)) .setEffectiveTimeFrame(ofStartDateTimeAndEndDateTime(startDateTime, endDateTime)).build(); String externalId = asOptionalString(sleepNode, "id").orElse(null); Boolean sensed = asOptionalBoolean(sleepNode, "autoDetected").orElse(null); return Optional.of(newDataPoint(measure, RESOURCE_API_SOURCE_NAME, externalId, sensed)); } }