/* * 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.gae.remoteapi; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.EntityNotFoundException; import com.google.appengine.api.datastore.FetchOptions; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.Filter; import com.google.appengine.api.datastore.Query.FilterOperator; import static org.akvo.gae.remoteapi.DataUtils.*; /** * This is a data modification script to rectify anomalies with survey instances in monitored * groups. It resets the surveyedLocaleId of monitoring form survey instances to point to the same * locale created by the registration form with which the monitoring instance shares a * survyedLocaleIdentifier */ public class CorrectSurveyedLocale implements Process { @Override public void execute(DatastoreService ds, String[] args) throws Exception { if (args.length == 0) { System.err .println("Usage: " + RemoteAPI.class.getName() + "CorrectSurveyedLocale <appid> <username> <password> <appid> <surveyId>"); System.exit(1); } // retrieve registration form id for surveygroup matching monitored group id passed in Long surveyId = Long.parseLong(args[0]); Entity monitoringSurvey = retrieveMonitoringSurvey(ds, surveyId); List<Entity> surveyForms = retrieveSurveyForms(ds, surveyId); // map locale identifiers and surveyedlocale ids for all survey instances created by // registration form Long registrationFormId = (Long) monitoringSurvey.getProperty("newLocaleSurveyId"); if (registrationFormId == null) { System.err.println("Unable to identify registration form for survey: " + surveyId); System.exit(1); } // retrieve all monitoring surveys for survey group List<Entity> dataPoints = retrieveSurveyInstances(ds, registrationFormId); Map<String, Long> surveyedLocaleDeviceIdentifierMap = mapDeviceIdentifierSurveyedLocaleId(dataPoints); // for each monitoring survey for (Entity monitoringForm : surveyForms) { Long formId = new Long(monitoringForm.getKey().getId()); if (registrationFormId.equals(formId)) { continue; // skip registration form processing } List<Entity> monitoringFormInstances = retrieveSurveyInstances(ds, formId); List<Entity> correctedMonitoringFormInstances = correctSurveyedLocale( monitoringFormInstances, surveyedLocaleDeviceIdentifierMap, ds); batchSaveEntities(ds, correctedMonitoringFormInstances); } } /** * Retrieve the monitoring survey entity * * @param ds * @param surveyId * @return * @throws EntityNotFoundException */ private Entity retrieveMonitoringSurvey(DatastoreService ds, Long surveyId) throws EntityNotFoundException { return ds.get(KeyFactory.createKey(SURVEY_KIND, surveyId)); } /** * Retrieve all forms belonging to the monitoring survey * * @param ds * @param surveyId * @return */ private List<Entity> retrieveSurveyForms(DatastoreService ds, Long surveyId) { Filter surveyFilter = new Query.FilterPredicate("surveyGroupId", FilterOperator.EQUAL, surveyId); Query q = new Query(FORM_KIND).setFilter(surveyFilter); return ds.prepare(q).asList(FetchOptions.Builder.withDefaults()); } /** * Retrieve all form instances (responses) linked to a form Id. * * @param ds * @param formId * @return */ private List<Entity> retrieveSurveyInstances(DatastoreService ds, Long formId) { Filter formFilter = new Query.FilterPredicate("surveyId", FilterOperator.EQUAL, formId); Query q = new Query(FORM_INSTANCE_KIND).setFilter(formFilter); List<Entity> formInstances = new ArrayList<Entity>(); for (Entity formInstance : ds.prepare(q).asIterable()) { formInstances.add(formInstance); } return formInstances; } /** * Create a mapping between the identifier and the data point id (surveyedLocaleId) of * registration form responses * * @param dataPoints * @return */ private Map<String, Long> mapDeviceIdentifierSurveyedLocaleId(List<Entity> dataPoints) { Map<String, Long> dataPointIdentifiersMap = new HashMap<String, Long>(); for (Entity dataPoint : dataPoints) { String identifier = (String) dataPoint.getProperty(DATA_POINT_STRING_ID); Long dataPointId = (Long) dataPoint.getProperty(DATA_POINT_NUMERICAL_ID); if (identifier != null && dataPointId != null) { dataPointIdentifiersMap.put(identifier, dataPointId); } else { System.out.println(dataPoint.getKey() + " missing identifier (" + identifier + ") or surveyedLocaleId (" + dataPointId + ")"); } } return dataPointIdentifiersMap; } /** * Reset the surveyedLocaleId for instances with matching identifiers and non-matching surveyed * locale ids * * @param monitoringFormInstances * @param surveyedLocaleDeviceIdentifierMap * @param ds TODO * @return */ private List<Entity> correctSurveyedLocale(List<Entity> monitoringFormInstances, Map<String, Long> surveyedLocaleDeviceIdentifierMap, DatastoreService ds) { List<Entity> correctedFormInstances = new ArrayList<Entity>(); List<Long> oldSurveyedLocaleIds = new ArrayList<Long>(); for (Entity formInstance : monitoringFormInstances) { String identifier = (String) formInstance.getProperty(DATA_POINT_STRING_ID); Long surveyedLocaleId = (Long) formInstance.getProperty(DATA_POINT_NUMERICAL_ID); if (identifier == null || !surveyedLocaleDeviceIdentifierMap.containsKey(identifier)) { System.out.println(formInstance.getKey() + " missing identifier"); continue; } Long dataPointId = surveyedLocaleDeviceIdentifierMap.get(identifier); if (!dataPointId.equals(surveyedLocaleId)) { formInstance.setProperty(DATA_POINT_NUMERICAL_ID, dataPointId); correctedFormInstances.add(formInstance); System.out.println("Changing " + formInstance.getKey() + ";" + identifier + ";" + surveyedLocaleId + "=>" + dataPointId); oldSurveyedLocaleIds.add(surveyedLocaleId); } } batchDelete(ds, oldSurveyedLocaleIds, DATA_POINT_KIND); return correctedFormInstances; } }