More uno reports

I’ve been playing around more with openoffice.org’s uno api for making reports. Since I’ll be making more updates I’ll just post a link to the Google Code site

http://code.google.com/p/student-worker-relational-database/source/browse/#svn/trunk/ecwsp/uno_report

My latest improvement is supporting tables. It’s probably best to just show what it does.

Now a user could just download the report, change fonts, layout, etc, and reupload it. It does have it’s limitations and is a work in progress but it’s already pretty cool. As a developer I can just define what variables can be used and let someone else make the report (and change it around later). To use it you just have to make the proper data structures, so really this could be ported to any data driven application, not just Django.

Django and OpenOffice.org uno reports

Openoffice.org’s python uno library is great for report writing in a web application. I wanted to create a report template system where users can create templates with a word processor of their choice. I created a simple template model which consists of a CharField and FileField. Users can upload templates here in .doc, .odt, or any format OpenOffice.org supports.

Next I created some generic template report functions.


from django.http import HttpResponse
from django.core.servers.basehttp import FileWrapper

import uno
import os
import string
import tempfile

def findandreplace(document, search, find, replace):
"""This function searches and replaces. Create search, call function findFirst, and finally replace what we found."""
#What to search for
search.SearchString = unicode(find)
#search.SearchCaseSensitive = True
#search.SearchWords = True
found = document.findFirst( search )
if found:
print 'Found %s' % find
while found:
found.String = string.replace( found.String, unicode(find),unicode(replace))
found = document.findNext( found.End, search)

def replace_report(infile, outfile, data):
"""Replace words in a file use like this
data={}
data['$TEST']='worked yay'
returns a django HttpResponse of the file"""
# Boring uno stuff
local = uno.getComponentContext()
resolver = local.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local)
context = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
desktop = context.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", context)

# open document
document = desktop.loadComponentFromURL("file://" + str(infile) ,"_blank", 0, ())
cursor = document.Text.createTextCursor()

search = document.createSearchDescriptor()
#Do a loop of the data and replace the content.
for find,replace in data.items():
findandreplace(document,search,unicode(find),unicode(replace))
print find,replace

# create temporariy file to store document in
tmp = tempfile.NamedTemporaryFile()
document.storeToURL("file://" + str(tmp.name), ())
document.dispose()

# create http response out of temporariy file.
wrapper = FileWrapper(file(tmp.name))
response = HttpResponse(wrapper, content_type='application/odt')
response['Content-Length'] = os.path.getsize(tmp.name)
response['Content-Disposition'] = 'attachment; filename=' + outfile

return response

In this example I’m replacing text in a file. The result is returned as a http response. I can use it like this


template = Template.objects.get_or_create(name="My Selected Template")[0]
data={}
data['$date'] = str(date.today())
return replace_report(template.file.path, "file.odt", data)

In this case I am taking out my template and replacing all instances of the word “$date” with the current date. I choose to use $ to mark it as a variable but it could really be anything. Now when a user wants to change a font they can do it themselves.

Django models from non Django applications

I have an existing LAMP application that I want to write some quick code for. I want to easily manipulate the database without taking the time to understand how the application works fully and mess around with PHP. The solution, create a Django model to act as a wrapper for the sections of the database I need.

First I created some very simple views in MySQL to my new Django created database. I could have just put both programs in the same database but this should make updates easier. Remember in MySQL views are updatable only when the view references one table. All my views look like
create otherdb.my_view as select * from newdb.some_table;
With this method I can easily drop and replace the other database when I update it.

Next I run Django’s inspectdb command to get some generic Django model code. Edit this to one’s liking. For me that meant renaming fields, deleting the primary key (so Django automatically makes it. Only works if it is in fact a primary key auto incr integer). Turn foreign keys into foreign key fields. Make dates into DateFields.

Setup my Django admin interface and all the sudden I can use the spiffy Django admin interface on a completely different application. YAY!

However I ran into a small problem. I’ll post this separately for people who only care about Creating a custom Django DateTimeField

Introducing SWORD

Student WOrker Relational Database (SWORD) is a student worker database for use in schools with work study programs. It’s the project I’ve been working on for the past 6 months or so.

http://code.google.com/p/student-worker-relational-database/

It’s a great example of leveraging Django to create a full website in very little time (Until recently it was just a side project I worked on occasionally) . I’m releasing it open source to help distribute it for use in other schools. Right now the installation process is a little ugly though. I’ll have to learn how to make Debian  and RPM packages sooner or later. Next steps will be to integrate it with some other programs, possibly Schooltool and  SugarCRM. It’s already working with CAS for Single Sign On. If you know any schools that might be able to use this please comment or email. I may be offering it as a hosted solution soon.

Django and Google Maps

