package org.mobicents.qa.report.sipp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.encoders.ImageEncoder;
import org.jfree.chart.encoders.KeypointPNGEncoderAdapter;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.DefaultXYDataset;
import org.jfree.data.xy.XYDataset;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfWriter;
public class Report {
private static Logger logger = Logger.getLogger(Report.class.getName());
private enum FieldType {
DOUBLE, TIME, DATE, INVALID
};
private static Set<String> selectedCategories = new HashSet<String>();
static {
String[] selected = new String[] { "TargetRate", "CallRate", "IncomingCall", "CurrentCall", "SuccessfulCall", "FailedCall", "FailedMaxUDPRetrans", "FailedUnexpectedMessage", "DeadCallMsgs", "Retransmissions", "ResponseTime1", "ResponseTime1StDev", "CallLength", "CallLengthStDev" };
for (String category : selected) {
selectedCategories.add(category);
selectedCategories.add(category.concat("(C)"));
selectedCategories.add(category.concat("(P)"));
}
}
private static String referenceCategory = "ElapsedTime(C)";
private static boolean allCharts = false;
private static boolean singleFile = false;
private static boolean printCharts = false;
private static boolean bigCharts = false;
private static void printInfo() {
logger.info("Usage: java -jar 'thisFile' [options] [file1 ... fileN]");
logger.info("Usage: If no files are specified, all .csv files in current directory are used");
logger.info("Option: -a - ALL - Generates charts for all the categories");
logger.info("Option: -b - BIG - Generates charts with 1 pixel for each elapsed second");
logger.info("Option: -d - DEBUG - Extra information during program execution");
logger.info("Option: -h - HELP - Shows this info and exits");
logger.info("Option: -p - PRINT - Print chart images in a subfolder");
}
public static void main(String[] args) {
// Setup Log4j
Logger.getRootLogger().addAppender(new ConsoleAppender(new PatternLayout("%c %-5p %m%n")));
logger.setLevel(Level.INFO);
logger.info("Sipp Report Tool starting ... ");
// Search for -d flag
for (String string : args) {
if ("-d".equals(string)) {
logger.setLevel(Level.DEBUG);
logger.debug("Debug level set");
break;
}
}
// Pring arguments
if (logger.isDebugEnabled()) {
logger.debug(Arrays.toString(selectedCategories.toArray()));
String s = "Arguments:_";
for (String string : args) {
s += string + "_|_";
}
logger.debug(s);
}
// Get filenames
Set<String> filenames = new HashSet<String>();
for (String string : args) {
if (string.charAt(0) != '-') {
filenames.add(string);
logger.debug("File to open: " + string);
} else {
if ("-a".equals(string)) {
allCharts = true;
logger.info("All charts set");
continue;
}
if ("-p".equals(string)) {
printCharts = true;
logger.info("Print chart images set");
continue;
}
if ("-b".equals(string)) {
bigCharts = true;
logger.info("Big chart set");
continue;
}
if (("-h".equals(string)) || ("--help".equals(string))) {
printInfo();
return;
}
}
}
if (filenames.isEmpty()) {
try {
URL url = Report.class.getProtectionDomain().getCodeSource().getLocation();
File myDir = new File(URLDecoder.decode(url.getFile(), "UTF-8")).getAbsoluteFile().getParentFile();
File[] files = myDir.listFiles(new FilenameFilter() {
public boolean accept(File file, String s) {
if (s.endsWith(".csv")) {
return true;
} else {
return false;
}
}
});
for (File file : files) {
filenames.add(file.getAbsolutePath());
}
} catch (UnsupportedEncodingException e) { // should not happen
logger.warn("Could not get current dir");
}
}
if (filenames.isEmpty()) {
printInfo();
} else {
if (filenames.size() == 1) {
singleFile = true;
logger.debug("Single file mode - set");
}
// Create the report
for (String string : filenames) {
createReports(string);
}
logger.info("Done. Oh yeah!");
}
}
public static void createReports(String filename) {
try {
// Create a CSV reader
OpenCsvReader csv = new OpenCsvReader(new FileReader(filename), ';', '\"');
// Get categories
String[] categories = csv.readNext();
if (logger.isDebugEnabled()) {
logger.debug("Categories read from CSV: " + Arrays.toString(categories));
}
// Get values
List<String[]> values = csv.readAll();
int rows = values.size();
csv.close();
// Get reference categories (elapsed time)
double[] referenceData = null;
for (int i = 0; i < categories.length; i++) {
if (referenceCategory.equals(categories[i])) {
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
double timeReference = timeFormat.parse(values.get(0)[i]).getTime();
int n = 0;
referenceData = new double[rows];
for (String[] value : values) {
try {
referenceData[n++] = (timeFormat.parse(value[i]).getTime() - timeReference) / 1000;
} catch (ParseException e) {
logger.warn("Unexpeted parse exception for field " + categories[i], e);
}
}
}
}
if (referenceData == null) {
logger.error("Invalid reference category: " + referenceCategory);
return;
}
// convert to categories
Map<String, XYDataset> categoryValues = new LinkedHashMap<String, XYDataset>();
Map<String, FieldType> categoryTypes = new LinkedHashMap<String, FieldType>();
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss:SSS");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
for (int i = 0; i < categories.length; i++) {
if (!selectedCategories.contains(categories[i]) && !allCharts) {
logger.debug("Category " + categories[i] + " does not belong to selected categories. Dropping.");
continue;
}
// check for usable colums
try {
Double.parseDouble(values.get(0)[i]);
categoryTypes.put(categories[i], FieldType.DOUBLE);
logger.debug("Category " + categories[i] + " is of Double type (" + values.get(0)[i] + ")");
} catch (NumberFormatException nfe) {
try {
timeFormat.parse(values.get(0)[i]);
categoryTypes.put(categories[i], FieldType.TIME);
logger.debug("Category " + categories[i] + " is of Time type (" + values.get(0)[i] + ")");
} catch (ParseException pe) {
try {
dateFormat.parse(values.get(0)[i]);
categoryTypes.put(categories[i], FieldType.DATE);
logger.warn("Date format not implemented for field " + categories[i] + " (" + values.get(0)[i] + ")");
continue;
} catch (ParseException e) {
categoryTypes.put(categories[i], FieldType.INVALID);
logger.warn("Column " + categories[i] + " is not in numeric format (" + values.get(0)[i] + "): " + e.getMessage());
continue;
}
}
}
// convert values
DefaultXYDataset dataset = new DefaultXYDataset();
double timeReference = 0;
if (categoryTypes.get(categories[i]) == FieldType.TIME) {
timeReference = timeFormat.parse(values.get(0)[i]).getTime();
}
int n = 0;
double[] valueData = new double[rows];
switch (categoryTypes.get(categories[i])) {
case DOUBLE:
for (String[] value : values) {
valueData[n++] = Double.parseDouble(value[i]);
}
break;
case TIME:
boolean isStDev = categories[i].contains("StDev");
for (String[] value : values) {
try {
double myTimeValue = (timeFormat.parse(value[i]).getTime() - timeReference) / 1000;
if (isStDev && (myTimeValue < 0)) {
valueData[n++] = Double.NaN;
} else {
valueData[n++] = myTimeValue;
}
} catch (ParseException e) {
logger.warn("Unexpeted parse exception for field " + categories[i], e);
}
}
break;
case DATE:
break;
default:
break;
}
dataset.addSeries(categories[i], new double[][] { referenceData, valueData });
categoryValues.put(categories[i], dataset);
}
if (categoryValues.isEmpty()) {
logger.warn("No categories to be written to file.");
return;
}
// print available columns
if (logger.isDebugEnabled()) {
logger.debug("Writting categories: " + Arrays.toString(categoryValues.keySet().toArray()));
}
logger.info("Writting report '" + (singleFile ? "report.pdf" : filename.replaceAll(".csv", ".pdf")) + "' ...");
// Write files (1600 is the default value because it looks prettier in my display)
int referenceSize = bigCharts ? new Double(referenceData[referenceData.length - 1]).intValue() : 1600;
int imageSizeX = referenceSize;
int imageSizeY = 800;
Document document = new Document();
document.setPageSize(new Rectangle(imageSizeX, imageSizeY));
document.setMargins(0, 0, 0, 0);
PdfWriter.getInstance(document, new FileOutputStream(singleFile ? "report.pdf" : filename.replaceAll(".csv", ".pdf")));
document.open();
for (String category : categoryValues.keySet()) {
String title = category.replace("(P)", " (Periodic)").replace("(C)", " (Cumulative)");
String xLabel = referenceCategory.replace("(P)", "").replace("(C)", "") + " (seconds)";
String yLabel = category.replace("(P)", "").replace("(C)", "");
switch (categoryTypes.get(category)) {
case DOUBLE:
if (category.contains("Rate")) {
yLabel += " (call/s)";
title = title.replace("(Cumulative)", "(Average)");
} else if (category.contains("(P)")) {
yLabel += " per second";
}
break;
case TIME:
yLabel += " (seconds)";
title = title.replace("(Cumulative)", "(Average)");
break;
case DATE:
break;
case INVALID:
break;
default:
break;
}
JFreeChart chart = ChartFactory.createXYLineChart(title, xLabel, yLabel, categoryValues.get(category), PlotOrientation.VERTICAL, false, false, false);
BufferedImage image = chart.createBufferedImage(imageSizeX, imageSizeY);
document.add(Image.getInstance(image, null));
logger.debug("Wrote category chart " + category);
if (printCharts) {
String newDirName = singleFile ? "charts" : filename.split("_")[1];
ImageEncoder encoder = new KeypointPNGEncoderAdapter();
new File(newDirName).mkdir();
String chartFile = newDirName + File.separator + category + "Chart.png";
encoder.encode(image, new FileOutputStream(chartFile));
logger.debug("Wrote category chart to file " + category);
}
}
document.close();
} catch (FileNotFoundException e) {
logger.error("Unable to open file: " + filename, e);
} catch (IOException e) {
logger.warn("IOException in csv file: " + filename, e);
} catch (DocumentException e) {
logger.warn("DocumentException: " + e.getMessage(), e);
} catch (ParseException e) {
logger.warn("ParseException: " + e.getMessage(), e);
}
}
}