Added service and invoker for reporting analytics data

pull/14/head
Sam Bosley 12 years ago
parent 2a0200a715
commit 7f57679469

@ -14,9 +14,11 @@ import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

@ -50,8 +50,7 @@ public class ABTestEventDao extends DatabaseDao<ABTestEvent> {
int currentTimeIntervalIndex = AndroidUtilities.indexOf(
ABTestEvent.TIME_INTERVALS, timeInterval);
if (lastRecordedTimeIntervalIndex < 0 || currentTimeIntervalIndex < 0
|| lastRecordedTimeIntervalIndex >= currentTimeIntervalIndex)
if (lastRecordedTimeIntervalIndex < 0 || currentTimeIntervalIndex < 0)
return false;
long now = DateUtilities.now();

@ -26,6 +26,7 @@ import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
import com.todoroo.astrid.service.abtesting.ABChooser;
import com.todoroo.astrid.service.abtesting.ABTestReporter;
import com.todoroo.astrid.service.abtesting.ABTests;
import com.todoroo.astrid.service.abtesting.FeatureFlipper;
import com.todoroo.astrid.tags.TagService;
@ -63,9 +64,9 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
// com.todoroo.astrid.dao
injectables.put("database", Database.class);
injectables.put("taskDao", TaskDao.class);
injectables.put("metadataDao", MetadataDao.class);
injectables.put("tagDataDao", TagDataDao.class);
injectables.put("taskDao", new TaskDao());
injectables.put("metadataDao", new MetadataDao());
injectables.put("tagDataDao", new TagDataDao());
injectables.put("storeObjectDao", StoreObjectDao.class);
injectables.put("updateDao", UpdateDao.class);
injectables.put("userDao", UserDao.class);
@ -101,7 +102,8 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
// AB testing
injectables.put("abChooser", ABChooser.class);
injectables.put("abTests", new ABTests());
injectables.put("abTestEventDao", ABTestEventDao.class);
injectables.put("abTestEventDao", new ABTestEventDao());
injectables.put("abTestReporter", ABTestReporter.class);
injectables.put("featureFlipper", FeatureFlipper.class);
// com.todoroo.astrid.tags

@ -0,0 +1,119 @@
package com.todoroo.astrid.service.abtesting;
import java.io.IOException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import com.todoroo.andlib.data.DatabaseDao.ModelUpdateListener;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.ABTestEventDao;
import com.todoroo.astrid.data.ABTestEvent;
@SuppressWarnings("nls")
public final class ABTestEventReportingService {
private static final String KEY_TEST = "test";
private static final String KEY_VARIANT = "variant";
private static final String KEY_NEW_USER = "new";
private static final String KEY_ACTIVE_USER = "activated";
private static final String KEY_DAYS = "days";
private static final String KEY_DATE = "date";
@Autowired
private ABTestEventDao abTestEventDao;
@Autowired
private ABTestReporter abTestReporter;
public ABTestEventReportingService() {
DependencyInjectionService.getInstance().inject(this);
}
public void initialize() {
abTestEventDao.addListener(new ModelUpdateListener<ABTestEvent>() {
@Override
public void onModelUpdated(ABTestEvent model) {
if (model.getValue(ABTestEvent.REPORTED) == 1)
return;
pushABTestEvent(model);
}
});
}
public void pushABTestEvent(final ABTestEvent model) {
new Thread(new Runnable() {
public void run() {
try {
JSONArray payload = new JSONArray().put(jsonFromABTestEvent(model));
abTestReporter.post(payload);
model.setValue(ABTestEvent.REPORTED, 1);
abTestEventDao.saveExisting(model);
} catch (JSONException e) {
handleException(e);
} catch (IOException e) {
handleException(e);
}
};
}).start();
}
public void pushAllUnreportedABTestEvents() {
TodorooCursor<ABTestEvent> unreported = abTestEventDao.query(Query.select(ABTestEvent.PROPERTIES)
.where(ABTestEvent.REPORTED.eq(0)));
try {
if (unreported.getCount() > 0) {
JSONArray payload = jsonArrayFromABTestEvents(unreported);
abTestReporter.post(payload);
for (unreported.moveToFirst(); !unreported.isAfterLast(); unreported.moveToNext()) {
ABTestEvent model = new ABTestEvent(unreported);
model.setValue(ABTestEvent.REPORTED, 1);
abTestEventDao.saveExisting(model);
}
}
} catch (JSONException e) {
handleException(e);
} catch (IOException e) {
handleException(e);
} finally {
unreported.close();
}
}
private void handleException(Exception e) {
Log.e("analytics", "analytics-error", e);
// TODO: Schedule retry
}
private static JSONObject jsonFromABTestEvent(ABTestEvent model) throws JSONException {
JSONObject payload = new JSONObject();
payload.put(KEY_TEST, model.getValue(ABTestEvent.TEST_NAME));
payload.put(KEY_VARIANT, model.getValue(ABTestEvent.TEST_VARIANT));
payload.put(KEY_NEW_USER, model.getValue(ABTestEvent.NEW_USER) > 0 ? true : false);
payload.put(KEY_ACTIVE_USER, model.getValue(ABTestEvent.ACTIVATED_USER) > 0 ? true : false);
payload.put(KEY_DAYS, model.getValue(ABTestEvent.TIME_INTERVAL));
long date = model.getValue(ABTestEvent.DATE_RECORDED) / 1000L;
payload.put(KEY_DATE, date);
return payload;
}
private static JSONArray jsonArrayFromABTestEvents(TodorooCursor<ABTestEvent> events) throws JSONException {
JSONArray result = new JSONArray();
for (events.moveToFirst(); !events.isAfterLast(); events.moveToNext()) {
ABTestEvent model = new ABTestEvent(events);
result.put(jsonFromABTestEvent(model));
}
return result;
}
}

@ -0,0 +1,81 @@
package com.todoroo.astrid.service.abtesting;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.RestClient;
@SuppressWarnings("nls")
public class ABTestReporter {
/** NOTE: these values are development values and will not work on production */
private static final String URL = "http://analytics.astrid.com/api/1/retention";
private static final String API_KEY = "ryyubd";
private static final String API_SECRET = "q9ef3i";
@Autowired private RestClient restClient;
public ABTestReporter() {
DependencyInjectionService.getInstance().inject(this);
}
public JSONObject post(JSONArray payload) throws IOException {
try {
HttpEntity postData = createPostData(payload);
if (postData == null)
throw new IOException("Unsupported URL encoding");
String response = restClient.post(URL, postData);
JSONObject object = new JSONObject(response);
if (object.getString("status").equals("error")) {
throw new IOException("Error reporting ABTestEvent: " +
object.optString("message"));
}
return object;
} catch (JSONException e) {
throw new IOException(e.getMessage());
}
}
private HttpEntity createPostData(JSONArray payload) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("apikey", API_KEY));
params.add(new BasicNameValuePair("payload", payload.toString()));
StringBuilder sigBuilder = new StringBuilder();
for(NameValuePair entry : params) {
if(entry.getValue() == null)
continue;
String key = entry.getName();
String value = entry.getValue();
sigBuilder.append(key).append(value);
}
sigBuilder.append(API_SECRET);
String signature = DigestUtils.md5Hex(sigBuilder.toString());
params.add(new BasicNameValuePair("sig", signature));
try {
return new UrlEncodedFormEntity(params, HTTP.UTF_8);
} catch (UnsupportedEncodingException e) {
return null;
}
}
}
Loading…
Cancel
Save