Commit c5305202 authored by Gaëlle FOURNIER's avatar Gaëlle FOURNIER
Browse files

Generate Probative value PDF



* remove apache FOP for performance problems
* use vitamui xdocreport for pdf generation with update freemarker version
* Re-enable probative value export ZIP containing PDF and json
Co-authored-by: default avatarbenemart <benedicte.martinez@cea.fr>
parent 3b1b25b1
......@@ -68,5 +68,14 @@ public class ProbativeOperationDto {
@JsonProperty("evDateTime")
private String evDateTime;
@JsonProperty("archivalAgreement")
private String archivalAgreement;
@JsonProperty("profil")
private String profil;
@JsonProperty("accessContract")
private String accessContract;
}
......@@ -38,6 +38,7 @@ package fr.gouv.vitamui.referential.common.export.probativevalue.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
......@@ -61,6 +62,9 @@ public class ProbativeReportDto {
@JsonProperty("reportEntries")
private List<ReportEntryDto> reportEntries;
@JsonProperty("ReportVersion")
private Integer ReportVersion;
@JsonProperty("reportVersion")
@JsonAlias("ReportVersion")
private Integer reportVersion;
}
\ No newline at end of file
......@@ -46,7 +46,7 @@
<groupId>fr.gouv.vitamui</groupId>
<artifactId>iam-security</artifactId>
</dependency>
<!-- SPRING BOOT -->
<dependency>
......@@ -106,19 +106,42 @@
<!-- PDF generation -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.odfdom.converter.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.converter.odt.odfdom</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.odt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>javax.media</groupId>
<artifactId>jai-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.media</groupId>
<artifactId>jai-codec</artifactId>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.upgraded.version}</version>
</dependency>
<!-- Metrics -->
<dependency>
......@@ -162,10 +185,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
......@@ -225,6 +244,25 @@
</dependencies>
<build>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>false</filtering>
</testResource>
</testResources>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources/templates</directory>
<filtering>false</filtering>
<excludes>
<exclude>*.odt</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
......
......@@ -50,29 +50,13 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.xml.sax.SAXException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import fr.gouv.vitam.common.client.VitamContext;
import fr.gouv.vitam.common.exception.InvalidParseOperationException;
......@@ -86,10 +70,12 @@ import fr.gouv.vitamui.commons.api.exception.InternalServerException;
import fr.gouv.vitamui.commons.api.logger.VitamUILogger;
import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
import fr.gouv.vitamui.commons.api.utils.ZipUtils;
import fr.gouv.vitamui.commons.utils.PdfFileGenerator;
import fr.gouv.vitamui.commons.vitam.api.access.UnitService;
import fr.gouv.vitamui.commons.vitam.api.dto.ResultsDto;
import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto;
import fr.gouv.vitamui.referential.common.export.probativevalue.dto.ItemWithLabelDto;
import fr.gouv.vitamui.referential.common.export.probativevalue.dto.ProbativeOperationDto;
import fr.gouv.vitamui.referential.common.export.probativevalue.dto.ProbativeReportDto;
import fr.gouv.vitamui.referential.common.export.probativevalue.dto.ReportEntryDto;
import fr.gouv.vitamui.referential.common.service.VitamBatchReportService;
......@@ -101,6 +87,8 @@ import fr.gouv.vitamui.referential.common.service.VitamBatchReportService;
@Service
public class ProbativeValueInternalService {
private static final String TEMPLATE_PROBATIVEVALUEREPORT_ODT = "templates/probativevaluereport.ftl.odt";
private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(ProbativeValueInternalService.class);
final private VitamBatchReportService vitamBatchReportService;
......@@ -127,7 +115,6 @@ public class ProbativeValueInternalService {
final String workspaceOperationPath, final OutputStream outputStream) {
checkWorkspacePath(workspaceOperationPath);
getProbativeValueReportJson(vitamContext, operationId, workspaceOperationPath);
generateXml(vitamContext, operationId, workspaceOperationPath);
generatePDF(vitamContext, operationId, workspaceOperationPath);
generateZip(operationId, workspaceOperationPath, outputStream);
......@@ -135,8 +122,7 @@ public class ProbativeValueInternalService {
private void getProbativeValueReportJson(final VitamContext vitamContext, final String operationId,
final String workspaceOperationPath) {
try (InputStream reportStream = vitamBatchReportService.downloadBatchReport(vitamContext,
operationId)) {
try (InputStream reportStream = vitamBatchReportService.downloadBatchReport(vitamContext, operationId)) {
File file = new File(workspaceOperationPath, operationId + ".json");
FileUtils.copyInputStreamToFile(reportStream, file);
} catch (VitamClientException e) {
......@@ -149,71 +135,32 @@ public class ProbativeValueInternalService {
}
private void generateXml(final VitamContext vitamContext, final String operationId,
private void generatePDF(final VitamContext vitamContext, final String operationId,
final String workspaceOperationPath) {
try {
File jsonReport = new File(workspaceOperationPath, operationId + ".json");
File xmlReport = new File(workspaceOperationPath, operationId + ".xml");
ProbativeReportDto report = JsonHandler.getFromFile(jsonReport, ProbativeReportDto.class);
reportEntriesConsolidation(vitamContext, report);
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION);
xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
xmlMapper.writer().withRootName("report").writeValue(xmlReport, report);
} catch (VitamClientException | InvalidParseOperationException | IOException e) {
LOGGER.error(e.getMessage());
throw new InternalServerException("Unable to create XML data from JSON Probative Value Report", e);
}
}
private void generatePDF(final VitamContext vitamContext, final String operationId,
final String workspaceOperationPath) {
File xmlfile = new File(workspaceOperationPath, operationId + ".xml");
try (InputStream fopconfigfile = getClass().getClassLoader().getResourceAsStream("fop/fop-config.xml");
InputStream xsltfile = getClass().getClassLoader().getResourceAsStream("fop/probativevaluereport.xsl");){
File pdffile = new File(workspaceOperationPath, operationId + ".pdf");
// configure fopFactory as desired
final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI(), fopconfigfile);
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// configure foUserAgent as desired
try (InputStream odtTemplate = getClass().getClassLoader()
.getResourceAsStream(TEMPLATE_PROBATIVEVALUEREPORT_ODT);
OutputStream pdfOutputStream = new java.io.FileOutputStream(pdffile);) {
// Setup output
try (OutputStream out = new java.io.FileOutputStream(pdffile)) {
// Construct fop with desired output format
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
// Setup XSLT
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));
// Set the value of a <param> in the stylesheet
transformer.setParameter("versionParam", "2.0");
// Setup input for XSLT transformation
Source src = new StreamSource(xmlfile);
// Resulting SAX events (the generated FO) must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Start XSLT transformation and FOP processing
transformer.transform(src, res);
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("report", report);
PdfFileGenerator.createPdf(odtTemplate, pdfOutputStream, dataMap);
} catch (Exception e) {
LOGGER.error(e.getMessage());
throw new InternalServerException("Unable to create PDF from Probative Value Report template ODT", e);
}
} catch (TransformerException | IOException | SAXException e) {
LOGGER.error(e.getMessage());
throw new InternalServerException("Unable to create PDF from Probative Value Report XML", e);
} finally {
FileUtils.deleteQuietly(xmlfile);
} catch (InvalidParseOperationException | VitamClientException exc) {
LOGGER.error(exc.getMessage());
throw new InternalServerException("Unable to create PDF from Probative Value Report Json value", exc);
}
}
private void generateZip(final String operationId, final String workspaceOperationPath,
......@@ -225,6 +172,7 @@ public class ProbativeValueInternalService {
streams.put(operationId + ".json", new FileInputStream(jsonFile));
streams.put(operationId + ".pdf", new FileInputStream(pdfFile));
} catch (FileNotFoundException e) {
LOGGER.error(e.getMessage());
throw new InternalServerException(String.format("Unable to generate ZIP: %s", e.getMessage()), e);
}
ZipUtils.generate(streams, outputStream);
......@@ -263,9 +211,30 @@ public class ProbativeValueInternalService {
ObjectGroupResponse.class);
Optional<QualifiersModel> qualifierObject = objectGroupResponse.getQualifiers().stream()
.filter(item -> usage.equals(item.getQualifier())).findFirst();
Optional<VersionsModel> qualifierVersionObject = qualifierObject.get().getVersions().stream()
.filter(item -> version.equals(String.valueOf(item.getDataVersion()))).findFirst();
reportEntry.setObjectLabel(qualifierVersionObject.get().getFileInfoModel().getFilename());
if (qualifierObject.isPresent()) {
Optional<VersionsModel> qualifierVersionObject = qualifierObject.get().getVersions().stream()
.filter(item -> version.equals(String.valueOf(item.getDataVersion()))).findFirst();
if (qualifierVersionObject.isPresent()) {
reportEntry.setObjectLabel(qualifierVersionObject.get().getFileInfoModel().getFilename());
}
}
}
// extract operations infos from JsonNode RightsStatementIdentifier
for (ProbativeOperationDto operation : reportEntry.getOperations()) {
if (operation.getRightsStatementIdentifier() != null) {
if (operation.getRightsStatementIdentifier().has("ArchivalAgreement")) {
operation.setArchivalAgreement(
operation.getRightsStatementIdentifier().get("ArchivalAgreement").asText());
}
if (operation.getRightsStatementIdentifier().has("Profil")) {
operation.setProfil(operation.getRightsStatementIdentifier().get("Profil").asText());
}
if (operation.getRightsStatementIdentifier().has("AccessContract")) {
operation.setAccessContract(
operation.getRightsStatementIdentifier().get("AccessContract").asText());
}
}
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="fo">
<xsl:output method="xml" version="1.0"
omit-xml-declaration="no" indent="yes" />
<xsl:param name="versionParam" select="'1.0'" />
<!-- ========================= -->
<!-- root element: report -->
<!-- ========================= -->
<xsl:template match="report">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simpleA4"
page-height="29.7cm" page-width="21cm" margin-top="3cm"
margin-bottom="3cm" margin-left="2cm" margin-right="2cm"
font-size="14pt">
<fo:region-body margin-bottom="2cm" margin-top="2cm"/>
<fo:region-before region-name="xsl-region-before" extent="2cm" />
<fo:region-after region-name="xsl-region-after" extent="2cm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="simpleA4">
<xsl:call-template name="globalHeader" />
<xsl:call-template name="globalFooter" />
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates select="operationSummary" />
<xsl:apply-templates select="reportSummary" />
</fo:flow>
</fo:page-sequence>
<fo:page-sequence master-reference="simpleA4">
<xsl:call-template name="globalHeader" />
<xsl:call-template name="globalFooter" />
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates select="context" />
</fo:flow>
</fo:page-sequence>
<xsl:apply-templates select="reportEntries/reportEntries" />
</fo:root>
</xsl:template>
<xsl:template name="globalHeader">
<fo:static-content flow-name="xsl-region-before">
<fo:block font-size="20pt" font-weight="bold" text-align="center" border-width="1pt" border-style="solid" padding-top="10pt" padding-bottom="8pt">
RELEVE DE VALEUR PROBANTE
</fo:block>
</fo:static-content>
</xsl:template>
<xsl:template name="globalFooter">
<fo:static-content flow-name="xsl-region-after">
<fo:block text-align="end">Page <fo:page-number/> / <fo:page-number-citation ref-id="TheVeryLastPage"/> </fo:block>
</fo:static-content>
</xsl:template>
<xsl:template match="operationSummary">
<fo:block font-size="16pt" font-weight="bold" padding-top="40pt">== RESUME DE L'OPERATION ==</fo:block>
<fo:table border-width="1pt" border-style="none">
<fo:table-column column-width="5.5cm" />
<fo:table-column />
<fo:table-body>
<fo:table-row>
<fo:table-cell padding-top="20pt">
<fo:block>Tenant</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="20pt">
<fo:block>
<xsl:value-of select="tenant" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-top="5pt">
<fo:block>Identifiant de l'opération</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="evId" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-top="5pt">
<fo:block>Type d'opération</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="evType" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-top="5pt">
<fo:block>Statut</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="outcome" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-top="5pt">
<fo:block>Détail</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="outDetail" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-top="5pt">
<fo:block>Message</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="outMsg" />
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="reportSummary">
<fo:block font-size="16pt" font-weight="bold" padding-top="40pt">== RESUME DU RAPPORT ==</fo:block>
<fo:table border-width="1pt" border-style="none">
<fo:table-column column-width="5.5cm" />
<fo:table-column />
<fo:table-body>
<fo:table-row>
<fo:table-cell padding-top="20pt">
<fo:block>Début</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="20pt">
<fo:block>
<xsl:value-of select="evStartDateTime" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-top="5pt">
<fo:block>Fin</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="evEndDateTime" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-top="5pt">
<fo:block>Type</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="reportType" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<xsl:apply-templates select="vitamResults" />
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="vitamResults">
<fo:table-row>
<fo:table-cell number-columns-spanned="2"
padding-top="10pt">
<fo:block>Résultats</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-left="20pt" padding-top="10pt">
<fo:block>Succès</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="10pt">
<fo:block>
<xsl:value-of select="OK" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-left="20pt" padding-top="5pt">
<fo:block>Erreurs</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="KO" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-left="20pt" padding-top="5pt">
<fo:block>Avertissements</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="WARNING" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-left="20pt" font-weight="bold" padding-top="5pt">
<fo:block>Total</fo:block>
</fo:table-cell>
<fo:table-cell padding-top="5pt">
<fo:block>
<xsl:value-of select="total" />
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="context">
<fo:block font-size="16pt" font-weight="bold" padding-top="40pt">== REQUETE EFFECTUEE ==</fo:block>
<fo:table border-width="1pt" border-style="none">
<fo:table-column column-width="5.5cm" />
<fo:table-column />