import os
from datetime import datetime
from past.utils import old_div
import PIL
import reportlab
import reportlab.lib.colors as rlcolors
import reportlab.pdfgen.canvas as rlcanvas
import reportlab.platypus as platypus
from reportlab.lib.units import inch
styles = reportlab.lib.styles.getSampleStyleSheet()
HeaderStyle = styles["Heading1"]
ParaStyle = styles["Normal"]
gray = rlcolors.Color(old_div(136., 256), old_div(136., 256),
old_div(136., 256))
colors = [
"#75D44A", "#BF58CB", "#D44B33", "#88C7D4", "#3B3628", "#CE497C", "#72D49B",
"#59345F", "#5C7B37", "#707AC9", "#C78A3B", "#C7BD94", "#CE98B5", "#CECF4F",
"#874236", "#597576"
]
blackColorMap = {
5: 0xFF000000,
7: 0xFF000000,
8: 0xFF000000,
9: 0xFF000000,
13: 0xFF000000,
14: 0xFF000000,
15: 0xFF000000,
16: 0xFF000000,
17: 0xFF000000,
35: 0xFF000000,
53: 0xFF000000
}
[docs]def new_page(Elements):
'''
insert page break
'''
Elements.append(platypus.PageBreak())
[docs]def add_vtable(Elements, table, style, width_list):
'''
add table, where header is in the first column
'''
width = [w * inch for w in width_list]
Elements.append(platypus.Table(table, width, style=style))
[docs]def add_table(Elements, table, style, col_width):
'''
horizontal table, where the header is on the first row
'''
nfields = len(table[0])
if isinstance(col_width, int) or isinstance(col_width, float):
width = nfields * [col_width * inch]
else:
width = [w * inch for w in col_width]
Elements.append(platypus.Table(table, width, style=style))
[docs]def add_and_parse_SMILES(smiles_str):
""" parse SMILES string such that it fits on the page """
smiles_style = ParaStyle
smiles_style.fontSize = 10
length = len(smiles_str)
new_str = ''
prev_idx = 0
max_length = 66
if length <= max_length:
return smiles_str
# output smiles only upto 3 lines; otherwise it gets messy.
elif length > max_length * 3:
return smiles_str[:max_length] + '...'
for i in list(range(0, length, max_length))[1:]:
new_str += smiles_str[prev_idx:i + 1] + ' '
prev_idx = i + 1
if prev_idx < length:
new_str += smiles_str[prev_idx:length]
return platypus.Paragraph(new_str, smiles_style)
[docs]def add_spacer(Elements):
Elements.append(platypus.Spacer(1, 11))
[docs]def get_image(fn, height, width):
with PIL.Image.open(fn) as img_temp:
size = img_temp.size
(sx, sy) = aspectScale(size[0], size[1], height, width)
img = platypus.Image(fn, sx * inch, sy * inch)
return img
[docs]def report_add_ms_logo(Elements):
logo_path = os.path.join(os.environ.get("MMSHARE_EXEC"), "..", "..",
"python", "scripts", "event_analysis_dir",
"schrodinger_ms_logo.png")
logo_img = platypus.Image(logo_path, 1.50 * inch, 0.43 * inch)
logo_img.hAlign = 'RIGHT'
Elements.append(logo_img)
[docs]def report_add_logo(Elements):
logo_path = os.path.join(os.environ.get("MMSHARE_EXEC"), "..", "..",
"python", "scripts", "event_analysis_dir",
"schrodinger_logo.png")
logo_img = platypus.Image(logo_path, 1.605 * inch, 0.225 * inch)
logo_img.hAlign = 'RIGHT'
Elements.append(logo_img)
[docs]def get_pargph(txt, fixed=False, fontsize=11, color='black', hAlign='left'):
style = ParaStyle
style.fontName = 'Courier' if fixed else 'Helvetica'
style.fontSize = fontsize
style.textColor = color
style.hAlign = hAlign
return platypus.Paragraph(txt, style)
[docs]def pargph(Elements,
txt,
fixed=False,
fontsize=11,
color='black',
leading=None):
"""
Add a paragraph to the report
:param list Elements: Report elements to add the paragraph to
:param str txt: The paragraph text
:param int fontsize: The size of the paragraph font
:param str color: Font color
:param leading: Spacing between paragraph lines, the default will be used
if nothing is passed
:type leading: int or None
"""
style = ParaStyle
style.fontName = 'Courier' if fixed else 'Helvetica'
style.fontSize = fontsize
style.textColor = color
if leading: # Set leading if a custom one has been provided
style.leading = leading
para = platypus.Paragraph(txt, style)
Elements.append(para)
[docs]def aspectScale(ix, iy, bx, by):
'''Scale image to fit into box (bx,by) keeping aspect ratio'''
if ix > iy:
# fit to width
scale_factor = old_div(bx, float(ix))
sy = scale_factor * iy
if sy > by:
scale_factor = old_div(by, float(iy))
sx = scale_factor * ix
sy = by
else:
sx = bx
else:
# fit to height
scale_factor = old_div(by, float(iy))
sx = scale_factor * ix
if sx > bx:
scale_factor = old_div(bx, float(ix))
sx = bx
sy = scale_factor * iy
else:
sy = by
return (sx, sy)
[docs]class NumberedCanvas(rlcanvas.Canvas):
[docs] def __init__(self, *args, **kwargs):
rlcanvas.Canvas.__init__(self, *args, **kwargs)
self._saved_page_states = []
[docs] def showPage(self):
self._saved_page_states.append(dict(self.__dict__))
self._startPage()
[docs] def save(self):
"""add page info to each page (page x of y)"""
num_pages = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self.drawFixedContents(num_pages)
rlcanvas.Canvas.showPage(self)
rlcanvas.Canvas.save(self)
[docs] def drawFixedContents(self, page_count):
"""
Draw nonflowables such as footers, headers and fixed graphics
:param int page_count: Total number of pages
"""
self.setFont("Helvetica", 8)
self.drawRightString(8 * inch, 0.3 * inch,
"Page %d of %d" % (self._pageNumber, page_count))
s = "Schrödinger Inc. Report generated " + datetime.now().strftime(
"%m-%d-%Y %H:%M")
self.drawString(0.8 * inch, 0.3 * inch, s)