/* * 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.HashSet; import java.util.List; import java.util.Set; 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.PreparedQuery; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.CompositeFilterOperator; import com.google.appengine.api.datastore.Query.Filter; import com.google.appengine.api.datastore.Query.FilterOperator; /** * Check for survey data that is publicly visible and correct its privacyLevel */ public class HidePrivateSurveyData implements Process { private static final int MAX_UNDERLYING_QUERIES = 30; @Override public void execute(DatastoreService ds, String[] args) throws Exception { if (args.length == 0) { System.err.println("Usage: " + RemoteAPI.class.getName() + "HidePrivateSurveyData <appid> <username> <password> <appid> [--dry-run]"); System.exit(1); } String appId = args[0]; // retrieve all private survey group ids Filter projectTypeFilter = new Query.FilterPredicate("projectType", FilterOperator.EQUAL, "PROJECT"); Filter privateLevelFilter = new Query.FilterPredicate("privacyLevel", FilterOperator.EQUAL, "PRIVATE"); Filter privateSurveyGroupFilter = CompositeFilterOperator.and(projectTypeFilter, privateLevelFilter); Query privateSurveyGroupQuery = new Query("SurveyGroup") .setFilter(privateSurveyGroupFilter).setKeysOnly(); Set<Long> privateSurveyGroupIds = new HashSet<Long>(); for (Entity surveyGroup : ds.prepare(privateSurveyGroupQuery).asIterable()) { privateSurveyGroupIds.add(surveyGroup.getKey().getId()); } // retrieve survey ids for private groups Query surveyQuery = new Query("Survey").setFilter(new Query.FilterPredicate( "surveyGroupId", FilterOperator.IN, privateSurveyGroupIds)); Set<Long> privateSurveyIds = new HashSet<Long>(); for (Entity survey : ds.prepare(surveyQuery).asIterable()) { String pointType = (String) survey.getProperty("pointType"); // forms created after introduction of folders don't have a pointType so adopt privacy // level from above selected 'PRIVATE' survey groups (folder) if (pointType == null || pointType.equals("Household")) { privateSurveyIds.add(survey.getKey().getId()); } } // retrieve survey instances for private surveys Query surveyResponses = new Query("SurveyInstance").setFilter( new Query.FilterPredicate("surveyId", FilterOperator.IN, privateSurveyIds)) .setKeysOnly(); List<Long> surveyInstanceIds = new ArrayList<Long>(); for (Entity surveyInstance : ds.prepare(surveyResponses).asIterable( FetchOptions.Builder.withChunkSize(1000))) { surveyInstanceIds.add(surveyInstance.getKey().getId()); } // identify publicly visible surveyed locales for private surveys List<Entity> publiclyVisibleLocalesList = new ArrayList<Entity>(); int startIdx = 0; // process locales in batches of size = MAX_UNDERLYING_QUERY while (startIdx < surveyInstanceIds.size()) { final int endIdx = startIdx + MAX_UNDERLYING_QUERIES > surveyInstanceIds.size() ? surveyInstanceIds .size() : startIdx + MAX_UNDERLYING_QUERIES; final List<Long> instancesSubList = new ArrayList<Long>(surveyInstanceIds.subList( startIdx, endIdx)); startIdx = endIdx; Filter surveyInstanceIdFilter = new Query.FilterPredicate("lastSurveyalInstanceId", Query.FilterOperator.IN, instancesSubList); Query localesQuery = new Query("SurveyedLocale").setFilter(surveyInstanceIdFilter); PreparedQuery pqLocales = ds.prepare(localesQuery); for (Entity entity : pqLocales.asIterable()) { String localeType = (String) entity.getProperty("localeType"); if (!localeType.trim().equals("Household") && !localeType.trim().equals("PRIVATE")) { publiclyVisibleLocalesList.add(entity); System.out.println(appId + "," + entity.getKey().getId() + "," + localeType); } } } // dry run exit boolean dryrun = args.length == 2 && args[1].equals("--dry-run"); if (dryrun) { System.out.println("Found " + publiclyVisibleLocalesList.size() + "publicly visible private data points"); System.out.println("Exiting without updating any data points"); System.exit(0); } // update locales if (publiclyVisibleLocalesList.size() > 0) { for (Entity locale : publiclyVisibleLocalesList) { locale.setProperty("localeType", "PRIVATE"); } System.out.println("Setting " + publiclyVisibleLocalesList.size() + " surveyedLocales to PRIVATE"); ds.put(publiclyVisibleLocalesList); } } }