package org.waterforpeople.mapping.app.web.rest; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.waterforpeople.mapping.app.web.rest.dto.NamedMapPayload; import com.gallatinsystems.common.util.PropertyUtil; import com.google.appengine.api.utils.SystemProperty; @Controller @RequestMapping("/cartodb") public class CartodbRestService { private static final String CDB_API_KEY = PropertyUtil.getProperty("cartodbApiKey"); private static final String CDB_ACCOUNT_NAME = SystemProperty.applicationId.get(); private static final String CDB_HOST = PropertyUtil.getProperty("cartodbHost"); private static final ObjectMapper objectMapper = new ObjectMapper(); @RequestMapping(method = RequestMethod.GET, value = "answers") @ResponseBody public Map<String, Object> getAnswers(@RequestParam("dataPointId") Long dataPointId, @RequestParam("surveyId") Long surveyId) { Map<String, Object> response = new HashMap<>(); response.put("answers", null); response.put("formId", null); try { String formIdQuery = String.format("SELECT id FROM form WHERE survey_id=%d", surveyId); List<Map<String, Object>> formIdResponse = queryCartodb(formIdQuery); if (!formIdResponse.isEmpty()) { Integer formId = (Integer) formIdResponse.get(0).get("id"); response.put("formId", formId); String rawDataQuery = String.format( "SELECT * FROM raw_data_%s WHERE data_point_id=%d", formId, dataPointId); List<Map<String, Object>> rawDataResponse = queryCartodb(rawDataQuery); if (!rawDataResponse.isEmpty()) { response.put("answers", rawDataResponse.get(0)); } } return response; } catch (IOException e) { return response; } } @RequestMapping(method = RequestMethod.GET, value = "data_point") @ResponseBody public Map<String, Object> getDataPointTableRow(@RequestParam("id") Long dataPointId) { Map<String, Object> response = new HashMap<>(); response.put("row", null); try { String rawDataQuery = String.format( "SELECT * FROM data_point WHERE id=%d", dataPointId); List<Map<String, Object>> rawDataResponse = queryCartodb(rawDataQuery); if (!rawDataResponse.isEmpty()) { response.put("row", rawDataResponse.get(0)); } return response; } catch (IOException e) { return response; } } @RequestMapping(method = RequestMethod.GET, value = "raw_data") @ResponseBody public Map<String, Object> getPointData(@RequestParam("dataPointId") Long dataPointId, @RequestParam("formId") Long formId) { Map<String, Object> response = new HashMap<>(); response.put("answers", null); response.put("formId", formId); try { String rawDataQuery = String.format( "SELECT * FROM raw_data_%s WHERE data_point_id=%d", formId, dataPointId); List<Map<String, Object>> rawDataResponse = queryCartodb(rawDataQuery); if (!rawDataResponse.isEmpty()) { response.put("answers", rawDataResponse.get(0)); } return response; } catch (IOException e) { return response; } } @RequestMapping(method = RequestMethod.GET, value = "questions") @ResponseBody public Map<String, Object> getQuestions( @RequestParam(value = "form_id", required = true) Long formId) { Map<String, Object> response = new HashMap<>(); response.put("questions", null); try { response.put( "questions", queryCartodb(String.format("SELECT * FROM question WHERE form_id = %d", formId))); return response; } catch (IOException e) { return response; } } @RequestMapping(method = RequestMethod.GET, value = "columns") @ResponseBody public Map<String, Object> getColumns( @RequestParam(value = "form_id", required = true) Long formId) { Map<String, Object> response = new HashMap<>(); response.put("column_names", null); try { response.put( "column_names", queryCartodb(String .format("SELECT column_name from information_schema.columns where table_name='raw_data_%d'", formId))); return response; } catch (IOException e) { return response; } } @RequestMapping(method = RequestMethod.GET, value = "surveys") @ResponseBody public Map<String, Object> listSurveys() { Map<String, Object> response = new HashMap<>(); response.put("surveys", null); try { response.put("surveys", queryCartodb("SELECT * FROM survey")); return response; } catch (IOException e) { return response; } } @RequestMapping(method = RequestMethod.GET, value = "forms") @ResponseBody public Map<String, Object> getForms(@RequestParam("surveyId") Long surveyId) { Map<String, Object> response = new HashMap<>(); response.put("forms", null); try { response.put("forms", queryCartodb(String.format("SELECT * FROM form WHERE survey_id=%d", surveyId))); return response; } catch (IOException e) { return response; } } @RequestMapping(method = RequestMethod.GET, value = "distinct") @ResponseBody public Map<String, Object> getDistinctValues( @RequestParam("question_name") String questionName, @RequestParam("form_id") Long formId) { Map<String, Object> response = new HashMap<>(); response.put("distinct_values", null); try { response.put("distinct_values", queryCartodb(String.format("SELECT DISTINCT %s FROM raw_data_%d", questionName, formId))); return response; } catch (IOException e) { return response; } } @SuppressWarnings("unchecked") @RequestMapping(method = RequestMethod.GET, value = "named_maps") @ResponseBody public Map<String, Object> getNamedMaps() throws IOException { HttpURLConnection connection = getConnection("GET", mapsApiURL()); return objectMapper.readValue(connection.getInputStream(), Map.class); } @RequestMapping(method = RequestMethod.POST, value = "named_maps") @ResponseBody public Map<String, Object> createNamedMaps( @RequestBody NamedMapPayload payload) throws IOException { HttpURLConnection connection = getConnection("POST", mapsApiURL()); OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); objectMapper.writeValue(writer, buildNamedMap(payload)); writer.close(); InputStream result = connection.getInputStream(); @SuppressWarnings("unchecked") Map<String, Object> resultMap = objectMapper.readValue(result, Map.class); return resultMap; } @SuppressWarnings("unchecked") private static List<Map<String, Object>> queryCartodb(String query) throws IOException { HttpURLConnection connection = getConnection("GET", sqlApiURL(query)); JsonNode jsonNode = objectMapper.readTree(connection.getInputStream()); return objectMapper.convertValue(jsonNode.get("rows"), List.class); } private static HttpURLConnection getConnection(String method, URL url) throws IOException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestMethod(method); connection.setRequestProperty("Content-Type", "application/json"); return connection; } private static final URL mapsApiURL() throws MalformedURLException { return new URL(String.format("https://%s.%s/api/v1/map/named?api_key=%s", CDB_ACCOUNT_NAME, CDB_HOST, CDB_API_KEY)); } private static final URL sqlApiURL(String query) throws MalformedURLException, UnsupportedEncodingException { String urlString = String.format("https://%s.%s/api/v2/sql?q=%s&api_key=%s", CDB_ACCOUNT_NAME, CDB_HOST, URLEncoder.encode(query, "UTF-8"), CDB_API_KEY); return new URL(urlString); } private static final Map<String, Object> buildNamedMap(NamedMapPayload namedMapPayload) { Map<String, Object> result = new HashMap<>(); result.put("name", namedMapPayload.getName()); result.put("version", "0.0.1"); Map<String, String> authMap = new HashMap<>(); authMap.put("method", "open"); result.put("auth", authMap); Map<String, Object> layerGroupMap = new HashMap<>(); Map<String, Object> optionsMap = new HashMap<>(); optionsMap.put("cartocss_version", "2.1.1"); optionsMap.put("cartocss", namedMapPayload.getCartocss()); optionsMap.put("sql", namedMapPayload.getSql()); optionsMap.put("interactivity", namedMapPayload.getInteractivity()); Map<String, Object> layerMap = new HashMap<>(); layerMap.put("type", "cartodb"); layerMap.put("options", optionsMap); List<Object> layersList = new ArrayList<>(); layersList.add(layerMap); layerGroupMap.put("layers", layersList); result.put("layergroup", layerGroupMap); return result; } }