package com.socrata.datasync.publishers;
import com.socrata.datasync.SocrataConnectionInfo;
import com.socrata.datasync.config.userpreferences.UserPreferences;
import com.socrata.datasync.imports2.*;
import com.socrata.datasync.job.GISJob;
import com.socrata.datasync.job.JobStatus;
import com.socrata.datasync.HttpUtility;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.util.EntityUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GISPublisher {
private static final Logger logging = Logger.getLogger(GISJob.class.getName());
private static String ticket = "";
public static JobStatus replaceGeo(File file,
SocrataConnectionInfo connectionInfo,
String datasetID,
Map<String, String> layerMap,
UserPreferences userPrefs) {
try {
URI scan_url = makeUri(connectionInfo.getUrl(), "scan", "");
Blueprint blueprint = postRawFile(scan_url, file, userPrefs);
return replaceGeoFile(blueprint, file, userPrefs, connectionInfo, datasetID, layerMap);
} catch (IOException e) {
String message = e.getMessage();
JobStatus status = JobStatus.PUBLISH_ERROR;
status.setMessage(message);
return status;
}
}
private static JobStatus replaceGeoFile(Blueprint blueprint,
File file,
UserPreferences userPrefs,
SocrataConnectionInfo connectionInfo,
String datasetID,
Map<String, String> layerMap) {
try {
if (blueprint.getError() != null) {
String message = blueprint.getError().getMessage();
System.out.println(message);
JobStatus status = JobStatus.PUBLISH_ERROR;
status.setMessage(message);
return status;
}
ObjectMapper mapper = new ObjectMapper();
applyLayerMapToBlueprintSummary(blueprint.getSummary(), layerMap);
String blueprintSummary = mapper.writeValueAsString(blueprint.getSummary());
String query = "&fileId=" + URLEncoder.encode(blueprint.getFileId(),"UTF-8");
query = query + "&name=" + URLEncoder.encode(file.getName(),"UTF-8");
query = query + "&blueprint=" + URLEncoder.encode(blueprintSummary,"UTF-8");
query = query + "&viewUid=" + URLEncoder.encode(datasetID,"UTF-8");
URI uri = makeUri(connectionInfo.getUrl(),"replace",query);
logging.log(Level.FINE, uri.toString());
return postReplaceGeoFile(uri, connectionInfo, userPrefs);
} catch (IOException e) {
String message = e.getMessage();
JobStatus status = JobStatus.PUBLISH_ERROR;
status.setMessage(message);
return status;
}
}
private static void applyLayerMapToBlueprintSummary(Summary summary, Map<String, String> layerMap) {
for (Layer layer : summary.getLayers()) {
if (layerMap.containsKey(layer.getName())) {
layer.setReplacingUid(layerMap.get(layer.getName()));
}
}
}
private static JobStatus postReplaceGeoFile(URI uri,
SocrataConnectionInfo connectionInfo,
UserPreferences userPrefs) {
HttpUtility httpUtility = new HttpUtility(userPrefs, true, 3, 2);
try {
HttpEntity empty = MultipartEntityBuilder.create().build();
HttpResponse response = httpUtility.post(uri,empty);
HttpEntity resEntity = response.getEntity();
String result = EntityUtils.toString(resEntity);
JSONParser parser = new JSONParser();
JSONObject resJson = (JSONObject) parser.parse(result);
logging.log(Level.FINE, result);
boolean error = (boolean) resJson.get("error");
if (error) {
JobStatus s = JobStatus.PUBLISH_ERROR;
String error_message = (String) resJson.get("message");
s.setMessage(error_message);
return s;
}
ticket = resJson.get("ticket").toString();
try {
return pollForStatus(ticket, userPrefs, connectionInfo, false);
} catch (InterruptedException e) {
// This should be very rare, but we should throw if it happens.
throw new RuntimeException(e);
}
} catch (IOException | ParseException e) {
String message = e.getMessage();
JobStatus status = JobStatus.PUBLISH_ERROR;
status.setMessage(message);
return status;
}
}
private static JobStatus pollForStatus(String ticket, UserPreferences userPrefs, SocrataConnectionInfo connectionInfo, boolean complete) throws InterruptedException {
if (!complete) {
URI status_url = makeUri(connectionInfo.getUrl(), "status", ticket);
String[] status = getStatus(status_url, userPrefs);
System.out.println("Polling the job status: " + status[1]);
Thread.sleep(1000);
if (status[0].equals("Complete")) {
return JobStatus.SUCCESS;
}
if (status[0].equals("Error")) {
JobStatus s = JobStatus.PUBLISH_ERROR;
s.setMessage(status[1]);
return s;
}
pollForStatus(ticket, userPrefs, connectionInfo, false);
}
return JobStatus.SUCCESS;
}
private static String[] getStatus(URI uri,
UserPreferences userPrefs) {
HttpUtility httpUtility = new HttpUtility(userPrefs, true, 3, 2);
String[] status = new String[2];
try {
HttpResponse response = httpUtility.get(uri,"application/json");
HttpEntity resEntity = response.getEntity();
String result = EntityUtils.toString(resEntity);
JSONParser parser = new JSONParser();
JSONObject resJson = (JSONObject) parser.parse(result);
try {
boolean error = (boolean) resJson.get("error");
JSONObject details = (JSONObject) resJson.get("details");
if (error){
status[0] = "Error";
status[1] = resJson.get("message").toString() + " with code: " + resJson.get("code").toString();
return status;
} else {
try {
status[0] = "Progress";
status[1] = details.get("status").toString() + ": " + details.get("progress").toString() + " features completed";
} catch (NullPointerException e) {
status[0] = "Progress";
status[1] = details.get("stage").toString();
return status;
}
}
} catch (NullPointerException e) {
// For once the ticket is complete
status[0] = "Complete";
status[1] = "Complete";
return status;
}
return status;
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
status[0] = "Complete";
status[1] = "Complete";
return status;
}
}
private static Blueprint postRawFile(URI uri,
File file,
UserPreferences userPrefs) throws IOException {
HttpUtility httpUtility = new HttpUtility(userPrefs, true, 3, 2);
System.out.println("Posting file...");
HttpEntity httpEntity = MultipartEntityBuilder.create()
.addBinaryBody(file.getName(), file, ContentType.APPLICATION_OCTET_STREAM,file.getName())
.build();
HttpResponse response = httpUtility.post(uri, httpEntity);
HttpEntity resEntity = response.getEntity();
String result = EntityUtils.toString(resEntity);
logging.log(Level.FINE, result);
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(result, Blueprint.class);
}
private static URI makeUri(String domain, String method, String query) {
switch(method) {
case "scan":
return URI.create(domain + "/api/imports2?method=scanShape");
case "replace":
return URI.create(domain + "/api/imports2?method=replaceShapefile" + query);
case "status":
return URI.create(domain + "/api/imports2?ticket=" + query);
default:
return URI.create("");
}
}
}