package org.fluxtream.connectors.moves; import com.wordnik.swagger.annotations.ApiModel; import com.wordnik.swagger.annotations.ApiModelProperty; import org.codehaus.plexus.util.StringUtils; import org.fluxtream.core.OutsideTimeBoundariesException; import org.fluxtream.core.domain.GuestSettings; import org.fluxtream.core.mvc.models.DurationModel; import org.joda.time.DateTimeZone; import org.joda.time.format.ISODateTimeFormat; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.TimeZone; /** * User: candide * Date: 20/06/13 * Time: 17:33 */ @ApiModel(value="Moves Activity", description="") public class MovesActivityVO { @ApiModelProperty(required=true) public String eventStart; @ApiModelProperty(required=true) public String eventEnd; @ApiModelProperty(required=true, value="Was this activity entered manually?") public boolean manual; @ApiModelProperty(required=true, value="This activity's human-readable name") public String activity; @ApiModelProperty(notes="For a complete list, please refer to https://dev.moves-app.com/docs/api_activity_list", value="The moves API code for this activity", required=true) public String activityCode = "generic"; @ApiModelProperty(value="Generic Activity Code", allowableValues = "walking, cycling, transport, running", required=true) public String activityGroup; @ApiModelProperty(required=true) public String distance; @ApiModelProperty(required=true) public Integer steps; @ApiModelProperty(required=true) public DurationModel duration; @ApiModelProperty(required=true) public String date; @ApiModelProperty(required=true) public final String type = "moves-move-activity"; private static final ArrayList<String> validActivityCodes = new ArrayList<String>(Arrays.asList(new String[]{"running", "walking", "cycling", "transport"})); public MovesActivityVO(MovesActivity activity, TimeZone timeZone, long dateStart, long dateEnd, GuestSettings settings, boolean doDateBoundsCheck) throws OutsideTimeBoundariesException { this.activity = StringUtils.capitalise(activity.activity); if (activity.activityGroup!=null&&validActivityCodes.contains(activity.activityGroup)) this.activityCode = activity.activityGroup; else if (activity.activityGroup==null&&validActivityCodes.contains(activity.activity)) this.activityCode = activity.activity; this.date = activity.date; this.eventStart = ISODateTimeFormat.dateTime().withZone(DateTimeZone.forTimeZone(timeZone)).print(activity.start); this.eventEnd = ISODateTimeFormat.dateTime().withZone(DateTimeZone.forTimeZone(timeZone)).print(activity.end); this.activityGroup = activity.activityGroup; this.manual = activity.manual!=null?activity.manual:false; // Potentially trucate to fit within the date long truncStartMilli = activity.start; long truncEndMilli = activity.end; boolean timeTruncated = false; // If we're doing date bounds checking, check if facet is entirely outside the time bounds of this date // If so, throw an exception so this facet isn't returned. Otherwise potentially trim the start and end times if(doDateBoundsCheck) { if (activity.end<dateStart || activity.start>dateEnd) { throw new OutsideTimeBoundariesException(); } // We know this facet overlaps the time bounds of date, check if it needs // to be truncated. if(activity.start<dateStart){ truncStartMilli = dateStart; timeTruncated=true; } if(activity.end>=dateEnd) { truncEndMilli = dateEnd-1; timeTruncated=true; } } if (this.manual) { this.duration = new DurationModel(activity.duration); } else { // The args for creating a DurationModel are in seconds. // The units of start and end are milliseconds, so divide by 1000 to // calculate the duration in seconds to pass to the Duration Model. this.duration = new DurationModel((int)((truncEndMilli-truncStartMilli)/1000)); // Note that the distance isn't going to be accurate here if we've done truncation // In that case, skip distance and steps for now if (activity.distance>0 && !timeTruncated) { if (settings.distanceMeasureUnit==GuestSettings.DistanceMeasureUnit.SI) getMetricDistance(activity); else getImperialdistance(activity); } if(activity.steps!=null && !timeTruncated) { this.steps = activity.steps; } } } private void getImperialdistance(final MovesActivity activity) { double yards = activity.distance / 0.9144; double miles = activity.distance * 0.00062137119; if (miles>1) { DecimalFormat df = new DecimalFormat("0.#"); String mstr = df.format(miles); if(mstr.contentEquals("1")) { this.distance = "1 mile"; } else { this.distance = mstr + " miles"; } } else { int yint = (int)(round(yards)); if(yint == 1) { this.distance = "1 yard"; } else { this.distance = yint + " yards"; } } } private void getMetricDistance(final MovesActivity activity) { if (activity.distance>1000) { double km = round((double)activity.distance/1000d); this.distance = km + " km"; } else { this.distance = activity.distance + " m"; } } double round(double v) { return (double) Math.round(v * 100) / 100; } }