Last post I wrote about extending the Django Admin interface by adding a little dynamic data to the help_text field. For more advanced things you need to customize the html widget itself.

One thing I want to do is have a Django model automatically get a Google Map of the address I have stored and save this file. For my purposes though I don’t want this to be automatic, I’d rather an admin user check the map first then decide to overwrite any previous map file. I’ll extend the a widget to get this effect.

First I need a preview box. I want to insert some extra html, I’m actually going to put this into a checkbox form, you will see why later. I need to override the render def on the widget to add some code.


class MapImageWidget(forms.CheckboxInput):
def render(self, name, value, attrs=None):
output = []
output.append(super(MapImageWidget, self).render(name, value, attrs))
output.append("If checked, the above map file will be overwritten with Google Maps.<table><tr><td><a href="javascript:get_map()">Preview Google Maps</a></td></tr><tr><td> </td></tr></table>")

return mark_safe(u''.join(output))

I’m using Google Maps static maps url to get my previews. I made a javascript function called get_map to look up the url based on the address field on my model. I want it to actually use the currently typed in address rather than some ajax solution which may get the currently saved address.


function get_map(){
var address = document.getElementById("id_address").value;
var city = document.getElementById("id_city").value;
var state = document.getElementById("id_state").value;
var zip = document.getElementById("id_zip").value;
parent.mapframe.location="http://maps.google.com/maps/api/staticmap?sensor=false&size=400x400&markers=size:mid|color:red|" + address + "," + city + "," + state + "," + zip;
}

I put the javascript into my change_form.html admin template. This function is only for previewing by the user and has nothing to do with actually saving my map image. For that I have a checkbox as in this screen shot

Maps preview in admin edit page

When the user checks off the box, Django will know to get that image and set it to the map ImageField. I do this by overriding the save function on my model.


def save(self, *args, **kwargs):
if (self.use_google_maps):
self.use_google_maps = False;
image = urllib.urlretrieve("http://maps.google.com/maps/api/staticmap? sensor=false&size=400x400&markers=size:mid|color:red|" +
self.address + "," + self.city + "," + self.state + "," + self.zip)
self.map.save(self.company_name + "_map.jpg", File(open(image[0])))
super(Company, self).save(*args, **kwargs)

I use urllib to “wget” the file. This process will replace the previous practice of manually looking up maps, saving them, and uploading them. Of course if I trusted Google maps (or my addresses to be correct) I could just have it dynamically get that image when needed instead of storing the image file.

Django Hack: adding extra data to admin interface

A common need I have for Django’s admin interface is to show a little more data for convenience right on the edit page. For example showing a link to a foreign key’s edit page right there. The way I do this is by setting the help_text field in the render_change_form function. I create a new function in my admin class to override render_change_form

class whateverAdmin(admin.modelAdmin):
 def render_change_form(self, request, context, *args, **kwargs):
  context['adminform'].form.fields['someField'].help_text = "Go to edit page " + str(context['original'].anyFunction()) + " (will discard changes)"
  return super(whateverAdmin, self).render_change_form(request, context, args, kwargs)

the anyFunction() is just a function I made to display a URL in my model. Notice the allow_tags line to allow the function to return the html <a> tag

def anyFunction(self):
 try:
  field = self.someField
  urlRes = urlresolvers.reverse('admin:appName_someField_change', args=(field.id,))
  return '</a><a href="http://example.com' + urlRes + '">' + str(field) + '</a>'
 except:
 return ""
anyFunction.allow_tags = True

This link is then very convenient when using the admin interface just to look up information. The render_change_from function is also useful to editing about the admin page. I use it to modify queryset’s for foreign key data as well.

Django

I mentioned the Django app I made in the previous post so I thought I would provide some info about what it is. Essentially the goal was to reduce duplicate work everywhere possible be moving data from spreadsheets and other database’s into one central database. Also to allow a non skilled worker to edit this data, then allow for reporting to remake all the excel sheets that were originally needed. Here’s my setup

Ubuntu 9.04 server running Django, MySQL, and Apache (LAMD?) Data models are defined in Django which automatically makes an Admin interface with some customization options.

data entry

Django also makes short work of authentication with is done against Active Directory. Reporting is done with PyExcelerator and pyRTF to make downloadable excel and rtf documents.

reporting

It’s a pretty basic database but it really saves a lot of time compared to maintaining lots of xls documents and mail merges. Also it allows a technical worker to import exported data from other database into MySQL. Ideally this program could also be linked with other MySQL backend programs. So say I want to use SugarCRM I could symlink the contacts table so both Sugar and Django use the same one for perfect 2-way syncing. The real beauty of this is that it was so quick to develop. This is just been a side project for me. Doing it in PHP or .NET would have easily taken 3 times as long.