| Home | Trees | Indices | Help |
|
|---|
|
|
1 # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2 # vi: set ft=python sts=4 ts=4 sw=4 et:
3 ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
4 #
5 # See COPYING file distributed along with the PyMVPA package for the
6 # copyright and license terms.
7 #
8 ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9 """Creating simple PDF reports using reportlab
10 """
11
12 __docformat__ = 'restructuredtext'
13
14
15 import os
16 from datetime import datetime
17
18 import mvpa
19 from mvpa.base import externals, verbose
20
21 if __debug__:
22 from mvpa.base import debug
23
24 if externals.exists('reportlab', raiseException=True):
25 import reportlab as rl
26 from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image
27 from reportlab.lib.styles import getSampleStyleSheet
28 from reportlab.lib.units import inch
29
30 # Actually current reportlab's Image can't deal directly with .pdf images
31 # Lets use png for now
32 if externals.versions['reportlab'] >= '1112.2':
33 _fig_ext_default = 'pdf'
34 else:
35 _fig_ext_default = 'png'
36
37
38 __all__ = [ 'rl', 'Report', 'escapeXML' ]
42 s = s.replace('&', '&')
43 s = s.replace('<', '<')
44 s = s.replace('>', '>')
45 return s
46
48 """Simple PDF reports using reportlab
49
50 Named report 'report' generates 'report.pdf' and directory 'report/' with
51 images which were requested to be included in the report
52
53 You can attach report to the existing 'verbose' with
54
55 report = Report()
56 verbose.handlers += [report]
57
58 and then all verbose messages present on the screen will also be recorded
59 in the report. Use
60 report.text("string") to add arbitrary text
61 report.xml("<H1>skajdsf</H1>") to add XML snippet
62 or
63 report.figure() to add the current figure to the report.
64 report.figures() to add existing figures to the report, but they
65 might not be properly interleaved with verbose messages if there were
66 any between the creations of the figures.
67
68 Inspired by Andy Connolly
69 """
70
71 - def __init__(self, name='report', title=None, path=None,
72 author=None, style="Normal",
73 fig_ext=None, font='Helvetica',
74 pagesize=None):
75 """Initialize report
76
77 :Parameters:
78 name : string
79 Name of the report
80 title : string or None
81 Title to start the report, if None, name will be used
82 path : string
83 Top directory where named report will be stored. Has to be
84 set now to have correct path for storing image renderings.
85 Default: current directory
86 author : string or None
87 Optional author identity to be printed
88 style : string
89 Default Paragraph to be used. Must be the one of the known
90 to reportlab styles, e.g. Normal
91 fig_ext : string
92 What extension to use for figures by default. If None, a default
93 will be used. Since versions prior 2.2 of reportlab might do not
94 support pdf, 'png' is default for those, 'pdf' otherwise
95 font : string
96 Name of the font to use
97 pagesize : tuple of floats
98 Optional page size if not to be default
99 """
100
101 if pagesize is None:
102 pagesize = rl.rl_config.defaultPageSize
103 self.pagesize = pagesize
104
105 self.name = name
106 self.author = author
107 self.font = font
108 self.title = title
109 if fig_ext is None:
110 self.fig_ext = _fig_ext_default
111 else:
112 self.fig_ext = fig_ext
113
114 if path is None:
115 self._filename = name
116 else:
117 self._filename = os.path.join(path, name)
118
119 self.__nfigures = 0
120
121 try:
122 styles = getSampleStyleSheet()
123 self.style = styles.byName[style]
124 except KeyError:
125 raise ValueError, \
126 "Style %s is not know to reportlab. Known are %s" \
127 % (styles.keys())
128
129 self._story = []
130
131
132 @property
134 """Compose the beginning of the report
135 """
136 date = datetime.today().isoformat(' ')
137
138 owner = 'PyMVPA v. %s' % mvpa.__version__
139 if self.author is not None:
140 owner += ' Author: %s' % self.author
141
142 return [ Spacer(1, 0.8*inch),
143 Paragraph("Generated on " + date, self.style),
144 Paragraph(owner, self.style)] + self.__flowbreak
145
146
151
152
154 """Adding XML string to the report
155 """
156 if __debug__ and not self in debug.handlers:
157 debug("REP", "Adding xml '%s'" % line.strip())
158 if style is None:
159 style = self.style
160 self._story.append(Paragraph(line, style=style))
161
163 """Add a text string to the report
164 """
165 if __debug__ and not self in debug.handlers:
166 debug("REP_", "Adding text '%s'" % line.strip())
167 # we need to convert some of the characters to make it
168 # legal XML
169 line = escapeXML(line)
170 self.xml(line, **kwargs)
171
172 write = text
173 """Just an alias for .text, so we could simply provide report
174 as a handler for verbose
175 """
176
177
178
180 """Add a figure to the report
181
182 :Parameters:
183 fig : None or string or `figure.Figure`
184 Figure to place into report
185 string : treat as a filename
186 Figure : stores it into a file under directory
187 and embedds into the report
188 None : takes the current figure
189 savefig_kwargs : dict
190 Additional keyword arguments to provide savefig with (e.g. dpi)
191 **kwargs
192 Passed to :class:`reportlab.platypus.Image` constructor
193 """
194
195 if externals.exists('pylab', raiseException=True):
196 import pylab as P
197 figure = P.matplotlib.figure
198
199 if fig is None:
200 fig = P.gcf()
201
202 if isinstance(fig, figure.Figure):
203 # Create directory if needed
204 if not (os.path.exists(self._filename) and
205 os.path.isdir(self._filename)):
206 os.makedirs(self._filename)
207
208 # Figure out the name for image
209 self.__nfigures += 1
210 if name is None:
211 name = 'Figure#'
212 name = name.replace('#', str(self.__nfigures))
213
214 # Save image
215 fig_filename = os.path.join(self._filename,
216 '%s.%s' % (name, self.fig_ext))
217 if __debug__ and not self in debug.handlers:
218 debug("REP_", "Saving figure '%s' into %s"
219 % (fig, fig_filename))
220
221 fig.savefig(fig_filename, **savefig_kwargs)
222
223 # adjust fig to the one to be included
224 fig = fig_filename
225
226 if __debug__ and not self in debug.handlers:
227 debug("REP", "Adding figure '%s'" % fig)
228
229 im = Image(fig, **kwargs)
230
231 # If the inherent or provided width/height are too large -- shrink down
232 imsize = (im.drawWidth, im.drawHeight)
233
234 # Reduce the size if necessary so reportlab does not puke later on
235 r = [float(d)/m for d,m in zip(imsize, self.pagesize)]
236 maxr = max(r)
237 if maxr > 1.0:
238 if __debug__ and not self in debug.handlers:
239 debug("REP_", "Shrinking figure by %.3g" % maxr)
240 im.drawWidth /= maxr
241 im.drawHeight /= maxr
242
243 self._story.append(im)
244
245
247 """Adds all present figures at once
248
249 If called twice, it might add the same figure multiple times,
250 so make sure to close all previous figures if you use
251 figures() multiple times
252 """
253 if externals.exists('pylab', raiseException=True):
254 import pylab as P
255 figs = P.matplotlib._pylab_helpers.Gcf.figs
256 if __debug__ and not self in debug.handlers:
257 debug('REP', "Saving all %d present figures" % len(figs))
258 for fid, f in figs.iteritems():
259 self.figure(f.canvas.figure, *args, **kwargs)
260
261 @property
266
268 """Just a marker for the break of the flow
269 """
270 if __debug__ and not self in debug.handlers:
271 debug("REP", "Adding flowbreak")
272
273 self._story.append(self.__flowbreak)
274
275
276 ## def __del__(self):
277 ## """Store report upon deletion
278 ## """
279 ## if __debug__ and not self in debug.handlers:
280 ## debug("REP", "Report is being deleted. Storing")
281 ## self.save()
282
283
285 """Saves PDF
286
287 :Parameters:
288 add_preamble : bool
289 Either to add preamble containing title/date/author information
290 """
291
292 if self.title is None:
293 title = self.name + " report"
294 else:
295 title = self.title
296
297 pageinfo = self.name + " data"
298
299 def myFirstPage(canvas, doc):
300 canvas.saveState()
301 canvas.setFont(self.font, 16)
302 canvas.drawCentredString(self.pagesize[0]/2.0,
303 self.pagesize[1]-108, title)
304 canvas.setFont(self.font, 9)
305 canvas.drawString(inch, 0.75 * inch,
306 "First Page / %s" % pageinfo)
307 canvas.restoreState()
308
309 def myLaterPages(canvas, doc):
310 canvas.saveState()
311 canvas.setFont(self.font, 9)
312 canvas.drawString(inch, 0.75 * inch,
313 "Page %d %s" % (doc.page, pageinfo))
314 canvas.restoreState()
315
316 filename = self._filename + ".pdf"
317 doc = SimpleDocTemplate(filename)
318
319 story = self._story
320 if add_preamble:
321 story = self.__preamble + story
322
323 if __debug__ and not self in debug.handlers:
324 debug("REP", "Saving the report into %s" % filename)
325
326 doc.build(story,
327 onFirstPage=myFirstPage,
328 onLaterPages=myLaterPages)
329
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Apr 23 23:09:46 2012 | http://epydoc.sourceforge.net |