""" Script to create simple report of the MCE project.
It is used in the :mod:`mce_result_collector`.
"""
# Copyright (c) 2014 MTA Centre for Ecological Research
# Distributed under the GNU GPL v3. For full terms see the file LICENSE.
import os
import time
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.ticker as plticker
import random as rnd
from decimal import Decimal
from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table
from reportlab.platypus.figures import ImageFigure
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.platypus.flowables import PageBreak
from xml.sax.saxutils import escape
class Chart:
""" Base Class of different Charts. """
def __init__(self, title, x_label, y_label, x_vals, y_vals):
""" x_vals and y_vals has to be the same length. """
# Check for Decimals (plot functions don't work with Decimals)
if x_vals is not None:
self.x_vals = [float(x) if type(x) is Decimal else x for x in x_vals]
self.y_vals = [float(y) if type(y) is Decimal else y for y in y_vals]
self.title = title
self.y_label = y_label
self.x_label = x_label
self.grid = True
# Create a figure with size 6 x 6 inches.
self.fig = Figure() #figsize=(6,6)
self.canvas = FigureCanvas(self.fig)
self.ax = self.fig.add_subplot(111)
self.ax.set_title(self.title, fontsize=14)
self.ax.set_xlabel(self.x_label, fontsize=12)
self.ax.set_ylabel(self.y_label, fontsize=12)
self.ax.grid(True, linestyle='-',color='0.75')
class ScatterPlot(Chart):
""" Create a scatterPlot image. """
def __init__(self, title, x_label, y_label, x_vals, y_vals):
""" x_vals and y_vals has to be the same length. """
Chart.__init__(self, title, x_label, y_label, x_vals, y_vals)
def create_image(self, filename):
self.ax.scatter(self.x_vals, self.y_vals, marker=u'+', color='tomato')
self.canvas.print_figure(filename, dpi=500)
class ErrorBar(Chart):
""" Points with error bars. """
def __init__(self, title, x_label, y_label, x_vals, y_vals, y_err):
Chart.__init__(self, title, x_label, y_label, x_vals, y_vals)
self.y_err = y_err
def create_image(self, filename):
self.ax.errorbar(self.x_vals, self.y_vals, yerr=self.y_err, fmt='-')
self.canvas.print_figure(filename, dpi=500)
class BarChart(Chart):
""" Bars. """
def __init__(self, title, x_label, y_label, y_vals):
Chart.__init__(self, title, x_label, y_label, None, y_vals)
def create_image(self, filename):
width = 0.2
xlocs = [float(x) for x in range(len(self.y_vals))] #[x + width for x in range(len(self.y_vals))]
vals = [float(x) for x in self.y_vals]
#[x + 0.2 for x in xlocs], ibd, width, color='#FFFF00'
self.ax.bar(xlocs, vals, width, color='b')
loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
self.ax.xaxis.set_major_locator(loc)
self.canvas.print_figure(filename, dpi=500)
class ReportGenerator:
""" Create a Report template and provide methods to fill it. """
def __init__(self, title, filename):
if isinstance(title, str):
self.title = escape(title)
else:
self.title = ''
for i in title:
self.title += escape(i)+'
'
self.filename = filename
self.section_num = 1
self.doc = SimpleDocTemplate(filename, pagesize=A4,
rightMargin=72,leftMargin=72,
topMargin=72,bottomMargin=72)
self.content = []
self.styles = getSampleStyleSheet()
self.styles.add(ParagraphStyle(name='Justify',
alignment=TA_JUSTIFY,
leading=14))
self.styles.add(ParagraphStyle(name='Center',
alignment=TA_CENTER,
leading=16))
ptext = '%s' % self.title
self.content.append(Paragraph(ptext+'
', self.styles["Center"]))
#ptext = 'Generated: %s' % time.ctime()
#self.content.append(Paragraph(ptext, self.styles["Normal"]))
def html_decode(self, s):
"""
Returns the ASCII decoded version of the given HTML string. This does
NOT remove normal HTML tags like
. """ htmlCodes = [['&', '&'],["'", '''],['"', '"'],['>', '>'],['<', '<']] #htmlCodes = [['&', '&']] for code in htmlCodes: s = s.replace(code[0], code[1]) return s def add_figure(self, filename, caption): """Insert a figure with caption into the report. """ im = ImageFigure(filename, caption) self.content.append(im) def add_spacer(self): self.content.append(Spacer(1,12)) def add_text(self, text, is_url=False): """ Insert a justified paragraph. """ if not is_url: text = self.html_decode(text) ptext = '%s' % text self.content.append(Paragraph(ptext, self.styles["Justify"])) def add_section_title(self, text): """ Insert a section title.""" self.content.append(Spacer(1,12)) text2 = self.html_decode(text) tmp_s = escape(str(self.section_num)+'. '+text2) ptext = '%s' % tmp_s self.content.append(Paragraph(ptext, self.styles["Justify"])) self.content.append(Spacer(1,12)) self.section_num += 1 def add_url_line(self, url, url_text=None, before_text=None, after_text=None): """ Insert a URL link line, with optional text before and after the link. """ text = '' if before_text: text = before_text + ' ' text += '' if url_text: text += url_text else: text += url text += '' if after_text: text += ' ' + after_text self.add_text(text, True) def add_table(self, data): """ Insert Table. data is a list of lists, where sublists are the rows. """ self.content.append(Spacer(1,12)) t = Table(data) self.content.append(t) self.content.append(Spacer(1,12)) def new_page(self): self.content.append(PageBreak()) def save_report(self): self.doc.build(self.content) if __name__ == '__main__': scp = ScatterPlot('Test image', 'x var', 'y var', range(0,10), range(0,10)) scp.create_image('proba_plot.png') rg = ReportGenerator('Test Report', 'proba_report.pdf') rg.add_figure('proba_plot.png', 'This is a test plot') ptext = 'BioVeL is a virtual e-laboratory that \ supports research on biodiversity issues using large \ amounts of data from cross-disciplinary sources. \ BioVeL offers the possibility to use computerised...' rg.add_text(ptext) rg.save_report()