I haven’t written one of these in … um, in a while. So here’s what I’ve been doing and thinking about:
We deployed TaskTracker. I’m very happy with how this went; we ended up deploying about a week later than we intended to, but that extra week let us test and fix a lot of important bugs and inconsistencies. In the weeks (is it a month already?) since TaskTracker was deployed I’ve been fixing little bugs that I knew about but didn’t have a chance to look at before deployment and bugs that users have reported, including at least one fairly critical one (no unicode support). There are quite a few bugs remaining, but of the ones I’m aware of, none are critical, and most are user-facing bugs which will be easier to solve later, when we tear down and redo the XHTML, CSS and Javascript in an intelligent and consistent way, rather than trying to search through and patch up the existing code.
I’m very happy with the deployment process, but that doesn’t mean I’m very happy with the product; I have a few major complaints which I want to work on addressing as I have time. I created a diagram illustrating some of my complaints:
The two big things to note on this diagram are the relative size and position of the Tasks bubble and the external clients trying to reach TaskTracker: their number, their success, and the routes they take.
TaskTracker currently feels to me like it’s giving, at best, equal emphasis to tasklists and tasks, at worst greater emphasis on tasklists: tasks belong exclusively to single tasklists; tasks store information about their siblings in a tasklist; tasks are viewed and created within the context of a tasklist. This feels (and has always felt) completely wrong to me.
Tasks, after all, are what TaskTracker tracks. Tasks are what you want to get done. Tasklists are just a potentially convenient way of grouping tasks together. But they’re only one way of grouping tasks. Groupings of existing tasks by owner, by creator, by status — by any arbitrary field or set of fields — can be just as useful, as can overlapping tasklists. Just about every tracking product I know of features this; it’s a good idea, even if (and I’m not sure this is true) it might make it harder to provide a clean and simple UI. In addition, the fact that tasks can have an unlimited number of generations of subtasks makes the task/tasklist distinction curiously arbitrary. When a task has fifteen subtasks, in what sense is it not a tasklist? When you just want to throw up a task or two that you need to get done during the day, why do you need an enclosing tasklist; why can’t the tasks just stand on their own?
The only tricky difference between a task and a tasklist is security — and even that isn’t entirely clear-cut since tasks can be private and transfer their privacy to their descendants — and I’m sure we can come up with a coherent way to do security without firm tasklists. Every other piece of information that lives in a tasklist more properly belongs either in tasks (custom statuses) or in any arbitrary view (displayed fields — which sort of includes custom statuses too, since we do have a distinction between “status” and “resolution” even if the distinction is less obvious, and makes more sense, than in trac).
My other gripe is, fundamentally, that we designed TaskTracker explicitly for OpenPlans.org instead of as a standalone web service which is used on the OpenPlans website, a recent realization which I’ll discuss further at the end of this post . This means that OpenPlans-specific concepts and logic have bled all over TaskTracker where they don’t belong and where they artificially restrict TaskTracker’s functionality.
For example, note that in my diagram TaskTracker currently only exposes its information in one way, through its human-readable AJAX web interface; if any other client tries to get information out of, or issue commands to, TaskTracker, it just can’t do it. This isn’t a major problem: it’s just a matter of finding use cases, adding interfaces, and refactoring, but I’ll be much happier with TaskTracker when we address it.
As another example, TaskTracker is very project-centric: tasklists belong to projects, and a lot of critical and decentralized security rules and other logic care about projects. This is crucial for integration with OpenPlans.org, since you don’t want to be able to go to http://www.openplans.org/projects/bob-project/tasks/joe-tasklist if joe-tasklist isn’t affiliated with bob-project. But it’s entirely non-crucial for TaskTracker, since projects mean absolutely nothing to TaskTracker, and it will almost certainly cause headaches when we add views for “all my tasks” or when we add “calls to action.”
Anyway, after deploying TaskTracker, I started working on the new OpenPlans UI, AKA nui. First Jeff and I were working on account screens: login, join, and resetting password. My first serious work in Zope was a lot less painful than I had feared; I think this is largely because we were writing views, which, broadly, make sense to me after using Pylons for eight months to write my first-ever webapp, instead of using skins, which I vaguely understand how to manipulate but which, even so, make no sense to me at all. Jeff and I got most of the basic stuff working by the end of the iteration, though we left a lot of minor pieces, testing, and cleanup undone, and we didn’t get around to our lowest-priority task, the project contents screen.
For the iteration after that, Jeff was working on the aforementioned minor pieces and cleanup and I was working on the project contents screen. Jeff’s piece in particular turned out to be a lot harder than we expected, as he’s described in his recent blog post. Mine, meanwhile, wasn’t too bad, although I spent almost half the iteration just digging around trying to figure out how to fetch, delete and rename objects, which are needed for the project contents screen.
Once I got this information, though, everything went remarkably smoothly, thanks to the multi-part form handling system that Nick and I worked out while I was digging around the code for a week. The contents page allows you to perform several operations on either a single object or an arbitrary set of objects, and it should work both asynchronously and synchronously without Javascript. We both wanted to make our method for solving this problem as general as we could: the contents page might add more actions or item types in the future, and a good solution to this problem could probably be imported into TaskTracker to make its AJAX live-edit multipart forms much more elegant. The system we developed contains three parts: a fairly specific markup/form convention for the form, a little bit of Javascript, and a server-side function, octopus_form_handler, which decorates a form handling function to parse the request according to the convention and return an appropriate response for both AJAX and standard requests.
So, this worked really well, and we were able to make a project contents page containing all wiki pages, file attachments and images, and mailing lists with very little hassle. The mockup also called for a list of the project’s tasklists, though. There were two ways to do this: by exposing a widget in TaskTracker to be transcluded into the contents page; or by querying TaskTracker for tasklist data, building a widget in opencore, and sending user requests to opencore which then makes internal requests to TaskTracker.
Initially I thought I should take the second approach, mostly as an excuse to give TaskTracker the RESTful interface I’ve been wanting to give it. After considerable discussion, though, I decided to take the first approach, for two reasons: one, for the second approach to work TaskTracker would have to send, and opencore would have to understand, data about whether the current user has permission to update and delete each tasklist, which sounded ugly and silly; and two, this widget would be useful outside of the opencore.nui project contents page — in TaskTracker itself, for example, or as an embeddable widget on unaffiliated websites.
On Friday, then, I ported our multi-form system to TaskTracker — a very simple process of writing Myghty templates out of the ZPTs, including the exact same javascript file, and changing a few lines in octopus_form_handler to reflect Pylons’ request and redirect syntax — and in about two hours (more than half of which was spent debugging my Myghty) I had a tasklist-managing widget that was structured and worked exactly the same way as the ones I had in opencore.nui for wiki pages, attachments, and mailing lists. I’m really quite thrilled with this success, because it suggests that the system Nick and I developed really *is* generally useful; the inconsistent request and redirect syntax between Zope and Pylons is annoying, but even so I’m wondering if it’s worth extracting all the relevant Javascript and Python code and putting them in their own package somewhere outside of opencore and TaskTracker instead of duplicating the code. On the other hand, it’s only about thirty lines.
Anyway, now, on Monday, I just have to figure out how to transclude that widget (where, exactly, does Transcluder go in our stack, and in what form?) and the project contents page will be complete, except for some minor issues: elegant error handling, for example; possibly pagination; and only exposing the action buttons to users who are permitted to take those actions.
While thinking about my dissatisfaction with TaskTracker and about transluding TaskTracker widgets into nui, I changed my mind completely about the opencore naming issue. That is, is “opencore” the Stuff We Do In Zope, or is “opencore” the Entire Codebase That Runs OpenPlans.org? When the question was first raised, I thought it should be the latter, since the name implies centrality and we’re trying to move away from a Zope-centric architecture.
What hadn’t really sunk in at the time was that the products we develop (Listen, Wicked, TaskTracker, etc) are all really intended to stand alone. I suspect I missed this major point when developing TaskTracker (which, as I said, I think is a root cause of many of my complaints about that product) in large part because I had an outsider’s perspective, working as I was on a Zope-external product whose use case was to be integrated with the OpenPlans website; on the other hand, the fact that all of our Zope-internal products happen to run on the same Zope instance also made it much harder for me to realize this.
But the point is that — at least according to my recently-adopted personal nomenclature, if not the official one, which I’m still unclear of — “opencore” is neither the Stuff We Do In Zope *or* the Entire Codebase That Runs OpenPlans.org: it’s the software that _integrates_ all of our various standalone products and presents them, unified and consistent, on the OpenPlans website. This makes sense: opencore is the core of OpenPlans.org and any other website that uses it; it’s what makes OpenPlans.org exist and be usable; but it’s *not* most of the things that OpenPlans.org provides: those are independent products and services, which may or may not be initiatives of The Open Planning Project, but which are *not* initiatives of OpenPlans. I get this, and it makes a huge amount of sense to me, so I hope it’s right.
So can I get my separate tasktracker trac instance now?