package org.fluxtream.connectors.quantifiedmind;
import java.io.IOException;
import org.fluxtream.core.connectors.Connector;
import org.fluxtream.core.connectors.annotations.Updater;
import org.fluxtream.core.connectors.updaters.AbstractUpdater;
import org.fluxtream.core.connectors.updaters.UpdateInfo;
import org.fluxtream.core.domain.ApiKey;
import org.fluxtream.core.services.GuestService;
import org.fluxtream.core.utils.HttpUtils;
import org.fluxtream.core.utils.UnexpectedHttpResponseCodeException;
import org.fluxtream.core.utils.Utils;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author candide
*
*/
@Component
@Updater(prettyName = "QuantifiedMind", value = 100, updateStrategyType = Connector.UpdateStrategyType.INCREMENTAL,
objectTypes = {QuantifiedMindTestFacet.class}, extractor = QuantifiedMindTestFacetExtractor.class)
public class QuantifiedMindUpdater extends AbstractUpdater {
@Autowired
GuestService guestService;
public QuantifiedMindUpdater() {
super();
}
@Override
protected void updateConnectorDataHistory(final UpdateInfo updateInfo) throws Exception {
// Don't do anything special for initial history update; updateStartMillis will default to 0
updateConnectorData(updateInfo);
}
@Override
public void updateConnectorData(UpdateInfo updateInfo) throws Exception {
// Get start time from the stored attributes and update through now
long updateStartMillis = getUpdateStartMillis(updateInfo);
loadHistory(updateInfo, updateStartMillis, System.currentTimeMillis());
}
// Retrieve the stored value of when to start update from
long getUpdateStartMillis(UpdateInfo updateInfo) {
String updateKeyName = "updateStartMillis";
String updateStartMillisAttrib = guestService.getApiKeyAttribute(updateInfo.apiKey, updateKeyName);
// The first time we do this there won't be an apiKeyAttribute yet. In that case default to 0
// Otherwise parse it out of the attribute
if(updateStartMillisAttrib != null) {
// There is a stored update start time, parse it
try {
long updateStartMillis = Long.valueOf(updateStartMillisAttrib);
return(updateStartMillis);
}
catch (Throwable e) {
// Don't worry about parse errors, just start over at 0
}
}
return 0;
}
// Update the current progress on the update so we can know where to start next time.
void setUpdateStartMillis(UpdateInfo updateInfo, long updateProgressMillis)
{
String updateKeyName = "updateStartMillis";
guestService.setApiKeyAttribute(updateInfo.apiKey, updateKeyName, String.valueOf(updateProgressMillis));
}
// Get and update data between the start and end times specified, updating updateStartMillis as we go
private void loadHistory(UpdateInfo updateInfo, long from, long to) throws Exception {
String queryUrl = "request url not set yet";
long then = System.currentTimeMillis();
String username = guestService.getApiKeyAttribute(updateInfo.apiKey,
"username");
String token = guestService.getApiKeyAttribute(updateInfo.apiKey,
"token");
try {
boolean partialResult = false;
// Start at the from time specified by the caller, and handle additional pages if we need to,
// updating updateStartMillis each time we're successful. from and to are in units of long milliseconds
// and start_time and next_date are in units of double seconds. Do the conversion.
String start_time = String.valueOf(from/1000.0);
do {
queryUrl = "http://www.quantified-mind.com/api/get_session_data?username=" + username + "&token=" + token;
queryUrl += "&start_time=" + start_time;
final String json = HttpUtils.fetch(queryUrl);
countSuccessfulApiCall(updateInfo.apiKey, updateInfo.objectTypes, then, queryUrl);
JSONObject jsonObject = JSONObject.fromObject(json);
String status = jsonObject.getString("status");
partialResult = status.equals("partial");
JSONArray sessionData = jsonObject.getJSONArray("session_data");
final String sessionDataJSON = sessionData.toString();
apiDataService.cacheApiDataJSON(updateInfo, sessionDataJSON, -1, -1);
if (partialResult) {
// If we got a partial result, save next_date as the updateStartMillis
// to start from next time
start_time = jsonObject.getString("next_date");
// next_date is floating point; parse as a Double then convert to long
setUpdateStartMillis(updateInfo, (long)Math.floor(Double.valueOf(start_time)*1000.0));
}
} while (partialResult);
}
catch (UnexpectedHttpResponseCodeException e) {
countFailedApiCall(updateInfo.apiKey, updateInfo.objectTypes, then,
queryUrl, Utils.stackTrace(e), e.getHttpResponseCode(), e.getHttpResponseMessage());
throw new Exception("Could not get QuantifiedMind tests: "
+ e.getMessage() + "\n" + Utils.stackTrace(e));
}
catch (IOException e) {
reportFailedApiCall(updateInfo.apiKey, updateInfo.objectTypes, then,
queryUrl, Utils.stackTrace(e), "I/O");
throw new Exception("Unexpected error, getting QuantifiedMind tests: "
+ e.getMessage() + "\n" + Utils.stackTrace(e));
}
// We completed successfully and processed all data until the present. Store the value of "to",
// which is the time we started the update, as the time to start from next time in order to
// be conservative about not missing items which appeared between the start of the update and now.
setUpdateStartMillis(updateInfo, Long.valueOf(to));
}
@Override
public void setDefaultChannelStyles(ApiKey apiKey) {}
}