package org.akvo.gae.remoteapi;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.akvo.flow.events.EventUtils;
import org.akvo.flow.events.EventUtils.EventTypes;
import org.akvo.flow.events.EventUtils.Kind;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Query;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
public class ExportDataToEventLog implements Process {
private static final int BATCH_SIZE = 1000;
private static final String EVENT_QUEUE = "EventQueue";
private static final String[] kinds = { Kind.SURVEY_GROUP, Kind.FORM,
Kind.QUESTION_GROUP, Kind.QUESTION, Kind.DATA_POINT,
Kind.FORM_INSTANCE, Kind.DEVICE_FILE, Kind.ANSWER };
// The timestamp used for imported data.
private static final Date IMPORT_DATE = new Date(0);
@Override
public void execute(DatastoreService ds, String[] args) throws Exception {
if (args.length != 2) {
System.err
.println("Usage: \n"
+ "This data script depends on code in GAE for generating json event data.\n"
+ "* Compile the appengine code by running 'ant compile' in the GAE directory. \n"
+ "* Add GAE/war/WEB-INF/classes and GAE/war/WEB-INF/lib/* to the classpath. \n"
+ "* Example cli call: \n"
+ " java -classpath \"bin:lib/*:../../GAE/war/WEB-INF/classes:../../GAE/war/WEB-INF/lib/*\"\n"
+ " org.akvo.gae.remoteapi.RemoteAPI\n"
+ " ExportDataToEventLog\n"
+ " <app-id>\n"
+ " <remote-api-email>\n"
+ " <remote-api-password>\n"
+ " <app-id>\n"
+ " <event-notification-endpoint>");
return;
}
final String orgId = args[0];
final String servicesEndpoint = args[1];
final Date firstEvent = findFirstEventDate(ds);
for (final String kind : kinds) {
System.out.println("Exporting " + kind);
int batch = 0;
Query kindQuery = new Query(kind);
final EventTypes eventTypes = EventUtils
.getEventAndActionType(kind);
Iterable<Entity> entities = ds.prepare(kindQuery).asIterable(
FetchOptions.Builder.withChunkSize(BATCH_SIZE));
Iterable<Entity> entitiesNotInEventQueue = Iterables.filter(
entities, new Predicate<Entity>() {
@Override
public boolean apply(Entity entity) {
Date date = (Date) entity
.getProperty("createdDateTime");
if (date == null) {
// If the createdDateTime is not present, assume
// it's old enough to be part of the data import
return true;
} else {
return date.before(firstEvent);
}
}
});
Iterable<Map<String, Object>> events = Iterables.transform(
entitiesNotInEventQueue,
new Function<Entity, Map<String, Object>>() {
@Override
public Map<String, Object> apply(Entity entity) {
return createEvent(entity, eventTypes, orgId);
}
});
Iterable<List<Map<String, Object>>> eventBatches = Iterables
.partition(events, BATCH_SIZE);
for (List<Map<String, Object>> eventBatch : eventBatches) {
System.out.println(" Batch #" + batch);
batch++;
EventUtils.sendEvents(servicesEndpoint + orgId, eventBatch);
}
}
}
private static Date findFirstEventDate(DatastoreService ds) {
List<Entity> eventsInQueue = ds.prepare(new Query(EVENT_QUEUE)).asList(
FetchOptions.Builder.withDefaults().limit(1));
if (eventsInQueue.isEmpty()) {
return new Date(Long.MAX_VALUE);
} else {
return (Date) eventsInQueue.get(0).getProperty("createdDateTime");
}
}
private static Map<String, Object> createEvent(Entity entity,
EventTypes eventTypes, String orgId) {
Map<String, Object> source = EventUtils.newSource("import");
Map<String, Object> context = EventUtils
.newContext(IMPORT_DATE, source);
context.put("import", true);
Map<String, Object> entityMap = EventUtils.newEntity(eventTypes.type,
entity.getKey().getId());
EventUtils.populateEntityProperties(eventTypes.type, entity, entityMap);
Map<String, Object> event = EventUtils.newEvent(orgId,
eventTypes.action + "Created", entityMap, context);
return event;
}
}