package org.akaza.openclinica.bean.extract;
import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.akaza.openclinica.bean.core.EntityBean;
import org.akaza.openclinica.bean.managestudy.DiscrepancyNoteBean;
import org.akaza.openclinica.service.DiscrepancyNoteThread;
import org.akaza.openclinica.service.DiscrepancyNoteUtil;
import org.apache.commons.lang.StringEscapeUtils;
import com.lowagie.text.BadElementException;
import com.lowagie.text.Cell;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.HeaderFooter;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Table;
import com.lowagie.text.pdf.PdfWriter;
/**
* This class converts or serializes DiscrepancyNoteBeans to Strings or iText-related
* classes so that they can be compiled into a file and downloaded to the user. This is a
* convenience class with a number of different methods for serializing beans to Strings.
* @see org.akaza.openclinica.control.extract.DiscrepancyNoteOutputServlet
* @author Bruce W. Perry
*
*/
public class DownloadDiscrepancyNote implements DownLoadBean{
public static String CSV ="text/plain; charset=UTF-8";
public static String PDF = "application/pdf";
public static String COMMA = ",";
public static Map<Integer,String> RESOLUTION_STATUS_MAP = new HashMap<Integer,String> ();
static{
RESOLUTION_STATUS_MAP.put(1,"New");
RESOLUTION_STATUS_MAP.put(2,"Updated");
RESOLUTION_STATUS_MAP.put(3,"Resolution Proposed");
RESOLUTION_STATUS_MAP.put(4,"Closed");
RESOLUTION_STATUS_MAP.put(5,"Not Applicable");
}
//Does the user want the first line of the CSV to be column headers
private final boolean firstColumnHeaderLine;
//A list of DiscrepancyNoteBeans to be downloaded together
private final List<DiscrepancyNoteBean> discrepancyBeanList =
new ArrayList<DiscrepancyNoteBean>();
public DownloadDiscrepancyNote() {
this.firstColumnHeaderLine = false;
}
public DownloadDiscrepancyNote(boolean firstColumnHeaderLine) {
this.firstColumnHeaderLine = firstColumnHeaderLine;
}
public void downLoad(EntityBean bean,
String format,
OutputStream stream) {
if(bean == null || stream == null ||
!( bean instanceof org.akaza.openclinica.bean.managestudy.DiscrepancyNoteBean)){
throw new IllegalStateException(
"An invalid parameter was passed to the DownloadDiscrepancyNote.downLoad method.");
}
DiscrepancyNoteBean discNBean = (DiscrepancyNoteBean) bean;
//This must be a ServletOutputStream for our purposes
ServletOutputStream servletStream = (ServletOutputStream) stream;
try{
if(CSV.equalsIgnoreCase(format)) {
servletStream.print(serializeToString(discNBean, false, 0));
} else {
//Create PDF version
serializeToPDF(discNBean,servletStream);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(servletStream != null){
try {
servletStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void downLoad(List<EntityBean> listOfBeans, String format,
OutputStream stream) {
//The List must be of DiscrepancyNoteBeans
if (listOfBeans == null ) {
return;
}
StringBuilder allContent = new StringBuilder();
String singleBeanContent="";
for(EntityBean discNoteBean : listOfBeans){
if(! (discNoteBean instanceof DiscrepancyNoteBean)) return;
DiscrepancyNoteBean discNBean = (DiscrepancyNoteBean) discNoteBean;
singleBeanContent = serializeToString(discNBean, false, 0);
allContent.append(singleBeanContent);
allContent.append("\n");
}
//This must be a ServletOutputStream for our purposes
ServletOutputStream servletStream = (ServletOutputStream) stream;
try{
if(CSV.equalsIgnoreCase(format)) {
servletStream.print(allContent.toString());
} else {
//Create PDF version
serializeListToPDF(allContent.toString(),servletStream);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(servletStream != null){
try {
servletStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public int getContentLength(EntityBean bean, String format) {
return serializeToString(bean, false, 0).getBytes().length;
}
public int getListContentLength(List<DiscrepancyNoteBean> beans, String format) {
int totalLength = 0;
int count = 0;
for(DiscrepancyNoteBean bean : beans) {
++count;
//Only count the byte length of a CSV header row for the first DNote
totalLength += serializeToString(bean, (count == 1), 0).getBytes().length;
totalLength += "\n".getBytes().length;
}
return totalLength;
}
public int getThreadListContentLength(List<DiscrepancyNoteThread> threadBeans) {
int totalLength = 0;
int count = 0;
int threadCount = 1;
for(DiscrepancyNoteThread discrepancyNoteThread : threadBeans) {
for(DiscrepancyNoteBean discNoteBean : discrepancyNoteThread.getLinkedNoteList()) {
//DiscrepancyNoteBean discNoteBean = discrepancyNoteThread.getLinkedNoteList().getFirst();
++count;
//Only count the byte length of a CSV header row for the first DNote; we're only
//using response.setContentlength for CSV format, because apparently it is not
//necessary for PDF
totalLength += serializeToString(discNoteBean, (count == 1), 0).getBytes().length;
//each DN bean with have a column indicating the thread number for the
//note
totalLength += "\n".getBytes().length;
}
totalLength += ("Thread number: "+threadCount).getBytes().length;
//increment threadCounter
threadCount++;
}
return totalLength;
}
public String serializeToString(EntityBean bean, boolean includeHeaderRow,
int threadNumber){
DiscrepancyNoteBean discNoteBean = (DiscrepancyNoteBean) bean;
StringBuilder writer = new StringBuilder("");
//If includeHeaderRow = true, the first row of the output consists of header names, only
//for CSV format
if(includeHeaderRow) {
writer.append("Study Subject ID");
writer.append(",");
writer.append("Subject Status");
writer.append(",");
writer.append("Study/Site OID");
writer.append(",");
//we're adding a thread number row
writer.append("Thread ID");
writer.append(",");
writer.append("Note ID");
writer.append(",");
writer.append("Parent Note ID");
writer.append(",");
writer.append("Date Created");
writer.append(",");
writer.append("Date Update");
writer.append(",");
writer.append("Days Open");
writer.append(",");
writer.append("Days Since Updated");
writer.append(",");
if(discNoteBean.getDisType() != null) {
writer.append("Discrepancy Type");
writer.append(",");
}
writer.append("Resolution Status");
writer.append(",");
writer.append("Event Name");
writer.append(",");
writer.append("Event Occurrence");
writer.append(",");
writer.append("CRF Name");
writer.append(",");
writer.append("CRF Status");
writer.append(",");
writer.append("Group Label");
writer.append(",");
writer.append("Group Ordinal");
writer.append(",");
writer.append("Entity name");
writer.append(",");
writer.append("Entity value");
writer.append(",");
writer.append("Description");
writer.append(",");
writer.append("Detailed Notes");
writer.append(",");
writer.append("Assigned User");
writer.append(",");
writer.append("Study Id");
writer.append("\n");
}
//Fields with embedded commas must be
// delimited with double-quote characters.
writer.append(escapeQuotesInCSV(discNoteBean.getStudySub().getLabel()));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getStudySub().getStatus().getName()));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getStudy().getOid()));
writer.append(",");
writer.append(escapeQuotesInCSV(threadNumber+""));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getId()+""));
writer.append(",");
writer.append(discNoteBean.getParentDnId()>0?discNoteBean.getParentDnId():"");
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getCreatedDateString()+""));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getUpdatedDateString()+""));
writer.append(",");
if (discNoteBean.getParentDnId() == 0){
writer.append(escapeQuotesInCSV(discNoteBean.getAge()+""));
writer.append(",");
String daysSinceUpdated = escapeQuotesInCSV(discNoteBean.getDays()+"");
writer.append(daysSinceUpdated.equals("0") ? "" : daysSinceUpdated);
writer.append(",");
} else {
writer.append(",");
writer.append(",");
}
if (discNoteBean.getDisType() != null) {
writer.append(escapeQuotesInCSV(discNoteBean.getDisType().getName()));
writer.append(",");
}
writer.append(escapeQuotesInCSV(RESOLUTION_STATUS_MAP.get(discNoteBean.getResolutionStatusId())+""));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getEventName()));
writer.append(",");
String eventOccurrence = null != discNoteBean.getStudyEventDefinitionBean()
&& discNoteBean.getStudyEventDefinitionBean().isRepeating()
? String.valueOf(discNoteBean.getEvent().getSampleOrdinal()) : "";
writer.append(escapeQuotesInCSV(eventOccurrence));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getCrfName()));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getCrfStatus()));
writer.append(",");
String itemGroupName = discNoteBean.getItemGroupName() == null
? "" : String.valueOf(discNoteBean.getItemGroupName());
writer.append(escapeQuotesInCSV(itemGroupName));
writer.append(",");
String itemDataOccurence = discNoteBean.getItemDataOrdinal() == null
? "" : String.valueOf(discNoteBean.getItemDataOrdinal());
writer.append(escapeQuotesInCSV(itemDataOccurence));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getEntityName()));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getEntityValue()));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getDescription()+""));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getDetailedNotes()+""));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getAssignedUser().getName()));
writer.append(",");
writer.append(escapeQuotesInCSV(discNoteBean.getStudyId()+""));
writer.append("\n");
return writer.toString();
}
private void serializeToPDF(EntityBean bean, OutputStream stream) {
ServletOutputStream servletStream = (ServletOutputStream) stream;
DiscrepancyNoteBean discNBean = (DiscrepancyNoteBean) bean;
StringBuilder writer = new StringBuilder();
writer.append(serializeToString(discNBean, false, 0));
Document pdfDoc = new Document();
try {
PdfWriter.getInstance(pdfDoc,
servletStream);
pdfDoc.open();
pdfDoc.add(new Paragraph(writer.toString()));
} catch (DocumentException e) {
e.printStackTrace();
}
pdfDoc.close();
}
public void serializeListToPDF(String content, OutputStream stream) {
ServletOutputStream servletStream = (ServletOutputStream) stream;
Document pdfDoc = new Document();
try {
PdfWriter.getInstance(pdfDoc,
servletStream);
pdfDoc.open();
pdfDoc.add(new Paragraph(content));
} catch (DocumentException e) {
e.printStackTrace();
}
pdfDoc.close();
}
public void serializeListToPDF(List<DiscrepancyNoteBean> listOfBeans,
OutputStream stream, String studyIdentifier) {
ServletOutputStream servletStream = (ServletOutputStream) stream;
Document pdfDoc = new Document();
try {
PdfWriter.getInstance(pdfDoc,
servletStream);
pdfDoc.open();
//Create header for the study identifier or name
if(studyIdentifier != null) {
HeaderFooter header = new HeaderFooter(
new Phrase("Study Identifier: "+studyIdentifier+" pg."),true);
header.setAlignment(Element.ALIGN_CENTER);
Paragraph para = new Paragraph("Study Identifier: "+studyIdentifier,
new Font(Font.HELVETICA, 18, Font.BOLD, new Color(0, 0, 0)));
para.setAlignment(Element.ALIGN_CENTER);
pdfDoc.setHeader(header);
pdfDoc.add(para);
}
for(DiscrepancyNoteBean discNoteBean : listOfBeans){
pdfDoc.add(this.createTableFromBean(discNoteBean));
pdfDoc.add(new Paragraph("\n"));
}
//pdfDoc.add(new Paragraph(content));
} catch (DocumentException e) {
e.printStackTrace();
}
pdfDoc.close();
}
public void serializeThreadsToPDF(List<DiscrepancyNoteThread> listOfThreads,
OutputStream stream, String studyIdentifier) {
ServletOutputStream servletStream = (ServletOutputStream) stream;
Document pdfDoc = new Document();
try {
PdfWriter.getInstance(pdfDoc,
servletStream);
pdfDoc.open();
//Create header for the study identifier or name
if(studyIdentifier != null) {
HeaderFooter header = new HeaderFooter(
new Phrase("Study Identifier: "+studyIdentifier+" pg."),true);
header.setAlignment(Element.ALIGN_CENTER);
Paragraph para = new Paragraph("Study Identifier: "+studyIdentifier,
new Font(Font.HELVETICA, 18, Font.BOLD, new Color(0, 0, 0)));
para.setAlignment(Element.ALIGN_CENTER);
pdfDoc.setHeader(header);
pdfDoc.add(para);
}
for(DiscrepancyNoteThread discNoteThread : listOfThreads){
pdfDoc.add(this.createTableThreadHeader(discNoteThread));
//Just the parent of the thread? discNoteThread.getLinkedNoteList()
for(DiscrepancyNoteBean discNoteBean : discNoteThread.getLinkedNoteList()){
//DiscrepancyNoteBean discNoteBean = discNoteThread.getLinkedNoteList().getFirst();
if(discNoteBean.getParentDnId()>0) {
pdfDoc.add(this.createTableFromBean(discNoteBean));
pdfDoc.add(new Paragraph("\n"));
}
}
}
//pdfDoc.add(new Paragraph(content));
} catch (DocumentException e) {
e.printStackTrace();
}
pdfDoc.close();
}
public void downLoadDiscBeans(List<DiscrepancyNoteBean> listOfBeans,
String format,
OutputStream stream, String studyIdentifier) {
if (listOfBeans == null ) {
return;
}
StringBuilder allContent = new StringBuilder();
String singleBeanContent="";
int counter=0;
if(CSV.equalsIgnoreCase(format)) {
for(DiscrepancyNoteBean discNoteBean : listOfBeans){
++counter;
singleBeanContent = counter == 1 ? serializeToString(discNoteBean, true, 0) : serializeToString(discNoteBean, false, 0);
allContent.append(singleBeanContent);
allContent.append("\n");
}
}
//This must be a ServletOutputStream for our purposes
ServletOutputStream servletStream = (ServletOutputStream) stream;
try{
if(CSV.equalsIgnoreCase(format)) {
servletStream.print(allContent.toString());
} else {
//Create PDF version
this.serializeListToPDF(listOfBeans,servletStream, studyIdentifier);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(servletStream != null){
try {
servletStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void downLoadThreadedDiscBeans(List<DiscrepancyNoteThread> listOfThreadedBeans,
String format,
HttpServletResponse response, String studyIdentifier) throws Exception {
if (listOfThreadedBeans == null ) {
return;
}
StringBuilder allContent = new StringBuilder();
String singleBeanContent="";
int counter=0;
int threadCounter=0;
if(CSV.equalsIgnoreCase(format)) {
for(DiscrepancyNoteThread dnThread : listOfThreadedBeans ) {
threadCounter++;
for(DiscrepancyNoteBean discNoteBean : dnThread.getLinkedNoteList()){
//DiscrepancyNoteBean discNoteBean = dnThread.getLinkedNoteList().getFirst();
++counter;
singleBeanContent = counter == 1 ? serializeToString(discNoteBean, true, threadCounter) : serializeToString(discNoteBean, false, threadCounter);
allContent.append(singleBeanContent);
}
}
}
//This must be a ServletOutputStream for our purposes
ServletOutputStream servletStream = null;
try{
if(CSV.equalsIgnoreCase(format)) {
String result = StringEscapeUtils.unescapeJava(allContent.toString());
response.getWriter().print(result);
//servletStream.print(allContent.toString());
} else {
//Create PDF version
//this.serializeListToPDF(listOfBeans,servletStream, studyIdentifier);
servletStream = (ServletOutputStream) response.getOutputStream();
this.serializeThreadsToPDF(listOfThreadedBeans,servletStream, studyIdentifier);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(servletStream != null){
try {
servletStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private String escapeQuotesInCSV(String csvValue){
if(csvValue == null) return "";
if(csvValue.contains("\u2018")){
csvValue = csvValue.replaceAll("\u2018", "'");
}
if(csvValue.contains("\u201C")){
csvValue = csvValue.replaceAll("\u201C", "\"");
}
if(csvValue.contains("\r\n")){
csvValue = csvValue.replaceAll("\r\n", "");
}
if(csvValue.contains("\n")){
csvValue = csvValue.replaceAll("\n", "");
}
//Escaping special characters in the String.
csvValue = StringEscapeUtils.escapeJava(csvValue);
if(csvValue.contains(",")){
return new StringBuilder("\"").append(csvValue).append("\"").toString();
} else {
return csvValue;
}
}
private Table createTableThreadHeader(DiscrepancyNoteThread discNoteThread)
throws BadElementException {
Table table = new Table(2);
table.setTableFitsPage(true);
table.setCellsFitPage(true);
table.setBorderWidth(1);
table.setBorderColor(new java.awt.Color(0,0,0));
table.setPadding(4);
table.setSpacing(4);
if(discNoteThread == null || discNoteThread.getLinkedNoteList().isEmpty()){
return table;
}
//Get information for the header; the resolution status, however, has to be the latest
//resolution status for the DN thread
DiscrepancyNoteBean dnBean = discNoteThread.getLinkedNoteList().getFirst();
DiscrepancyNoteUtil discUtil = new DiscrepancyNoteUtil();
String latestResolutionStatus = discUtil.getResolutionStatusName(
discNoteThread.getLinkedNoteList().getFirst().getResolutionStatusId());
StringBuilder content = new StringBuilder("");
if(dnBean != null){
if(! "".equalsIgnoreCase(dnBean.getEntityName())) {
content.append("Item field name/value: ");
content.append(dnBean.getEntityName());
if(! "".equalsIgnoreCase(dnBean.getEntityValue())) {
content.append(" = ");
content.append(dnBean.getEntityValue());
}
}
Paragraph para = new Paragraph(content.toString(),
new Font(Font.HELVETICA, 14, Font.BOLD, new Color(0, 0, 0)));
Cell cell = new Cell(para);
cell.setHeader(true);
cell.setHorizontalAlignment(Element.ALIGN_LEFT);
cell.setColspan(2);
table.addCell(cell);
table.endHeaders();
//Add at least three more rows of data -- dnBean.getSubjectName()
//row 1
cell = createCell("Study Subject",dnBean.getSubjectName());
table.addCell(cell);
cell = createCell("Study Event",dnBean.getEventName());
table.addCell(cell);
//row 2
cell = createCell("Study Event Date",dnBean.getEventStart()+"");
table.addCell(cell);
StringBuilder tmpStrBuilder = new StringBuilder("CRF: ");
tmpStrBuilder.append(dnBean.getCrfName());
tmpStrBuilder.append("\n");
tmpStrBuilder.append("Status: ");
tmpStrBuilder.append(dnBean.getCrfStatus());
content.append(dnBean.getCrfName());
cell = new Cell(new Paragraph(tmpStrBuilder.toString(), new Font(Font.HELVETICA, 14, Font.BOLD, new Color(0, 0, 0))));
table.addCell(cell);
//row 3
cell = createCell("Type",discUtil.getResolutionStatusTypeName(
dnBean.getDiscrepancyNoteTypeId()));
table.addCell(cell);
cell = createCell("Resolution Status",
latestResolutionStatus);
table.addCell(cell);
cell = createCell("Number of notes",discNoteThread.getLinkedNoteList().size()+"");
table.addCell(cell);
cell = createCell("Discrepancy Note ID",dnBean.getId()+"");
table.addCell(cell);
cell = createCell("Days Open",dnBean.getAge()+"");
table.addCell(cell);
String daysSinceUpdated = escapeQuotesInCSV(dnBean.getDays()+"");
cell = createCell("Days Since Updated", daysSinceUpdated.equals("0") ? "" : daysSinceUpdated +"");
table.addCell(cell);
}
return table;
}
private Cell createCell(String propertyName, String propertyValue) throws BadElementException {
StringBuilder content = new StringBuilder(propertyName+": ");
content.append(propertyValue);
Paragraph para = new Paragraph(content.toString(),
new Font(Font.HELVETICA, 14, Font.BOLD, new Color(0, 0, 0)));
return new Cell(para);
}
private Paragraph createThreadHeader(DiscrepancyNoteThread discNoteThread){
String content ="";
int size = discNoteThread.getLinkedNoteList().size();
int counter=0;
for(DiscrepancyNoteBean discBean : discNoteThread.getLinkedNoteList()){
++counter;
content += discBean.getEntityName()+"; "+
RESOLUTION_STATUS_MAP.get(discBean.getResolutionStatusId());
if(size > 1 && counter != size) {
content +=" --->";
}
}
Paragraph para = new Paragraph(content,
new Font(Font.HELVETICA, 16, Font.BOLD, new Color(0, 0, 0)));
return para;
}
private Table createTableFromBean(DiscrepancyNoteBean discBean) throws
BadElementException {
Table table = new Table(2);
table.setTableFitsPage(true);
table.setCellsFitPage(true);
table.setBorderWidth(1);
table.setBorderColor(new java.awt.Color(0, 0, 0));
table.setPadding(4);
table.setSpacing(4);
Cell cell = new Cell("Discrepancy note id: "+discBean.getId());
cell.setHeader(true);
cell.setColspan(2);
table.addCell(cell);
table.endHeaders();
cell = new Cell("Subject name: "+discBean.getSubjectName());
table.addCell(cell);
cell = new Cell("CRF name: "+discBean.getCrfName());
table.addCell(cell);
cell = new Cell("Description: "+discBean.getDescription());
table.addCell(cell);
if(discBean.getDisType() != null) {
cell = new Cell("Discrepancy note type: "+discBean.getDisType().getName());
table.addCell(cell);
}
cell = new Cell("Event name: "+discBean.getEventName());
table.addCell(cell);
cell = new Cell("Parent note ID: "+(discBean.getParentDnId()>0? discBean.getParentDnId():""));
table.addCell(cell);
cell = new Cell("Resolution status: "+
new DiscrepancyNoteUtil().getResolutionStatusName(discBean.getResolutionStatusId()));
table.addCell(cell);
cell = new Cell("Detailed notes: "+discBean.getDetailedNotes());
table.addCell(cell);
cell = new Cell("Entity name: "+discBean.getEntityName());
table.addCell(cell);
cell = new Cell("Entity value: "+discBean.getEntityValue());
table.addCell(cell);
cell = new Cell("Date updated: "+discBean.getUpdatedDateString());
table.addCell(cell);
cell = new Cell("Study ID: "+discBean.getStudyId());
table.addCell(cell);
return table;
}
}