/* * Copyright (C) 2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo FLOW. * * Akvo FLOW is free software: you can redistribute it and modify it under the terms of * the GNU Affero General Public License (AGPL) as published by the Free Software Foundation, * either version 3 of the License or any later version. * * Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License included below for more details. * * The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>. */ package org.akvo.flow.events; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.codehaus.jackson.map.ObjectMapper; import org.waterforpeople.mapping.app.web.rest.security.user.GaeUser; import com.gallatinsystems.survey.domain.SurveyGroup; import com.gallatinsystems.survey.domain.SurveyGroup.PrivacyLevel; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Text; public class EventUtils { private static Logger log = Logger.getLogger(EventUtils.class.getName()); private static final ObjectMapper objectMapper = new ObjectMapper(); public enum EventSourceType { USER, DEVICE, SENSOR, WEBFORM, API, UNKNOWN, SYSTEM }; public enum EntityType { SURVEY_GROUP, FORM, QUESTION_GROUP, QUESTION, DATA_POINT, FORM_INSTANCE, ANSWER, DEVICE_FILE }; // names of kinds in Google App Engine static public class Kind { public static final String SURVEY_GROUP = "SurveyGroup"; public static final String FORM = "Survey"; public static final String QUESTION_GROUP = "QuestionGroup"; public static final String QUESTION = "Question"; public static final String DATA_POINT = "SurveyedLocale"; public static final String FORM_INSTANCE = "SurveyInstance"; public static final String ANSWER = "QuestionAnswerStore"; public static final String DEVICE_FILE = "DeviceFiles"; } // How we name the actions static class Action { public static final String SURVEY_GROUP = "surveyGroup"; public static final String FORM = "form"; public static final String QUESTION_GROUP = "questionGroup"; public static final String QUESTION = "question"; public static final String DATA_POINT = "dataPoint"; public static final String FORM_INSTANCE = "formInstance"; public static final String ANSWER = "answer"; public static final String DEVICE_FILE = "deviceFile"; public static final String DELETED = "Deleted"; public static final String CREATED = "Created"; public static final String UPDATED = "Updated"; public static final String UNIFIED_LOG_NOTIFIED = "unifiedLogNotified"; } static class Key { public static final String ID = "id"; public static final String EMAIL = "email"; public static final String DISPLAY_TEXT = "displayText"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String TIMESTAMP = "timestamp"; public static final String SOURCE = "source"; public static final String TYPE = "type"; public static final String ORDER = "order"; public static final String LAT = "lat"; public static final String LON = "lon"; public static final String ORG_ID = "orgId"; public static final String SURVEY_ID = "surveyId"; public static final String QUESTION_TYPE = "questionType"; public static final String ANSWER_TYPE = "answerType"; public static final String PARENT_ID = "parentId"; public static final String FORM_ID = "formId"; public static final String QUESTION_GROUP_ID = "questionGroupId"; public static final String QUESTION_ID = "questionId"; public static final String FORM_INSTANCE_ID = "formInstanceId"; public static final String ANSWER_ID = "answerId"; public static final String DATA_POINT_ID = "dataPointId"; public static final String SUBMITTER_NAME = "submitterName"; public static final String COLLECTION_DATE = "collectionDate"; public static final String SURVEYAL_TIME = "surveyalTime"; public static final String PUBLIC = "public"; public static final String VALUE = "value"; public static final String IDENTIFIER = "identifier"; public static final String SURVEY_GROUP_TYPE = "surveyGroupType"; public static final String APP_ID = "orgId"; public static final String URL = "url"; public static final String ITERATION = "iteration"; } static class Prop { public static final String SURVEY_INSTANCE_ID = "surveyInstanceId"; public static final String TYPE = "type"; public static final String VALUE = "value"; public static final String VALUE_TEXT = "valueText"; public static final String SURVEY_ID = "surveyId"; public static final String SURVEYED_LOCALE_ID = "surveyedLocaleId"; public static final String COLLECTION_DATE = "collectionDate"; public static final String SURVEYAL_TIME = "surveyalTime"; public static final String IDENTIFIER = "identifier"; public static final String LATITUDE = "latitude"; public static final String LONGITUDE = "longitude"; public static final String DISPLAY_NAME = "displayName"; public static final String NAME = "name"; public static final String PARENT_ID = "parentId"; public static final String DESCRIPTION = "description"; public static final String PRIVACY_LEVEL = "privacyLevel"; public static final String DESC = "desc"; public static final String SURVEY_GROUP_ID = "surveyGroupId"; public static final String ORDER = "order"; public static final String TEXT = "text"; public static final String QUESTION_ID = "questionID"; public static final String QUESTION_IDENTIFIER = "questionId"; public static final String QUESTION_GROUP_ID = "questionGroupId"; public static final String PROJECT_TYPE = "projectType"; public static final String LAST_UPDATE_DATE_TIME = "lastUpdateDateTime"; public static final String CREATED_DATE_TIME = "createdDateTime"; public static final String ALIAS = "alias"; public static final String EVENT_NOTIFICATION = "eventNotification"; public static final String ENABLE_CHANGE_EVENTS = "enableChangeEvents"; public static final String ITERATION = "iteration"; } public static final String SURVEY_GROUP_TYPE_SURVEY = "SURVEY"; public static final String SURVEY_GROUP_TYPE_FOLDER = "FOLDER"; public static class EventTypes { public final EntityType type; public final String action; public EventTypes(EntityType type, String action) { this.type = type; this.action = action; } } public static EventTypes getEventAndActionType(String kindName) { switch (kindName) { case Kind.ANSWER: return new EventTypes(EntityType.ANSWER, Action.ANSWER); case Kind.FORM_INSTANCE: return new EventTypes(EntityType.FORM_INSTANCE, Action.FORM_INSTANCE); case Kind.DATA_POINT: return new EventTypes(EntityType.DATA_POINT, Action.DATA_POINT); case Kind.SURVEY_GROUP: return new EventTypes(EntityType.SURVEY_GROUP, Action.SURVEY_GROUP); case Kind.FORM: return new EventTypes(EntityType.FORM, Action.FORM); case Kind.QUESTION_GROUP: return new EventTypes(EntityType.QUESTION_GROUP, Action.QUESTION_GROUP); case Kind.QUESTION: return new EventTypes(EntityType.QUESTION, Action.QUESTION); case Kind.DEVICE_FILE: return new EventTypes(EntityType.DEVICE_FILE, Action.DEVICE_FILE); } return null; } private static Map<String, Object> addProperty(String key, Object val, Map<String, Object> data) { if (val != null) { data.put(key, val); } return data; } private static Map<String, Object> addProperty(String key, Object val, Object defaultVal, Map<String, Object> data) { if (val != null) { data.put(key, val); } else { data.put(key, defaultVal); } return data; } public static Map<String, Object> populateEntityProperties(EntityType type, Entity e, Map<String, Object> data) { switch (type) { case ANSWER: addProperty(Key.FORM_INSTANCE_ID, e.getProperty(Prop.SURVEY_INSTANCE_ID), data); addProperty(Key.ANSWER_TYPE, e.getProperty(Prop.TYPE), data); addProperty(Key.QUESTION_ID, Long.valueOf((String) e.getProperty(Prop.QUESTION_ID)), data); Object value = e.getProperty(Prop.VALUE); if (value == null) { Object text = e.getProperty(Prop.VALUE_TEXT); if (text != null && text instanceof Text) { value = ((Text) text).getValue(); } } addProperty(Key.VALUE, value, data); addProperty(Key.FORM_ID, e.getProperty(Prop.SURVEY_ID), data); addProperty(Key.ITERATION, e.getProperty(Prop.ITERATION), data); break; case FORM_INSTANCE: addProperty(Key.FORM_ID, e.getProperty(Prop.SURVEY_ID), data); addProperty(Key.DATA_POINT_ID, e.getProperty(Prop.SURVEYED_LOCALE_ID), data); addProperty(Key.COLLECTION_DATE, e.getProperty(Prop.COLLECTION_DATE), data); addProperty(Key.SURVEYAL_TIME, e.getProperty(Prop.SURVEYAL_TIME), data); break; case DATA_POINT: addProperty(Key.IDENTIFIER, e.getProperty(Prop.IDENTIFIER), data); addProperty(Key.LAT, e.getProperty(Prop.LATITUDE), data); addProperty(Key.LON, e.getProperty(Prop.LONGITUDE), data); addProperty(Key.NAME, e.getProperty(Prop.DISPLAY_NAME), data); addProperty(Key.SURVEY_ID, e.getProperty(Prop.SURVEY_GROUP_ID), data); break; case SURVEY_GROUP: addProperty(Key.NAME, e.getProperty(Prop.NAME), "<name missing>", data); addProperty(Key.PARENT_ID, e.getProperty(Prop.PARENT_ID), data); if (e.getProperty(Prop.PROJECT_TYPE).toString() .equals(SurveyGroup.ProjectType.PROJECT.toString())) { data.put(Key.SURVEY_GROUP_TYPE, SURVEY_GROUP_TYPE_SURVEY); } else if (e.getProperty(Prop.PROJECT_TYPE).toString() .equals(SurveyGroup.ProjectType.PROJECT_FOLDER.toString())) { data.put(Key.SURVEY_GROUP_TYPE, SURVEY_GROUP_TYPE_FOLDER); } addProperty(Key.DESCRIPTION, e.getProperty(Prop.DESCRIPTION), data); addProperty(Key.PUBLIC, String.valueOf(e.getProperty(Prop.PRIVACY_LEVEL)) .equals(PrivacyLevel.PUBLIC.toString()), data); break; case FORM: addProperty(Key.NAME, e.getProperty(Prop.NAME), data); addProperty(Key.DESCRIPTION, e.getProperty(Prop.DESC), data); addProperty(Key.SURVEY_ID, e.getProperty(Prop.SURVEY_GROUP_ID), data); break; case QUESTION_GROUP: addProperty(Key.NAME, e.getProperty(Prop.NAME), "<name missing>", data); addProperty(Key.ORDER, e.getProperty(Prop.ORDER), data); addProperty(Key.FORM_ID, e.getProperty(Prop.SURVEY_ID), data); break; case QUESTION: addProperty(Key.DISPLAY_TEXT, e.getProperty(Prop.TEXT), data); addProperty(Key.IDENTIFIER, e.getProperty(Prop.QUESTION_IDENTIFIER), data); addProperty(Key.QUESTION_GROUP_ID, e.getProperty(Prop.QUESTION_GROUP_ID), data); addProperty(Key.FORM_ID, e.getProperty(Prop.SURVEY_ID), data); addProperty(Key.QUESTION_TYPE, e.getProperty(Prop.TYPE), data); break; case DEVICE_FILE: // FIXME move those keys to the proper place addProperty("uri", e.getProperty("URI"), data); addProperty("checksum", e.getProperty("checksum"), data); addProperty("phoneNumber", e.getProperty("phoneNumber"), data); addProperty("imei", e.getProperty("imei"), data); break; } return data; } public static Map<String, Object> newEvent(String orgId, String eventType, Map<String, Object> entity, Map<String, Object> context) throws AssertionError { assert orgId != null : "orgId is required"; assert eventType != null : "eventType is required"; assert entity != null : "entity is required"; assert context != null : "context is required"; Map<String, Object> result = new HashMap<String, Object>(); result.put("orgId", orgId); result.put("eventType", eventType); result.put("entity", entity); result.put("context", context); return result; } public static Map<String, Object> newSource(Object principal) { Map<String, Object> source = new HashMap<String, Object>(); if (principal instanceof String) { // Tasks related events get an "anonymousUser" as principal source.put(Key.TYPE, EventSourceType.SYSTEM); } else if (principal instanceof GaeUser) { GaeUser usr = (GaeUser) principal; source.put(Key.TYPE, EventSourceType.USER); source.put(Key.EMAIL, usr.getEmail()); source.put(Key.ID, usr.getUserId()); } else { log.log(Level.WARNING, "Unable to identify source from authentication principal: " + principal.toString()); } return source; } public static Map<String, Object> newContext(Date timestamp, Map<String, Object> source) { Map<String, Object> context = new HashMap<String, Object>(); context.put(Key.TIMESTAMP, timestamp); context.put(Key.SOURCE, source); return context; } public static Map<String, Object> newEntity(EntityType type, Long id) { Map<String, Object> entity = new HashMap<String, Object>(); entity.put(Key.TYPE, type); entity.put(Key.ID, id); return entity; } public static void sendEvents(String urlString, List<Map<String, Object>> events) throws IOException { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url .openConnection(); connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); OutputStreamWriter writer = new OutputStreamWriter( connection.getOutputStream()); objectMapper.writeValue(writer, events); System.out.println(" " + connection.getResponseCode()); writer.close(); connection.disconnect(); } }