package org.fluxtream.connectors.bodymedia; import java.util.ArrayList; import java.util.List; import java.util.TimeZone; import org.fluxtream.core.ApiData; import org.fluxtream.core.TimezoneMap; import org.fluxtream.core.aspects.FlxLogger; import org.fluxtream.core.connectors.ObjectType; import org.fluxtream.core.connectors.updaters.UpdateInfo; import org.fluxtream.core.domain.AbstractFacet; import org.fluxtream.core.facets.extractors.AbstractFacetExtractor; import org.fluxtream.core.services.ConnectorUpdateService; import org.fluxtream.core.services.MetadataService; import org.fluxtream.core.utils.TimeUtils; import net.sf.json.JSONArray; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.joda.time.DateTime; import org.joda.time.DateTimeConstants; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.joda.time.LocalDate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; /** * Extracts information from the apicall and creates a facet */ @Component public class BodymediaBurnFacetExtractor extends AbstractFacetExtractor { //Logs various transactions FlxLogger logger = FlxLogger.getLogger(BodymediaBurnFacetExtractor.class); DateTimeFormatter form = DateTimeFormat.forPattern("yyyyMMdd'T'HHmmssZ"); DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyyMMdd"); @Qualifier("metadataServiceImpl") @Autowired MetadataService metadataService; @Qualifier("connectorUpdateServiceImpl") @Autowired ConnectorUpdateService connectorUpdateService; @Override public List<AbstractFacet> extractFacets(final UpdateInfo updateInfo, final ApiData apiData, ObjectType objectType) throws Exception { logger.info("guestId=" + apiData.updateInfo.getGuestId() + " connector=bodymedia action=extractFacets objectType=" + objectType.getName()); ArrayList<AbstractFacet> facets; String name = objectType.getName(); if(name.equals("burn")) { facets = extractBurnFacets(updateInfo,apiData); } else //If the facet to be extracted wasn't a burn facet { throw new JSONException("Burn extractor called with illegal ObjectType"); } return facets; } /** * Extracts facets for each day from the data returned by the api. * @param apiData The data returned by the Burn api * @return A list of facets for each day provided by the apiData */ private ArrayList<AbstractFacet> extractBurnFacets(final UpdateInfo updateInfo, ApiData apiData) { ArrayList<AbstractFacet> facets = new ArrayList<AbstractFacet>(); /* burnJson is a JSONArray that contains a seperate JSONArray and calorie counts for each day */ JSONObject bodymediaResponse = JSONObject.fromObject(apiData.json); if(bodymediaResponse.has("days") && bodymediaResponse.has("lastSync")) { DateTime d = form.parseDateTime(bodymediaResponse.getJSONObject("lastSync").getString("dateTime")); // Get timezone map from UpdateInfo context TimezoneMap tzMap = (TimezoneMap)updateInfo.getContext("tzMap"); // Insert lastSync into the updateInfo context so it's accessible to the updater updateInfo.setContext("lastSync", d); JSONArray daysArray = bodymediaResponse.getJSONArray("days"); for(Object o : daysArray) { if(o instanceof JSONObject) { JSONObject day = (JSONObject) o; BodymediaBurnFacet burn = new BodymediaBurnFacet(apiData.updateInfo.apiKey.getId()); //The following call must be made to load data about he facets super.extractCommonFacetData(burn, apiData); burn.setTotalCalories(day.getInt("totalCalories")); burn.date = day.getString("date"); burn.setEstimatedCalories(day.getInt("estimatedCalories")); burn.setPredictedCalories(day.getInt("predictedCalories")); burn.json = day.getString("minutes"); burn.lastSync = d.getMillis(); DateTime date = formatter.parseDateTime(day.getString("date")); burn.date = TimeUtils.dateFormatter.print(date.getMillis()); if(tzMap!=null) { // Create a LocalDate object which just captures the date without any // timezone assumptions LocalDate ld = new LocalDate(date.getYear(),date.getMonthOfYear(),date.getDayOfMonth()); // Use tzMap to convert date into a datetime with timezone information DateTime realDateStart = tzMap.getStartOfDate(ld); // Set the start and end times for the facet. The start time is the leading midnight // of burn.date according to BodyMedia's idea of what timezone you were in then. // End should, I think, be start + the number of minutes in the minutes array * // the number of milliseconds in a minute. burn.start = realDateStart.getMillis(); int minutesLength = 1440; burn.end = burn.start + DateTimeConstants.MILLIS_PER_MINUTE * minutesLength; } else { // This is the old code from Prasanth that uses metadataService, which isn't right TimeZone timeZone = metadataService.getTimeZone(apiData.updateInfo.getGuestId(), date.getMillis()); long fromMidnight = TimeUtils.fromMidnight(date.getMillis(), timeZone); long toMidnight = TimeUtils.toMidnight(date.getMillis(), timeZone); //Sets the start and end times for the facet so that it can be uniquely defined burn.start = fromMidnight; burn.end = toMidnight; } facets.add(burn); } else throw new RuntimeException("days array is not a proper JSONObject"); } } return facets; } }