Pie Charts in TurboGears

You might of looked at Ralph Bean’s tutorial on graphs and thought, that’s nice but I’d like something different.  The great thing about ToscaWidgets using the jqPlot library is that pretty much anything you can do in jqPlot, you can do in ToscaWidgets and by extension in TurboGears.  You want different graph types? jqPlot has heaps!

My little application needed a Pie Chart to display the overall status of attributes. There are 6 states an attribute can be in: Up, Alert, Down, Testing, Admin Down and Unknown.  The Pie Chart would show them all with some sort of colour representing the status. For this example I’ve used hard-coded data but you would normally extract it from the database using a TurboGears model and possibly some bucket sorting code.

I’ve divided my code into widget specific, which is found in myapp/widgets/attribute.py and the controller code found unsurprisingly at myapp/controllers/attribute.py  Also note that some versions of ToscaWidgets have a bug which means any jqPlot widget won’t work, version 2.0.4 has the fix for issue 80 that explains briefly the bug.

The widget code looks like this:

from tw2.jqplugins.jqplot import JQPlotWidget
from tw2.jqplugins.jqplot.base import pieRenderer_js
import tw2.core as twc

class AttributeStatusPie(JQPlotWidget):
    """
    Pie Chart of the Attributes' Status """
    id = 'attribute-status-pie'
    resources = JQPlotWidget.resources + [
            pieRenderer_js,
            ]

    options = {
            'seriesColors': [ "#468847", "#F89406", "#B94A48", "#999999", "#3887AD", "#222222"],
            'seriesDefaults' : {
                'renderer': twc.js_symbol('$.jqplot.PieRenderer'),
                },
            'legend': {
                'show': True,
                'location': 'e',
                },
            }

Some important things to note are:

  • resources are the way of pulling in the javascript includes that actually do the work, generally if you have something like a renderer using js_symbol further on, it needs to be listed in the resources too.
  • seriesColors is how you make a specific data item a specific colour, or perhaps change the range of colours.  It’s not required if you use the default set, which is defined in the jqPlot options.
  • The renderer tells jqPlot what sort of graph we want, the line above says we want pie

 

Next the controller needs to be defined:

from myapp.widgets.attribute import AttributeStatusPie

@expose('myapp.templates.widget')
    def statuspie(self):
        data = [[
                ['Up', 20], ['Alert', 7], ['Down', 12], ['Admin Down', 3], ['Testing', 1], ['Unknown', 4],
                ]]
        pie = AttributeStatusPie(data=data)
        return dict(w=pie)

And that is about it, we now have a controller path attributes/statuspie which shows us the pie chart.
My template is basically a bare template with a ${w.display | n} in it to just show the widget for testing.

Pie Chart in Turbogears
Pie Chart in Turbogears

 

Enhanced by Zemanta

Bootstrap – The hidden gem in Turbogears

I’ve been trying to tidy up my Mako templates within my Turbogears 2 project.  As part of that I was looking at some of them that are quickstarted including one which is the About page.

What was curious was there was all this CSS work all done already for you, including icons.  Digging further I found out that one of the many projects Turbogears takes in, is Bootstrap.  It’s website describes Bootstrap as “Sleek, intuitive, and powerful front-end framework for faster and easier web development.”  but what it means to me is a bunch of guys who understand HTML and CSS way better than me have made it easy for me to build a decent web application.

While the framework won’t suit everyone’s tastes, it makes a lot of the formatting decisions so much easier.  The thing is, in all the Turbogears documentation I have read I’ve not heard it mentioned. Not sure why, its pretty awesome (not SQLAlchemy awesome but not many things are).

Enhanced by Zemanta

Pre-selecting ToscaWidgets jqgrid values in TurboGears

My Turbogears project has recently reached an important milestone; one of the back-end processes now runs pretty much continuously and the plugins it use (or at least the ones I can see) are also working.  That means I can turn to the front-end which displays the data the back-end collected.

For some of the data I am using a ToscaWidgets (or TW2) widget called a jqGridWidget which is a very nice jquery device that separates the presentation and data using a json query.  I’ve mentioned previously about how to get a jqGridWidget working but left the pre-filtering out until now.  This meant that my grid showed all the items in the database, not just the ones I wanted to see, but it was a start.

Now this widget displays things called Attributes which are basically children of another model called Hosts. Basically, Attributes are things you want to check or track about a Host.  My widget used to show all Attributes but often on a Host screen, I want to see its hosts only. So, this is how I got my widget to behave; I’m not sure this is THE CORRECT way of doing it, but it does work.

First, in the Hosts controller you need to create the widget and pass along the host_id that the controller has obtained.  I was not able to use the sub-classing trick you see in some TW2 documentation but actually make a widget instance.

class HostsController(BaseController):
    # other stuff here
    class por2(portlets.Portlet):
        title = 'Host Attributes'
        widget = AttributeGrid()
        widget.host_id = host_id

Next, the prepare() method in the Widget needs to get hold of the host_id and put it into the postData list.  I needed to do it before calling super() because the options become one long string in the sub-class.

class AttributeGrid(jqgrid.jqGridWidget):
    def prepare(self, **kw):
        if self.host_id is not None:
            self.options['postData'] = {'hostid': self.host_id}
        super(AttributeGrid, self).prepare()

This means the jqgrid when it asks for its JSON data will include the hostid parameter as well.  We need that method to “see” the host ID so we can filter the database access.

