E-Scribe : a programmer’s blog

About Me

PBX I'm Paul Bissex. I build web applications using open source software, especially Django. Started my career doing graphic design for newspapers and magazines in the '90s. Then wrote tech commentary and reviews for Wired, Salon, Chicago Tribune, and others you never heard of. Then I built operations software at a photography school. Then I helped big media serve 40 million pages a day. Then I worked on a translation services API doing millions of dollars of business. Now I'm building the core platform of a global startup accelerator. Feel free to email me.

Book

I co-wrote "Python Web Development with Django". It was the first book to cover the long-awaited Django 1.0. Published by Addison-Wesley and still in print!

Colophon

Built using Django, served with gunicorn and nginx. The database is SQLite. Hosted on a FreeBSD VPS at Johncompanies.com. Comment-spam protection by Akismet.

Pile o'Tags

Stuff I Use

bitbucket, Django, Emacs, FreeBSD, Git, jQuery, LaunchBar, Markdown, Mercurial, OS X, Python, Review Board, S3, SQLite, Sublime Text, Ubuntu Linux

Spam Report

At least 236325 pieces of comment spam killed since 2008, mostly via Akismet.

The twenty-minute trouble ticket system

People are always going on about how it "only" takes twenty minutes or whatever to knock together some little application. Screencasts don't count, unless they are screencasts of somebody who hasn't done any preparation. But damn, I just built a trouble-ticket system in Django in twenty minutes. Really. Of course, I already had a running install, this is just something I added. But it was instantly useful. I assigned four tickets to myself and now I can go have dinner.

It leverages the users you already have installed to assign and attribute tickets, and uses your list of installed apps to categorize them.

This is the whole thing, a models.py file. It requires the magic-removal branch of Django.

Updated: adopted Luke's suggestion from the comments, and made it sort by status codes (which are now numeric, so they sort correctly). But I swear all that only took me, like, eight minutes!
Updated again: A newer, slightly better version of this now lives on at Django Snippets.
from django.db import models
from django.contrib.auth.models import User
from YOUR_PROJECT import settings

STATUS_CODES = (
    (1, 'Open'),
    (2, 'Working'),
    (3, 'Closed'),
    )

PRIORITY_CODES = (
    (1, 'Now'),
    (2, 'Soon'),
    (3, 'Someday'),
    )

PROJECTS = list(enumerate(settings.INSTALLED_APPS))

class Ticket(models.Model):
    """Trouble tickets"""
    title = models.CharField(maxlength=100)
    project = models.CharField(blank=True, maxlength=100, choices=PROJECTS)
    submitted_date = models.DateField(auto_now_add=True)
    modified_date = models.DateField(auto_now=True)
    submitter = models.ForeignKey(User, related_name="submitter")
    assigned_to = models.ForeignKey(User)
    description = models.TextField(blank=True)
    status = models.IntegerField(default=1, choices=STATUS_CODES)
    priority = models.IntegerField(default=1, choices=PRIORITY_CODES)

    class Admin:
        list_display = ('title', 'priority', 'status', 'submitted_date', 'submitter', 
            'submitted_date', 'modified_date')
        search_fields = ('title', 'description',)

    class Meta:
        ordering = ('status', 'priority', 'submitted_date', 'title')

    def __str__(self):
        return self.title

Friday, March 31st, 2006
+ + +
11 comments

Comment from Luke Plant , later that day

Nice work - if I hadn't already got trac set up (which python-hosting make really easy to do) I would probably have pinched this!

You can make it a little prettier by changing this:

APPLICATIONS = zip(range(len(settings.INSTALLED_APPS)), settings.INSTALLED_APPS

to this:

APPLICATIONS=list(enumerate(settings.INSTALLED_APPS))

Comment from Tony , later that day

This is really neat, I particularly like the idea of grabbing all the applications from the settings file - have you posted it on the django-users google group?

Cheers, Tone

Comment from Paul , later that day

Luke -- thanks for the enumerate tip, I like that, and changed the code accordingly.

Tony -- yeah, it's the leveraging of the data already in the project and the admin that makes it so satisfying. I haven't broadcast it, but this category on my blog does get picked up by the Django aggregator.

I've fixed a couple dumb usability things (e.g. not sorting by status) and posted new code above.

Comment from Tony , later that day

Ah, just seen it come through on the aggregator.

It's the sort of thing that would be a neat screencast - except what would it be....

% python manage.py startapp tracker
% python manage.py syncdb
% python manage.py runserver

(go into TextMate), paste text, save (server restarts)

(go into Safari), http://127.0.0.1:8000/admin/tracker

Have I missed anything? ;)

Cheers, Tone

Comment from John , 3 weeks later

Don't forget to add YOUR_PROJECT.tracker to INSTALLED_APPS in your settings.py

And the URL I got was http://127.0.0.1:8000/admin/tracker/ticket

Way cool though!

Comment from Paul , 3 weeks later

Thanks. I have to admit it's actually a bit broken due to the admin's one-column sorting restriction. I guess that just proves my point that nobody really does these things in 20 minutes!

Comment from striker17 , 10 months later

"itÂ’s actually a bit broken"

even so could you go through it line by line and explain what's happening?

Comment from Paul , 10 months later

My "broken" comment was specifically related to sorting -- the admin can't sort by, e.g. priority and status.

As far a line-by-line, I unfortunately don't have time for that at the moment but I will comment on the only really interesting line:

PROJECTS = list(enumerate(settings.INSTALLED_APPS))

This generates the choice-list for the "project" field dynamically. The Python function enumerate returns a generator that produces (index, item) pairs for each item in a sequence; the list call turns that into a static list of pairs, which is what the Django model choices param wants.

The rest is fairly standard Django fare, as covered in the models API documentation.

I still use this on a couple side projects, actually. It's good for a quick "note to self". I plan on revisiting in the course of a new project this spring or summer.

Thanks for your interest!

Comment from TaMeR , 11 months later

1) How do you reply to a ticket 2) how does a user post a ticket? Via admin panel? 3) How does he/she get a response. 4) How do you limit that the user only sees his/her own ticket?

Comment from Paul , 11 months later

TaMeR: This is really intended for internal use, i.e. communication between staff (admin) users.

Mostly I was just taken with the fact that one could make an actually useful "application" (useful to me, anyway) with only a model.

Comment from Rok , 5 years later

Thx!

Comments are closed for this post. But I welcome questions/comments via email or Twitter.