Added “getrows” Feature for additionalbands

I added a new feature, getrows, for use with additionalbands. Just declare a band like this:

1
2
3
Band([
    # ... elements ...
], getrows = rowfunction)

where rowfunction (or whatever you’ve decided to call it) is defined like this:

1
2
3
4
def rowfunction(row):
    return [
        # ... row objects, i.e. dicts or lists ...
    ]

If getrows is supplied, it will be called each time the Band is generated, receiving the current data source row as its only parameter.  The row function must return a sequence of row-like objects, i.e. dicts or lists; the Band will then be generated once for each of these rows, instead of being generated with the parent data source row.  This allows a kind of light-weight subreporting.  Because these are additionalbands, they are not attached to the parent band and can flow from page to page automatically.

PollyReports Additional Bands

I’ve added a new feature to the 1.7.6 version of PollyReports: additionalbands.  A Band instance may have one or more additionalbands defined (as a list of Band instances), and each will be rendered in order after the parent band renders.

Conceptually, this is much like the existing childbands feature; however, childbands become “part” of the parent band, rendering on the same page (and potentially forcing a page break before the parent band).  additionalbands are separate from the parent band, and from each other.

I added the feature to deal with a report that needed a two page long report footer.  So that’s what it’s for.

additionalbands render as part of the detailband, in groupheaders and groupfooters, and in the reportfooter.  They are not rendered as part of a pageheader or pagefooter band.  Also note that additionalbands will render childbands of their own.

Remember, you can get PollyReports using pip or by cloning the Github repo at:

http://github.com/Solomoriah/PollyReports

Orphaned Header Bands “Mostly” Fixed

Today I added a handful of lines of code to calculate the average detail band height (so far) and use that average to reduce the number of times group header bands are “orphaned” at the bottom of a page.  I’ll admit, I’m thinking strongly that I really need to use a figure between the average and maximum, but for now I’m going with just the average.  Of course, if all detail bands in a given report are the same height, it won’t matter anyway.

I’m using PollyReports in a production system currently; in fact, that’s how I came to realize this was needed.  After a bit of live testing, I may modify the algorithm a bit more.  Right now I just want to see how much difference it makes.

PollyReports vs. Geraldo Reports — A Correction

Some time back I made a post about the development of PollyReports, and I gave code line counts based on Robin Parmar’s lines-of-code counter which ascribed a truly huge number of lines to Geraldo.  While I knew it was more complex than PollyReports, I began to feel that there had to be some mistake… it just couldn’t be THAT big.

So I took Robin’s program apart and rewrote it, keeping his (or is it her?) line counting mechanism intact but altering the traversal scheme so that only *.py files would be counted, and so that they would be listed in a fashion similar to the Unix/Linux du command.  Using the current 1.5.1 version of PollyReports, the module itself weighs in at 262 actual code lines, 388 total lines (including comments and doc strings).  Using the version of Geraldo that I have downloaded, the total count for source files (excepting the effectively empty tests folder) is 1,785 actual lines of code, 4885 total lines including comments and doc strings.  I’m pretty sure that the code I abstracted from Robin’s script is not good in all cases; the docstring detector will not detect all docstrings, and may be confused by some literal string assignments (basically if you put three double quotes on a line by themselves, you’ll confuse it).  However, these counts do seem more reasonable.

Geraldo is almost 7 times the size of PollyReports, still pretty big, but not over 340 times as I originally reported it.  I think Robin’s code may have been tallying the documentation files as well as the actual Python code.

More updates to PollyReports

Made my first backwards-incompatible change today; instead of a right = … value in the Element initializer, I’m using align = … for a more general solution.  Before, your choices were right = 0 (the default) for left-aligned text, and right = 1 for right-aligned text.  But I needed something centered, and so I looked into the Reportlab docs and found drawCentredString(); to use it, I had to change the parameter, obviously.  While I was in there, I discovered drawAlignedString(), which is really cool, so I went ahead and added it to PollyReports also.

align may be set to “left” (the default), “right”, “center” (or “centre”, I’m not picky), or “align” to get any of these modes of alignment.

I guess that’s the only “real” change in version 1.4; it’s uploaded to PyPI and github, as usual.

http://pypi.python.org/pypi/PollyReports

https://github.com/Solomoriah/PollyReports

Minor PollyReports update

