package org.fluxtream.connectors.fitbit;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.fluxtream.core.connectors.updaters.AbstractUpdater;
import org.fluxtream.core.connectors.updaters.UpdateInfo;
import org.fluxtream.core.domain.AbstractLocalTimeFacet;
import org.fluxtream.core.facets.extractors.AbstractFacetExtractor;
import org.fluxtream.core.services.ApiDataService;
import org.fluxtream.core.utils.TimeUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* User: candide
* Date: 06/11/14
* Time: 21:49
*/
@Component
public class FitbitPersistenceHelper {
@Autowired
ApiDataService apiDataService;
List<FitbitWeightFacet> createOrUpdateWeightFacets(final UpdateInfo updateInfo, final JSONObject fitbitResponse) throws Exception {
JSONArray fitbitWeightMeasurements = fitbitResponse.getJSONArray("weight");
List<FitbitWeightFacet> weightFacets = new ArrayList<FitbitWeightFacet>();
for(int i=0; i<fitbitWeightMeasurements.size(); i++) {
final FitbitWeightFacet createdOrUpdatedWeightFacet = createOrUpdateWeightFacet(updateInfo, fitbitWeightMeasurements.getJSONObject(i));
if (createdOrUpdatedWeightFacet!=null)
weightFacets.add(createdOrUpdatedWeightFacet);
}
return weightFacets;
}
private FitbitWeightFacet createOrUpdateWeightFacet(final UpdateInfo updateInfo, final JSONObject fitbitWeightMeasurement) throws Exception {
final ApiDataService.FacetQuery facetQuery = new ApiDataService.FacetQuery(
"e.apiKeyId=? AND e.logId=?",
updateInfo.apiKey.getId(), fitbitWeightMeasurement.getLong("logId"));
final ApiDataService.FacetModifier<FitbitWeightFacet> facetModifier = new ApiDataService.FacetModifier<FitbitWeightFacet>() {
@Override
public FitbitWeightFacet createOrModify(FitbitWeightFacet facet, Long apiKeyId) throws Exception {
if (facet == null) {
facet = new FitbitWeightFacet(updateInfo.apiKey.getId());
}
AbstractUpdater.extractCommonFacetData(facet, updateInfo);
facet.date = (String) updateInfo.getContext("date");
facet.startTimeStorage = facet.endTimeStorage = AbstractFacetExtractor.noon(facet.date);
if (fitbitWeightMeasurement.containsKey("logId"))
facet.logId = fitbitWeightMeasurement.getLong("logId");
if (fitbitWeightMeasurement.containsKey("bmi"))
facet.bmi = fitbitWeightMeasurement.getDouble("bmi");
if (fitbitWeightMeasurement.containsKey("fat"))
facet.fat = fitbitWeightMeasurement.getDouble("fat");
if (fitbitWeightMeasurement.containsKey("weight"))
facet.weight = fitbitWeightMeasurement.getDouble("weight");
if (fitbitWeightMeasurement.containsKey("time")) {
String time = fitbitWeightMeasurement.getString("time");
String[] timeParts = time.split(":");
int hours = Integer.valueOf(timeParts[0]);
int minutes = Integer.valueOf(timeParts[1]);
int seconds = Integer.valueOf(timeParts[2]);
String[] dateParts = facet.date.split("-");
int year = Integer.valueOf(dateParts[0]);
int month = Integer.valueOf(dateParts[1]);
int day = Integer.valueOf(dateParts[2]);
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
c.set(year, month-1, day, hours, minutes, seconds);
c.set(Calendar.MILLISECOND, 0);
facet.start = facet.end = c.getTimeInMillis();
facet.startTimeStorage = facet.endTimeStorage = AbstractFacetExtractor.toTimeStorage(year, month, day, hours, minutes, seconds);
}
return facet;
}
};
return apiDataService.createOrReadModifyWrite(FitbitWeightFacet.class, facetQuery, facetModifier, updateInfo.apiKey.getId());
}
FitbitFoodLogSummaryFacet createOrUpdateFoodLogSummaryFacet(final UpdateInfo updateInfo, final JSONObject fitbitResponse) throws Exception {
final ApiDataService.FacetQuery facetQuery = new ApiDataService.FacetQuery(
"e.apiKeyId=? AND e.date=?",
updateInfo.apiKey.getId(), updateInfo.getContext("date"));
final ApiDataService.FacetModifier<FitbitFoodLogSummaryFacet> facetModifier = new ApiDataService.FacetModifier<FitbitFoodLogSummaryFacet>() {
@Override
public FitbitFoodLogSummaryFacet createOrModify(FitbitFoodLogSummaryFacet facet, Long apiKeyId) throws Exception {
if (facet == null) {
facet = new FitbitFoodLogSummaryFacet(updateInfo.apiKey.getId());
}
AbstractUpdater.extractCommonFacetData(facet, updateInfo);
setFacetTimeBounds(updateInfo, facet);
JSONObject fitbitSummary = fitbitResponse.getJSONObject("summary");
if (fitbitSummary.has("calories"))
facet.calories = fitbitSummary.getInt("calories");
if (fitbitSummary.has("carbs"))
facet.carbs = (float) fitbitSummary.getDouble("carbs");
if (fitbitSummary.has("fat"))
facet.fat = (float) fitbitSummary.getDouble("fat");
if (fitbitSummary.has("fiber"))
facet.fiber = (float) fitbitSummary.getDouble("fiber");
if (fitbitSummary.has("protein"))
facet.protein = (float) fitbitSummary.getDouble("protein");
if (fitbitSummary.has("sodium"))
facet.sodium = (float) fitbitSummary.getDouble("sodium");
if (fitbitSummary.has("water"))
facet.water = (float) fitbitSummary.getDouble("water");
if (fitbitResponse.has("goals")) {
JSONObject goals = fitbitResponse.getJSONObject("goals");
if (goals.has("calories"))
facet.caloriesGoal = goals.getInt("calories");
if (goals.has("estimatedCaloriesOut"))
facet.caloriesOutGoal = goals.getInt("estimatedCaloriesOut");
}
return facet;
}
};
return apiDataService.createOrReadModifyWrite(FitbitFoodLogSummaryFacet.class, facetQuery, facetModifier, updateInfo.apiKey.getId());
}
void createOrUpdateFoodEntry(final UpdateInfo updateInfo, final JSONObject foodEntry) throws Exception {
final ApiDataService.FacetQuery facetQuery = new ApiDataService.FacetQuery(
"e.apiKeyId=? AND e.logId=?",
updateInfo.apiKey.getId(), foodEntry.getLong("logId"));
final ApiDataService.FacetModifier<FitbitFoodLogEntryFacet> facetModifier = new ApiDataService.FacetModifier<FitbitFoodLogEntryFacet>() {
@Override
public FitbitFoodLogEntryFacet createOrModify(FitbitFoodLogEntryFacet facet, Long apiKeyId) throws Exception {
if (facet == null) {
facet = new FitbitFoodLogEntryFacet(updateInfo.apiKey.getId());
}
AbstractUpdater.extractCommonFacetData(facet, updateInfo);
setFacetTimeBounds(updateInfo, facet);
facet.isFavorite = foodEntry.getBoolean("isFavorite");
facet.logId = foodEntry.getLong("logId");
if (foodEntry.has("loggedFood")) {
JSONObject loggedFood = foodEntry.getJSONObject("loggedFood");
if (loggedFood.has("accessLevel"))
facet.accessLevel = loggedFood.getString("accessLevel");
if (loggedFood.has("amount"))
facet.amount = (float) loggedFood.getDouble("amount");
if (loggedFood.has("brand"))
facet.brand = loggedFood.getString("brand");
if (loggedFood.has("calories"))
facet.calories = loggedFood.getInt("calories");
if (loggedFood.has("foodId"))
facet.foodId = loggedFood.getLong("foodId");
if (loggedFood.has("mealTypeId"))
facet.mealTypeId = loggedFood.getInt("mealTypeId");
if (loggedFood.has("locale"))
facet.locale = loggedFood.getString("locale");
if (loggedFood.has("name"))
facet.name = loggedFood.getString("name");
if (loggedFood.has("unit")) {
JSONObject unit = loggedFood.getJSONObject("unit");
facet.unitId = unit.getInt("id");
facet.unitName = unit.getString("name");
facet.unitPlural = unit.getString("plural");
}
}
if (foodEntry.has("nutritionalValues")) {
JSONObject nutritionalValues = foodEntry.getJSONObject("nutritionalValues");
if (nutritionalValues.has("calories"))
facet.NV_Calories = nutritionalValues.getInt("calories");
if (nutritionalValues.has("carbs"))
facet.NV_Carbs = nutritionalValues.getInt("carbs");
if (nutritionalValues.has("fat"))
facet.NV_Fat = nutritionalValues.getInt("fat");
if (nutritionalValues.has("fiber"))
facet.NV_Fiber = nutritionalValues.getInt("fiber");
if (nutritionalValues.has("protein"))
facet.NV_Protein = nutritionalValues.getInt("protein");
if (nutritionalValues.has("sodium"))
facet.NV_Sodium = nutritionalValues.getInt("sodium");
}
return facet;
}
};
apiDataService.createOrReadModifyWrite(FitbitFoodLogEntryFacet.class, facetQuery, facetModifier, updateInfo.apiKey.getId());
}
void createOrUpdateLoggedActivities(final UpdateInfo updateInfo, final JSONObject fitbitResponse) throws Exception {
JSONArray loggedActivities = fitbitResponse.getJSONArray("activities");
if (loggedActivities == null || loggedActivities.size() == 0)
return;
for (int i=0; i<loggedActivities.size(); i++)
createOrUpdateLoggedActivity(updateInfo, loggedActivities.getJSONObject(i));
}
private void createOrUpdateLoggedActivity(final UpdateInfo updateInfo, final JSONObject loggedActivity) throws Exception {
final ApiDataService.FacetQuery facetQuery = new ApiDataService.FacetQuery(
"e.apiKeyId=? AND e.logId=?",
updateInfo.apiKey.getId(), loggedActivity.getLong("logId"));
final ApiDataService.FacetModifier<FitbitLoggedActivityFacet> facetModifier = new ApiDataService.FacetModifier<FitbitLoggedActivityFacet>() {
@Override
public FitbitLoggedActivityFacet createOrModify(FitbitLoggedActivityFacet facet, Long apiKeyId) throws Exception {
if (facet == null)
facet = new FitbitLoggedActivityFacet(updateInfo.apiKey.getId());
AbstractUpdater.extractCommonFacetData(facet, updateInfo);
facet.date = (String) updateInfo.getContext("date");
final String startTime = loggedActivity.getString("startTime");
facet.startTimeStorage = facet.endTimeStorage = facet.date+"T" + startTime+":00.000";
final DateTime startDateTime = AbstractLocalTimeFacet.timeStorageFormat.withZoneUTC().parseDateTime(facet.startTimeStorage);
facet.start = startDateTime.getMillis();
if (loggedActivity.containsKey("duration")) {
final int duration = loggedActivity.getInt("duration");
final DateTime endDateTime = startDateTime.plus(duration);
facet.endTimeStorage = AbstractLocalTimeFacet.timeStorageFormat.withZoneUTC().print(endDateTime.getMillis());
facet.end = endDateTime.getMillis();
}
if (loggedActivity.containsKey("activityId"))
facet.activityId = loggedActivity.getLong("activityId");
if (loggedActivity.containsKey("activityParentId"))
facet.activityParentId = loggedActivity
.getLong("activityParentId");
if (loggedActivity.containsKey("calories"))
facet.calories = loggedActivity.getInt("calories");
if (loggedActivity.containsKey("description"))
facet.fullTextDescription = loggedActivity
.getString("description");
if (loggedActivity.containsKey("distance"))
facet.distance = loggedActivity.getInt("distance");
if (loggedActivity.containsKey("isFavorite"))
facet.isFavorite = loggedActivity.getBoolean("isFavorite");
if (loggedActivity.containsKey("logId"))
facet.logId = loggedActivity.getLong("logId");
if (loggedActivity.containsKey("name"))
facet.name = loggedActivity.getString("name");
if (loggedActivity.containsKey("steps"))
facet.steps = loggedActivity.getInt("steps");
return facet;
}
};
apiDataService.createOrReadModifyWrite(FitbitLoggedActivityFacet.class, facetQuery, facetModifier, updateInfo.apiKey.getId());
}
FitbitTrackerActivityFacet createOrUpdateActivitySummary(final UpdateInfo updateInfo, final JSONObject fitbitResponse) throws Exception {
final ApiDataService.FacetQuery facetQuery = new ApiDataService.FacetQuery(
"e.apiKeyId=? AND e.date=?",
updateInfo.apiKey.getId(), updateInfo.getContext("date"));
final ApiDataService.FacetModifier<FitbitTrackerActivityFacet> facetModifier = new ApiDataService.FacetModifier<FitbitTrackerActivityFacet>() {
@Override
public FitbitTrackerActivityFacet createOrModify(FitbitTrackerActivityFacet facet, Long apiKeyId) throws Exception {
if (facet==null)
facet = new FitbitTrackerActivityFacet(updateInfo.apiKey.getId());
JSONObject fitbitSummary = fitbitResponse.getJSONObject("summary");
AbstractUpdater.extractCommonFacetData(facet, updateInfo);
facet.date = (String) updateInfo.getContext("date");
final DateTime dateTime = TimeUtils.dateFormatterUTC.parseDateTime(facet.date);
// returns the starting midnight for the date
facet.start = dateTime.getMillis();
facet.end = dateTime.getMillis()+ DateTimeConstants.MILLIS_PER_DAY-1;
facet.startTimeStorage = facet.date + "T00:00:00.000";
facet.endTimeStorage = facet.date + "T23:59:59.999";
if (fitbitSummary.containsKey("activeScore"))
facet.activeScore = fitbitSummary.getInt("activeScore");
if (fitbitSummary.containsKey("floors"))
facet.floors = fitbitSummary.getInt("floors");
if (fitbitSummary.containsKey("elevation"))
facet.elevation = fitbitSummary.getInt("elevation");
if (fitbitSummary.containsKey("caloriesOut"))
facet.caloriesOut = fitbitSummary.getInt("caloriesOut");
if (fitbitSummary.containsKey("fairlyActiveMinutes"))
facet.fairlyActiveMinutes = fitbitSummary
.getInt("fairlyActiveMinutes");
if (fitbitSummary.containsKey("lightlyActiveMinutes"))
facet.lightlyActiveMinutes = fitbitSummary
.getInt("lightlyActiveMinutes");
if (fitbitSummary.containsKey("sedentaryMinutes"))
facet.sedentaryMinutes = fitbitSummary.getInt("sedentaryMinutes");
if (fitbitSummary.containsKey("veryActiveMinutes"))
facet.veryActiveMinutes = fitbitSummary.getInt("veryActiveMinutes");
if (fitbitSummary.containsKey("steps"))
facet.steps = fitbitSummary.getInt("steps");
if (fitbitSummary.has("distances")) {
JSONArray distancesArray = fitbitSummary.getJSONArray("distances");
for (int i=0; i<distancesArray.size(); i++) {
JSONObject distanceObject = distancesArray.getJSONObject(i);
final String activityType = distanceObject.getString("activity");
final double distance = distanceObject.getDouble("distance");
if (activityType.equals("tracker"))
facet.trackerDistance = distance;
else if (activityType.equals("loggedActivities"))
facet.loggedActivitiesDistance = distance;
else if (activityType.equals("veryActive"))
facet.veryActiveDistance = distance;
else if (activityType.equals("total"))
facet.totalDistance = distance;
else if (activityType.equals("moderatelyActive"))
facet.moderatelyActiveDistance = distance;
else if (activityType.equals("lightlyActive"))
facet.lightlyActiveDistance = distance;
else if (activityType.equals("sedentary"))
facet.sedentaryActiveDistance = distance;
}
}
return facet;
}
};
return apiDataService.createOrReadModifyWrite(FitbitTrackerActivityFacet.class, facetQuery, facetModifier, updateInfo.apiKey.getId());
}
void createOrUpdateSleepFacets(final UpdateInfo updateInfo, final JSONObject fitbitResponse) throws Exception {
JSONArray sleepRecords = fitbitResponse.getJSONArray("sleep");
Iterator iterator = sleepRecords.iterator();
while (iterator.hasNext()) {
JSONObject record = (JSONObject) iterator.next();
createOrUpdateSleepFacet(updateInfo, record);
}
}
private void createOrUpdateSleepFacet(final UpdateInfo updateInfo, final JSONObject record) throws Exception {
final ApiDataService.FacetQuery facetQuery = new ApiDataService.FacetQuery(
"e.apiKeyId=? AND e.logId=?",
updateInfo.apiKey.getId(), record.getLong("logId"));
final ApiDataService.FacetModifier<FitbitSleepFacet> facetModifier = new ApiDataService.FacetModifier<FitbitSleepFacet>() {
@Override
public FitbitSleepFacet createOrModify(FitbitSleepFacet facet, Long apiKeyId) throws Exception {
if (facet == null)
facet = new FitbitSleepFacet(updateInfo.apiKey.getId());
int duration = record.getInt("duration");
if (duration==0)
return null;
AbstractUpdater.extractCommonFacetData(facet, updateInfo);
String startTime = record.getString("startTime");
facet.duration = duration;
if (record.containsKey("minutesAwake"))
facet.minutesAwake = record.getInt("minutesAwake");
if (record.containsKey("minutesAsleep"))
facet.minutesAsleep = record.getInt("minutesAsleep");
if (record.containsKey("minutesToFallAsleep"))
facet.minutesToFallAsleep = record
.getInt("minutesToFallAsleep");
facet.date = (String) updateInfo.getContext("date");
final long startTimeMillis = AbstractLocalTimeFacet.timeStorageFormat.withZoneUTC().parseMillis(startTime);
facet.start = startTimeMillis;
facet.end = startTimeMillis + duration;
facet.startTimeStorage = startTime;
final long endTimeMillis = startTimeMillis + duration;
facet.endTimeStorage = AbstractLocalTimeFacet.timeStorageFormat.withZoneUTC().print(endTimeMillis);
if (record.containsKey("awakeningsCount"))
facet.awakeningsCount = record.getInt("awakeningsCount");
if (record.containsKey("timeInBed"))
facet.timeInBed = record.getInt("timeInBed");
return facet;
}
};
apiDataService.createOrReadModifyWrite(FitbitSleepFacet.class, facetQuery, facetModifier, updateInfo.apiKey.getId());
}
private void setFacetTimeBounds(final UpdateInfo updateInfo, AbstractLocalTimeFacet facet) {
facet.date = (String) updateInfo.getContext("date");
final DateTime dateTime = TimeUtils.dateFormatterUTC.parseDateTime(facet.date);
// returns the starting midnight for the date
facet.start = dateTime.getMillis();
facet.end = dateTime.getMillis()+ DateTimeConstants.MILLIS_PER_DAY-1;
facet.startTimeStorage = facet.date + "T00:00:00.000";
facet.endTimeStorage = facet.date + "T23:59:59.999";
}
}