/*******************************************************************************
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019)
*
* contact.vitam@culture.gouv.fr
*
* This software is a computer program whose purpose is to implement a digital archiving back-office system managing
* high volumetry securely and efficiently.
*
* This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free
* software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as
* circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
* users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the
* successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or
* developing or reproducing the software by the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling the security of their systems and/or data
* to be ensured and, more generally, to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you
* accept its terms.
*******************************************************************************/
package fr.gouv.vitam.ihmdemo.core;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import fr.gouv.vitam.common.LocalDateUtil;
import fr.gouv.vitam.common.ParametersChecker;
import fr.gouv.vitam.common.exception.VitamException;
import fr.gouv.vitam.common.json.JsonHandler;
/**
* This class is used in order to make transformations on Json objects received from Vitam
*/
public final class JsonTransformer {
private static final String MISSING_ID_ERROR_MSG = "Encountered Missing ID field in the given parents list.";
private static final String MISSING_UP_ERROR_MSG = "Encountered Missing _up field in the given parents list.";
private static final String INVALID_UP_FIELD_ERROR_MSG = "Encountered invalid _up field in the given parents list.";
private static final String MISSING_UNIT_ID_ERROR_MSG = "The unit details is missing.";
private static final String EVENT_DATE_TIME_FIELD = "evDateTime";
private static final String EVENTS_FIELD = "events";
private static final String START_EVENT_DATETIME_HEADER = "startEventDateTime";
private static final String END_EVENT_DATETIME_HEADER = "endEventDateTime";
private static final String EXECUTION_TIME_HEADER = "executionTime (ms)";
private static final char SEMI_COLON_SEPARATOR = ';';
private static final char RECORD_SEPARATOR = '\n';
private static final String UNEXPECTED_EXCEPTION_DURING_CSV_FILE_GENERATION =
"An unexpected error occurred during CSV file generation process";
private JsonTransformer() {
// empty constructor
}
/**
* This method transforms ResultObjects so thr IHM could display results
*
* @param searchResult the Json to be transformed
* @return the transformed JsonNode
*/
public static JsonNode transformResultObjects(JsonNode searchResult) {
ParametersChecker.checkParameter("Result cannot be empty", searchResult);
final ObjectNode resultNode = JsonHandler.createObjectNode();
long nbObjects = 0;
final JsonNode result = searchResult.get("$results").get(0);
final JsonNode qualifiers = result.get("#qualifiers");
final List<JsonNode> versions = qualifiers.findValues("versions");
final Map<String, Integer> usages = new HashMap<>();
final ArrayNode arrayNode = JsonHandler.createArrayNode();
for (final JsonNode version : versions) {
for (final JsonNode object : version) {
final ObjectNode objectNode = JsonHandler.createObjectNode();
objectNode.put("#id", object.get("_id").asText());
final String usage = object.get("DataObjectVersion").asText();
if (usages.containsKey(usage)) {
final Integer rank = usages.get(usage) + 1;
objectNode.put("Rank", rank);
} else {
usages.put(usage, 0);
objectNode.put("Rank", 0);
}
objectNode.put("DataObjectVersion", usage);
objectNode.put("Size", object.get("Size").asText());
if (object.get("FileInfo").get("LastModified") != null) {
objectNode.put("LastModified", object.get("FileInfo").get("LastModified").asText());
}
if (object.get("FormatIdentification").get("FormatLitteral") != null) {
objectNode.put("FormatLitteral", object.get("FormatIdentification").get("FormatLitteral").asText());
}
if (object.get("FileInfo").get("Filename") != null) {
objectNode.put("FileName", object.get("FileInfo").get("Filename").asText());
}
objectNode.set("metadatas", object);
arrayNode.add(objectNode);
nbObjects++;
}
}
resultNode.put("nbObjects", nbObjects);
resultNode.set("versions", arrayNode);
return resultNode;
}
/**
* This method builds an ObjectNode based on a list of JsonNode object
*
* @param unitId
* @param allParents list of JsonNode Objects used to build the referential
* @return An ObjectNode where the key is the identifier and the value is the parent details (Title, Id, _up)
* @throws VitamException
*/
public static ObjectNode buildAllParentsRef(String unitId, JsonNode allParents) throws VitamException {
ParametersChecker.checkParameter("Result cannot be empty", allParents);
boolean hasUnitId = false;
final ObjectNode allParentsRef = JsonHandler.createObjectNode();
for (final JsonNode currentParentNode : allParents) {
if (!currentParentNode.has(UiConstants.ID.getResultCriteria())) {
throw new VitamException(MISSING_ID_ERROR_MSG);
}
if (!currentParentNode.has(UiConstants.UNITUPS.getResultCriteria())) {
throw new VitamException(MISSING_UP_ERROR_MSG);
}
if (!currentParentNode.get(UiConstants.UNITUPS.getResultCriteria()).isArray()) {
throw new VitamException(INVALID_UP_FIELD_ERROR_MSG);
}
final String currentParentId = currentParentNode.get(UiConstants.ID.getResultCriteria()).asText();
allParentsRef.set(currentParentId, currentParentNode);
if (unitId.equalsIgnoreCase(currentParentId)) {
hasUnitId = true;
}
}
if (!hasUnitId) {
throw new VitamException(MISSING_UNIT_ID_ERROR_MSG);
}
return allParentsRef;
}
/**
* Generates execution time by step relative to a logbook operation
*
* @param logbookOperation
* @return CSV report
* @throws VitamException
* @throws IOException
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static ByteArrayOutputStream buildLogbookStatCsvFile(JsonNode logbookOperation)
throws VitamException, IOException {
final ByteArrayOutputStream csvOutputStream = new ByteArrayOutputStream();
try (Writer csvWriter = new BufferedWriter(new OutputStreamWriter(csvOutputStream));) {
// Total execution time
final String startOperationTimeStr = logbookOperation.get(EVENT_DATE_TIME_FIELD).asText();
final List<JsonNode> events =
IteratorUtils.toList(logbookOperation.get(EVENTS_FIELD).iterator());
// Last event
final JsonNode lastEvent = events.get(events.size() - 1);
final String endOperationTimeStr = lastEvent.get(EVENT_DATE_TIME_FIELD).asText();
// Generate CSV report
final CSVPrinter csvPrinter = new CSVPrinter(csvWriter,
CSVFormat.newFormat(SEMI_COLON_SEPARATOR).withRecordSeparator(RECORD_SEPARATOR));
final List<String> header = IteratorUtils.toList(lastEvent.fieldNames());
header.add(START_EVENT_DATETIME_HEADER);
header.add(END_EVENT_DATETIME_HEADER);
header.add(EXECUTION_TIME_HEADER);
csvPrinter.printRecord(header);
for (int i = 0; i < events.size() - 1; i += 2) {
final JsonNode startEvent = events.get(i);
final JsonNode endEvent = events.get(i + 1);
final List<String> eventReportDetails = IteratorUtils.toList(endEvent.elements());
final String startEventDateTimeStr = startEvent.get(EVENT_DATE_TIME_FIELD).asText();
final String endEventDateTimeStr = endEvent.get(EVENT_DATE_TIME_FIELD).asText();
eventReportDetails.add(startEventDateTimeStr);
eventReportDetails.add(endEventDateTimeStr);
eventReportDetails.add(calculateExecutionTime(startEventDateTimeStr, endEventDateTimeStr).toString());
csvPrinter.printRecord(eventReportDetails);
}
// Last Event
final List<String> lastEventDetails = IteratorUtils.toList(lastEvent.elements());
lastEventDetails.add(startOperationTimeStr);
lastEventDetails.add(endOperationTimeStr);
lastEventDetails.add(calculateExecutionTime(startOperationTimeStr, endOperationTimeStr).toString());
csvPrinter.printRecord(lastEventDetails);
csvPrinter.flush();
csvPrinter.close();
} catch (final Exception e) {
csvOutputStream.close();
throw new VitamException(UNEXPECTED_EXCEPTION_DURING_CSV_FILE_GENERATION);
}
return csvOutputStream;
}
private static Long calculateExecutionTime(String startDateTimeStr, String endDateTimeStr) throws ParseException {
final Date startDateTime = LocalDateUtil.getDate(startDateTimeStr);
final Date endDateTime = LocalDateUtil.getDate(endDateTimeStr);
return endDateTime.getTime() - startDateTime.getTime();
}
}