I found an issue with the ordering and printing of group headers and footers, and I fixed that; the current release 1.3 is now correct, as far as I know.  Also, I’ve revised the code to act intelligently when no detail band is defined, since every once in a while, it makes sense to omit it.

The documentation on PyPI has been updated to reflect these changes:

http://packages.python.org/PollyReports/

Hm.  Guess that’s all I had to say.

PollyReports Tutorial

I’ve noticed that acceptance of a new software module or package for developers in the Open Source/Free Software world is greatly affected by the availability of a good tutorial. I mean, it seems obvious, doesn’t it? But I’ve also noticed that the original author of a project rarely writes a good tutorial.

EDIT 6/20/2012: I’ve moved the tutorial to PyPI; find it here:

http://packages.python.org/PollyReports/tutorial.html

What do they say about battle plans?

So, after posting that PollyReports was ready for use, I actually used it last night with a small report for one of my clients.  Turns out, it still needed work.

But now, it works.  There were a couple of things I had just plain forgotten, like… what if there are newlines in an Element’s text?  Answer: break up the text into lines and print them one after the other in vertical alignment, using the given font size and leading to space them out.  What about page numbers?  Well, oops.  I’ve added a sysvar parameter to Element initialization that can be used to access any of the parent Report’s variables.  All I really want is “Report.pagenumber” but I can see that there may well be other uses for this.

Though this particular report didn’t use it, I have other client’s reports that used Geraldo’s event system (mainly so the user wouldn’t decide a slow-generating report was borked).  Rather than add all those event hooks to PollyReports, I added just one: an onrender parameter added to Element, which is automatically passed to the Renderer when it’s instantiated.  When Renderer.render is called (i.e. when the data is actually output), onrender is called with a single parameter, a reference to the Renderer.  Assuming you called that parameter “obj”, the Element which spawned the Renderer is accessible as obj.parent, and the Report as obj.parent.report.

Making progress…

Wow, PollyReports.py is already usable!

I started on PollyReports yesterday morning, and as of right now, it’s usable.  It’s true, PollyReports lacks some functionality from Geraldo Reports, but as I said in my post yesterday, that was the plan.  Keep it simple, Stanley, or something to that effect.

Using Robin Parmar’s lines-of-code counter found here, I’ve counted the code lines in both projects.  Geraldo Reports consists of 90,138 lines of code (in my current fork, which is pretty close to the standard in terms of length), while PollyReports has just 1,345 lines.  These are the “minimal” numbers, with comments and blank lines ignored, and they include all the Python files in each of the respective directories.  This includes the sample data file for PollyReports… which is 1002 code-lines long.

The actual PollyReports.py is 382 lines long, including comments and blank lines!

I’m pretty proud of Polly.  She’s managed to mature nicely while keeping her girlish figure.  I’m sure, as time goes by, she’ll gain a little more weight, but hopefully she’ll never get close to the mass of Geraldo.

Okay, enough silliness.  What is still missing?  Two things come to mind:

1.  A means of adding fonts other than the standard PDF fonts.  Geraldo Reports handled this internally… but Polly doesn’t “know” you are using Reportlab, nor import any parts of it directly.  Therefore, if you want nonstandard fonts, register them with Reportlab before you pass your canvas to PollyReports.  You’ll be able to call on those fonts using whatever names you have registered, just as normal when using Reportlab directly.

What does this buy me?  The ability to use a wrapper and run PollyReports with something other than Reportlab.  The less of Reportlab’s API the wrapper has to replicate, the easier it will be.  Here’s the whole list of Canvas methods and attributes PollyReports uses:

canvas.drawRightString()
canvas.drawString()
canvas.line()
canvas._pagesize
canvas.restoreState()
canvas.saveState()
canvas.setFont()
canvas.setLineWidth()
canvas.setStrokeGray()
canvas.showPage()
canvas.translate()

There’s just no need to add anything to that list, other than perhaps the rect() method at some point (for a Box class, no doubt).

2.  Subreports.  I can’t think of a clean way to handle subreports, since there must be some way to retrieve the external recordset.  Perhaps an Element subclass where you register a callback to get the data?  Hmm.  Might do it just that way.

Anyway, I’m very pleased with this project.  I expect to be using PollyReports for several of my custom software clients very soon.

Are you interested in PollyReports?  Let me know!