Pull request #1: RED-231: Implemented logic

Merge in RED/redaction-report-service from RED-231 to master

* commit 'b9ac93aae92270edb13bf9e055c94e39f5fdfb57':
  RED-231: Implemented logic
This commit is contained in:
Dominique Eiflaender 2020-09-02 13:58:14 +02:00
commit 1b04d4c668
12 changed files with 249 additions and 1 deletions

View File

@ -14,6 +14,11 @@
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>redaction-service-api-v1</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<!-- This dependency contains annotations that are used in specifying REST endpoints. -->
<!-- It is optional since not all users of this API might use Feign. -->

View File

@ -0,0 +1,17 @@
package com.iqser.red.service.redaction.report.v1.api.model;
import com.iqser.red.service.redaction.v1.model.RedactionLog;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileRedactionLog {
private String filename;
private RedactionLog redactionLog;
}

View File

@ -0,0 +1,17 @@
package com.iqser.red.service.redaction.report.v1.api.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MultiFileRedactionLog {
private List<FileRedactionLog> FileRedactionLogs = new ArrayList<>();
}

View File

@ -0,0 +1,13 @@
package com.iqser.red.service.redaction.report.v1.api.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReportResult {
private byte[] document;
}

View File

@ -0,0 +1,17 @@
package com.iqser.red.service.redaction.report.v1.api.resource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.iqser.red.service.redaction.report.v1.api.model.MultiFileRedactionLog;
import com.iqser.red.service.redaction.report.v1.api.model.ReportResult;
public interface RedactionReportResource {
String SERVICE_NAME = "redaction-report-service-v1";
@PostMapping(value = "/generate", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
ReportResult generateReport(@RequestBody MultiFileRedactionLog multiFileRedactionLog);
}

View File

@ -19,6 +19,21 @@
<artifactId>redaction-report-service-api-v1</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.iqser.gin4.commons</groupId>
<artifactId>spring-commons</artifactId>

View File

@ -0,0 +1,23 @@
package com.iqser.red.service.redaction.report.v1.server.controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.redaction.report.v1.api.model.MultiFileRedactionLog;
import com.iqser.red.service.redaction.report.v1.api.model.ReportResult;
import com.iqser.red.service.redaction.report.v1.api.resource.RedactionReportResource;
import com.iqser.red.service.redaction.report.v1.server.service.ReportGenerationService;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class RedactionReportController implements RedactionReportResource {
private final ReportGenerationService reportGenerationService;
public ReportResult generateReport(@RequestBody MultiFileRedactionLog multiFileRedactionLog){
return new ReportResult(reportGenerationService.generateReport(multiFileRedactionLog));
}
}

View File

@ -0,0 +1,79 @@
package com.iqser.red.service.redaction.report.v1.server.service;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.report.v1.api.model.MultiFileRedactionLog;
import com.iqser.red.service.redaction.report.v1.server.utils.ResourceLoader;
@Service
public class ReportGenerationService {
private byte[] template;
@PostConstruct
public void init() {
template = ResourceLoader.load("templates/Appendix A2.docx");
}
public byte[] generateReport(MultiFileRedactionLog multiFileRedactionLog) {
try (ByteArrayInputStream is = new ByteArrayInputStream(template)) {
XWPFDocument doc = new XWPFDocument(is);
doc = addTableRows(doc, multiFileRedactionLog);
return toByteArray(doc);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private XWPFDocument addTableRows(XWPFDocument doc, MultiFileRedactionLog multiFileRedactionLog) {
XWPFTable table = doc.getTables().get(1);
multiFileRedactionLog.getFileRedactionLogs().forEach(fileRedactionLog -> {
fileRedactionLog.getRedactionLog().getRedactionLogEntry().forEach(redactionLogEntry -> {
Set<Integer> pages = new HashSet<>();
redactionLogEntry.getPositions().forEach(position -> {
pages.add(position.getPage());
});
pages.forEach(page -> {
XWPFTableRow row = table.createRow();
row.getCell(0).setText(fileRedactionLog.getFilename());
row.getCell(1).setText(String.valueOf(page));
row.getCell(2).setText(String.valueOf(redactionLogEntry.getSectionNumber()));
row.getCell(3).setText(redactionLogEntry.getReason());
});
});
});
return doc;
}
private byte[] toByteArray(XWPFDocument doc) throws FileNotFoundException, IOException {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
doc.write(byteArrayOutputStream);
doc.close();
return byteArrayOutputStream.toByteArray();
}
}
}

View File

@ -0,0 +1,24 @@
package com.iqser.red.service.redaction.report.v1.server.utils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import lombok.experimental.UtilityClass;
@UtilityClass
public class ResourceLoader {
public byte[] load(String classpathPath) {
URL resource = ResourceLoader.class.getClassLoader().getResource(classpathPath);
if (resource == null) {
throw new IllegalArgumentException("could not load classpath resource: " + classpathPath);
}
try (InputStream is = resource.openStream()){
return is.readAllBytes();
} catch (IOException e){
throw new IllegalArgumentException("could not load classpath resource: " + classpathPath, e);
}
}
}

View File

@ -1,16 +1,53 @@
package com.iqser.red.service.redaction.report.v1.server;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.redaction.report.v1.api.model.FileRedactionLog;
import com.iqser.red.service.redaction.report.v1.api.model.MultiFileRedactionLog;
import com.iqser.red.service.redaction.report.v1.api.model.ReportResult;
import com.iqser.red.service.redaction.report.v1.server.controller.RedactionReportController;
import com.iqser.red.service.redaction.v1.model.RedactionLog;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = DEFINED_PORT)
public class RedactionReportIntegrationTest {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private RedactionReportController redactionReportController;
@Test
public void testServiceTemplate() {
public void testReportGeneration() throws IOException {
ClassPathResource redactionLogResource = new ClassPathResource("files/RedactionLog.txt");
RedactionLog redactionLog = objectMapper.readValue(redactionLogResource.getInputStream(), RedactionLog.class);
FileRedactionLog fileRedactionLog = new FileRedactionLog("Metholachlor 1", redactionLog);
MultiFileRedactionLog multiFileRedactionLog = new MultiFileRedactionLog(List.of(fileRedactionLog));
ReportResult reportResult = redactionReportController.generateReport(multiFileRedactionLog);
try (FileOutputStream fileOutputStream = new FileOutputStream("/tmp/document.docx")) {
fileOutputStream.write(reportResult.getDocument());
}
assertThat(true).isTrue();
}

View File

@ -0,0 +1 @@
{"redactionLogEntry":[{"id":"3080535605f06a5a397c4a9a5e501e9e","type":"address","value":"Regina Dorn","reason":"Applicant information was found","redacted":true,"section":"1.2.1 Name and address of applicant(s) for approval of the active substance","color":[0.0,1.0,1.0],"positions":[{"topLeft":{"x":147.86,"y":226.28998},"width":32.17697,"height":11.017679,"page":1},{"topLeft":{"x":181.79697,"y":226.28998},"width":23.68721,"height":11.017679,"page":1}],"sectionNumber":3,"manual":false,"hint":false},{"id":"b54d906a78a3988c54c67f188acab849","type":"address","value":"Schwarzwaldallee 215 P.O. Box CH-4002 Basel Switzerland","reason":"Applicant information was found","redacted":true,"section":"1.2.1 Name and address of applicant(s) for approval of the active substance","color":[0.0,1.0,1.0],"positions":[{"topLeft":{"x":147.86,"y":289.52997},"width":81.095245,"height":11.017679,"page":1},{"topLeft":{"x":230.73732,"y":289.52997},"width":17.438568,"height":11.017679,"page":1},{"topLeft":{"x":147.86,"y":276.93},"width":20.562881,"height":11.017679,"page":1},{"topLeft":{"x":170.18288,"y":276.93},"width":19.359528,"height":11.017679,"page":1},{"topLeft":{"x":147.86,"y":264.21002},"width":41.950012,"height":11.017679,"page":1},{"topLeft":{"x":191.57,"y":264.21002},"width":25.519852,"height":11.017679,"page":1},{"topLeft":{"x":147.86,"y":251.60999},"width":53.649796,"height":11.017679,"page":1}],"sectionNumber":3,"manual":false,"hint":false},{"id":"7b4ff6e32a8bdaa7de5dcdeb13ff8edd","type":"address","value":"regina.dorn@syngenta.com","reason":"Applicant information was found","redacted":true,"section":"1.2.1 Name and address of applicant(s) for approval of the active substance","color":[0.0,1.0,1.0],"positions":[{"topLeft":{"x":147.86,"y":188.34003},"width":122.451065,"height":11.017679,"page":1}],"sectionNumber":3,"manual":false,"hint":false},{"id":"c357d6781fb4bb7e7020a67dbc63763c","type":"address","value":"Syngenta Crop Protection AG","reason":"Applicant information was found","redacted":true,"section":"1.2.1 Name and address of applicant(s) for approval of the active substance","color":[0.0,1.0,1.0],"positions":[{"topLeft":{"x":147.86,"y":302.25},"width":41.88115,"height":11.017679,"page":1},{"topLeft":{"x":191.50114,"y":302.25},"width":23.080002,"height":11.017679,"page":1},{"topLeft":{"x":216.34114,"y":302.25},"width":46.23088,"height":11.017679,"page":1},{"topLeft":{"x":264.2106,"y":302.25},"width":16.886566,"height":11.017679,"page":1}],"sectionNumber":3,"manual":false,"hint":false},{"id":"946a2cc404d6568d34b1ca9a4c688ec9","type":"address","value":"+41 (61) 323 6358","reason":"Applicant information was found","redacted":true,"section":"1.2.1 Name and address of applicant(s) for approval of the active substance","color":[0.0,1.0,1.0],"positions":[{"topLeft":{"x":147.86,"y":213.65997},"width":18.266571,"height":11.017679,"page":1},{"topLeft":{"x":167.88657,"y":213.65997},"width":19.315353,"height":11.017679,"page":1},{"topLeft":{"x":188.99504,"y":213.65997},"width":17.438568,"height":11.017679,"page":1},{"topLeft":{"x":208.1936,"y":213.65997},"width":23.080017,"height":11.017679,"page":1}],"sectionNumber":3,"manual":false,"hint":false},{"id":"687171974a4fc3aa0f43ec2118256590","type":"address","value":"+41 (61) 323 6155","reason":"Applicant information was found","redacted":true,"section":"1.2.1 Name and address of applicant(s) for approval of the active substance","color":[0.0,1.0,1.0],"positions":[{"topLeft":{"x":147.86,"y":200.94},"width":18.266571,"height":11.017679,"page":1},{"topLeft":{"x":167.88657,"y":200.94},"width":19.315353,"height":11.017679,"page":1},{"topLeft":{"x":188.99504,"y":200.94},"width":17.438568,"height":11.017679,"page":1},{"topLeft":{"x":208.1936,"y":200.94},"width":23.080017,"height":11.017679,"page":1}],"sectionNumber":3,"manual":false,"hint":false}]}