Finally in the JSON method for the Attribute we pick up and filter on the hostid.

    @expose('json')
    @validate(validators={'hostid':validators.Int()})
    def jqsumdata(self, hostid=0, page=1, rows=1, *args, **kw):
        conditions = []
        if hostid > 0:
            conditions.append(model.Attribute.host_id == hostid)
        attributes =model.DBSession.query(model.Attribute).filter(and_(*conditions))

From there you run through the attributes variable and build your JSON reply.

 

Enhanced by Zemanta

jqGridWidget in Turbogears

 

 

Turbogears 2 uses Toscawidgets 2 for a series of very clever widgets and base objects that you can use in your projects.  One I have been playing with is the jqGridWidget which uses jquery to display a grid. The idea with jquery is creating a grid or other object and then using javascript to call back to the webserver to obtain the data which usually comes from a database using the very excellent SQLAlchemy framework.

I found it a bit fiddly to get going and it is unforgiving, you get one thing wrong and you end up with an empty grid but when it works, it works very well.

from tw2.jqplugins.jqgrid import jqGridWidget
from tw2.jqplugins.jqgrid.base import word_wrap_css

class ItemsWidget(jqGridWidget):
    def prepare(self):
        self.resources.append(word_wrap_css)
        super(ItemsWidget, self).prepare()
    options = {
            'pager': 'item-list-pager2',
            'url': '/items/jqgrid',
            'datatype': 'json',
            'colNames': ['Date', 'Type', 'Area', 'Description'],

The code above is a snippet of the widget I use to make the graph. This is the visual side of the graph and shows just the  graph with nothing in it.  You need the datatype and url lines in the options which tell the code the url to get the data.  I have an items controller and within it there is a jqgrid method.  Note the number of columns I have, the data sent by the jqgrid method has to match this.

Deep inside the items controller is a jqgrid method. The idea is to return some rows of data back to the grid in the exact way the grid expects it otherwise you get, yep more empty grids.

   @expose('json')
    def jqgrid(self, page=1, rows=30, sidx=1, soid='asc', _search='false',
            searchOper=u'', searchField=u'', searchString=u'', **kw):

        qry = DBSession.query(Item)
        qry = qry.filter()
        qry = qry.order_by()
        result_count = qry.count()
        rows = int(rows)

        offset = (int(page)-1) * rows
        qry = qry.offset(offset).limit(rows)

        records = [{'id': rw.id,
           'cell': [ rw.created.strftime('%d %b %H:%M:%S'), rw.item_type.display_name), rw.display_name, rw.text()]} for rw in qry]
        total = int(math.ceil(result_count / float(rows)))
        return dict(page=int(page), total=total, records=result_count, rows=records)

The four items in the value for the cell in the dictionary must equal the number defined for the colNames.  I found it was useful to have firebug looking at my gets and another widget based upon SQLAjqGridWidget which is a lot simpler to setup but much less flexible too.

 

Enhanced by Zemanta

First Look at Python

When the programming language Python came out while it seemed like an interesting idea, I didn’t really see the point. I already know Perl, PHP and C (plus a few others) why learn another language? So until recently, I didn’t

Recent frustrations with PHP and how “loose” it is with things like variables made me have another look at Python and what it could do for me.  As just learning a language by itself it pretty boring I decided to write some small programs in the language and used the framework Turbogears to do it.

First of all, learning the basics of python from perl or PHP is dead simple. The idea of what a language looks like is not too different between these three so it was a snap to write simple stuff in python.  So, what does yet-another language give me?

  • Variables must be defined before being used, but assigning them defines them too.  This is a nice compromise between PHP where you can check undefined variables and C where it all must be defined first, or else.
  • try and except. For code that might fail, use try and then catch the error in except.  We’ve all been guilty of using the @ symbol in PHP to supress stupid error messages, this does it correctly.
  • No PHP extract() or compact(). Actually just no extract function is good enough. That function is pure evil.
  • Sqlalchemy – A proper “you look after the database and ill play with objects” database extraction layer thingamy.  I don’t want to play with databases just let me write code and thats what sqlalchemy does well.
  • Testing – Turbogears uses nosetests which does functional and unit testing. I do get some annoying artifacts based upon testing but generally its all good.
  • Python has help() everywhere and pydoc on the command line. Not sure what variable foo can do? Just type “help(foo)” or even stick it in your script.
  • paster! You can run a development site off the command-line using a simple sqlite database. Don’t like it, dump the DB and reinstall it with one command.

It’s not one-way though, there have been some challenges.  Quite likely these are more my deficiencies than the languages.

  • Error messages take up a whole screen and often make it difficult to isolate. It’s not quite 10-screens-full ala Eiffel but sometimes the error messages tell you something is going wrong, but not what. Mess up your object versus database table references and you get something rather obscure.
  • When is an integer not an integer? The answer is, sometimes. Sometimes they work, sometimes they break. It’s probably more to do with what pysnmp is doing with them.
  • Documentation – some is great, some is downright misleading. It comes down to the writer of the documentation for modules. At least it doesn’t have ‘well get round to it’ like some functions in PHP on their website. Documents that say you can directly iterate dictionaries were another dead-end I wandered down. If you are stuck here, add a .items() at the end of it.
  • Deployment. It’s not just a matter of taring the files up and sticking them into /var/www/sitename and you’re done.  There are probably good reasons why, but its just annoying.

Despite the minor problems, python really does give you much much more than PHP and some more than Perl.  For me and what I am intending on writing it is worth learning yet another language.

Enhanced by Zemanta