This week I finished up the initial release of Django-Reporter, my first open-source project based on work I've done for my full-time employer, Pegasus News. At Pegasus we send daily, weekly, and monthly email reports out to several people. We have a quite complex codebase, so we need these reports to be as flexible as possible.
Limitations of the old way
Previously, we were creating one-off executable report scripts and collecting them in a directory on the main web server to be hit by cron periodically. This involved a lot of boilerplate code and duplication, and became difficult to manage long-term - especially when we switched from a single-site to a multi-site structure and a lot of the reports broke. Also, we had to include different
DJANGO_SETTINGS_MODULE values depending on the site the report was for, and that filled up the crontab with a lot of verbosity.
Classes, registration, and a management command
My solution was to make the reports class-based, so that we could implement certain methods on each report that handled what makes those reports different while letting the base class handle what stays the same. We subclass the
BaseReport class, implement methods for the subject line and default recipients, and then implement a method that pulls our data and sets it up in a nested list for a CSV writer to take care of.
I first kept all of our reports in a subdirectory of the app, and had the management command walk that directory to find the reports. When I wanted to decouple the reports app, however, this became an issue. I first considered adding a setting that would define where the reports lived, but this meant that I'd have to stick the reports in a directory inside an app that had nothing to do with the reports. Instead, I decided to go with a registration model similar to Django's admin interface or Haystack's search indexes. This allows me to split the reports up and put them in a reports.py file inside the app that they are reporting on.
To make the reports easy to run and automatically resolve the
DJANGO_SETTINGS_MODULE issue, I made a management command that would handle the invocation of the reports. It takes the desired report frequency, optional date and recipient overrides, and establishes the report class with that information. The default behavior is to take the data, convert it to csv, and email it to the desired recipients. The command also provides options to save the report to a file, or print it to stdout.
At Pegasus, we're working on some great new features and on overhauling some of the more challenging areas of our codebase. As a result, I don't have a lot of time to work on the reports app even though I think it has a lot of potential to do more. I also think that this is something that could benefit the Django community because there are a limited number of report apps available, none of which focus on automatically emailing reports.
As a commercial venture, open-sourcing provides a great opportunity for Pegasus because we get to benefit from contributions to the app from talented developers who have the time and interest in making the tool better. More importantly, however, as an open-source consumer this gives us the opportunity to give back to the community that's given us so much. I hope you find the project useful, and I certainly welcome patches or forks to make it better!