Mind mapping on the web

March 26th, 2009

Check out the ultimately cool mind mapping web application called Mindmeister. It gives you roughly the same functionality as MindManager (minus visual templates), but enables collaboration and has a quite responsive user interface.

Mindmeister

New feature and bug fixes

March 18th, 2009

The following changes have been deployed in production today.

New Features

  • The user can specify a default phase for a task. The information is used when people are making time entries regarding the task.

Bug Fixes

  • Extra backslashes were appearing in task and project titles and descriptions when items were edited. This should not happen anymore.
  • Removed an ajax related warning message when creating projects or tasks.

Bug fixes

March 14th, 2009

The following fixes have been deployed in production today.

  • Phase selection added to the time entry dialog.
  • Priority and deadline can be left empty when editing project details.

Also, the code should load faster now. If you notice any difference, I’d like to hear about it.

New Release

March 10th, 2009

This release was deployed in production on March 10th, 2009.

New Features

  • When making time entries, you can specify the phase you are working on, if applicable. The phases can be for example:
    • Plan
    • Implement
    • Test
    • Fix
    • Refactor
    • Document
  • Absence requests are shown on the dashboard for admins.

Improved Usability

  • Form validation improved.
  • Tentative absence is shown in the journal with a dashed border.

Security

  • Eliminated XSS (Cross-Site Scripting) possibilities.

New Features

February 4th, 2009

This Tracker version will become available for production use during February 2009.

The new features and functionality

  • Navigation buttons for journal.
  • Time entry creation via my journal is no longer listing the closed tasks.
  • You can now comment on tasks and projects.
    • Comments are shown in task and project detail page in reverse chronological order.
    • Latest 5 comments are shown on the dashboard in reverse chronological order.
  • When removing projects, you can now either drag them to the Archive folder like before, or if the project is empty, just delete it.
  • The time entry form (the one you get by pressing the clock icon) accepts now the regular “1h 30m” format strings instead of the obscure “1.5″ format.
  • Task and project priority mechanism works in a new way.
    • Multiple items can have same priority.
    • Items are listed in priority order in the sub project contents.
  • Project search enables the user to list sub projects and tasks by author, assignee, priority and status.
  • Descriptions and comments support the textile markup.

Bug fixes

  • Inputing an absence entry without choosing a task gives now an error message.
  • IE rendering is at least slightly improved in various views.
  • Duplicate entries on active task list are handled gracefully.

Visual and usability improvements

  • New login screen.
  • Hover tips for image buttons.
  • Task links show the project path in the hover tip.
  • Tabs for report views for choosing the month and user.
  • Rounded corners for tabs and headings on Firefox browsers.
  • New soothing button design for task and project detail views.
  • User can open the project details, search and comments from behind a tab. This saves space and decreases clutter.

Some earlier features I may not have mentioned much anywhere

  • Support for OpenID authentication.
    • The user can specify a trusted OpenID URL, which the user can use for logging into the account.
    • The administrator can disable this feature for the whole system.
  • Support for Yubikey authentication.
    • The user can specify which physical Yubikey token the user can use for logging into Tracker.
    • When logging in, the user inputs their user ID and triggers a one-time password from the Yubikey token.

A one-key USB keyboard

January 21st, 2009

Riddle me this; What is the worlds smallest keyboard, with only one key, which gives you a different result every time you press it? It’s Yubikey from Yubico. Yubikey is an authentication token, which acts as a USB keyboard. You can use the Yubikey to output a one-time password to a password field of a system which supports this.

Check it out, it’s cool.

Yubikey

Automatic regression testing in Django

January 19th, 2009

Django and python make it a breeze to build and run automatic regression tests. In 1-2 days you can easily build 50 test cases which cover the basic functionality of your application, and make sure no view or model has been broken. The test cases can be divided into three groups:

  • Tests for models
  • Tests for views
  • Tests for various utility functions

Here’s how to do it. You start by adding a tests.py file in your application directory. The Django default test runner will execute the runTest() method for every TestCase derived class when you run manage.py test. Here’s an example tests.py. Django has specific test functions to log you in and out of the standard authentication framework, but this example is using it’s own authentication functionality.

from django.test import *
import doctest

class ViewTests(TestCase):
    def runTest(self):

        c = Client()
        # Try to get private content prior to login
        response = c.get('/projects/')
        self.assertRedirects(response, '/login/?next=/projects/')

        # Attempt login using the wrong password
        response = c.post('/login/',
            {'login': 'eki', 'pass': 'wrongpassword'})
        self.assertContains(response,
            'Please check your user ID and password')

        # Use proper credentials
        response = c.post('/login/?next=/projects/',
            {'login': 'eki', 'pass': 'helloworld'})
        self.assertRedirects(response, '/projects/')

        # Now fetch each of the basic pages
        response = c.get('/projects/')
        self.assertContains(response, 'Example project')
        response = c.get('/users/')
        self.assertContains(response, 'Erkki Tapola')

This is the mechanism for testing views. Next, you can add doctests to your models.py. Doctest is a very handy notation for test cases to be executed from the command line python. Essentially you just run a test case on the command line and cut and paste the command and it’s output to your class or function documentation. For example:


def get_base_dir(path):
    """
    >>> get_base_dir('/first/second')
    'first'
    >>> get_base_dir('first/second')
    'second'
    """
    items = string.split(path, '/')
    return items[1]

The Django test runner runs doctests from models.py automatically, but if you want it to execute doctest cases from other files than models.py as well, you can add a TestCase class to tests.py for that purpose:


class ModuleTests(TestCase):
    def runTest(self):
        def mytestmod(m):
            result = doctest.testmod(m)
            print str(result[1]) + ' test cases ran in ' + str(m)
            self.assertEqual(result[0], 0)

        print "\nRunning test cases for individual modules\n"
        print 80*'*'

        for x in [tracker.main.util, tracker.main.projects]:
            mytestmod(x)

        print 80*'*' + "\n"

When you’re finished with your test cases and able to run them all using manage.py test, you can connect your cool new test suite to svn using the pre-commit hook. This way you can make sure that the code changes are verified prior to commit. Here’s what you do on Linux:

  • Go to hooks/ which is under your SVN repository directory and rename the start-commit.tmpl to start-commit
  • Edit the file, and before the last exit 0, add the line:
    • for x in `find . -iname manage.py -exec dirname {} \;`; do cd $x; if ! ./manage.py test; then exit 1; fi; done

I should probably write this in python to make it platform independent. This unix shell command will find any manage.py from the subdirectories of your current location and use it for running tests. If any manage.py returns an error, the commit will be cancelled. This addition will not influence projects which doesn’t contain a manage.py, but if there is one, then the tests must pass.

A similar hooking mechanism exists in cvs and probably most other version control systems.

You can check out the article on Testing Django
applications
for a comprehensive tutorial.

PHP model for automatic SQL and form creation

January 16th, 2009

After using Django and it’s hibernate-like Object-Relational Model (ORM) for a month or so, I decided to try how I could create a similar model mechanism using PHP. Here’s an example which I wanted to keep just under 100 lines for light reading. This example code demonstrates how to:

  • Easily create a structure with various types of fields (though currently it only supports CharField)
  • Generate SQL code for table creation
  • Generate a HTML form for editing the information

When you run the example, it will output the following (note the intentional error in the SQL statement syntax):

CREATE TABLE `person` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`name` VARCHAR(40),
`address` VARCHAR(40),
`phone` VARCHAR(15),
);

<form action=’.’ method=’POST’>
<table>
<tr><th>Name</th><td></td></tr>
<tr><th>Address</th><td></td></tr>
<tr><th>Phone</th><td></td></tr>
</table>
</form>

The code itself is here for you to use in any way you please, as long as you don’t hold me accountable for the results:

download code
run code



<?php

class Field
{
    var $size;
    var $label;
    var $editor;
    var $value;

    function __construct($size = 80, $label = '')
    {
        $this->size = $size;
        $this->label = $label;
    }
}

class CharField extends Field
{
    function __construct($size = 80, $label = '')
    {
        $this->editor = 'input type="text"';
        parent::__construct($size, $label);
    }

    function get_db_type()
    {
        return 'VARCHAR('.$this->size.')';
    }
}

class Model
{
    function get_name()
    {
        return get_class($this);
    }

    function make_create_sql()
    {
        $str = '';
        echo "nCREATE TABLE `",strtolower($this->get_name()),"` (n";
        echo "   `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,n";

        foreach ( get_class_vars($this->get_name()) as $x => $y)
        {
            echo '   `',$x,'` ',$this->$x->get_db_type(),",n";
        }

        echo ");nn";
    }

    function make_form()
    {
        foreach ( get_class_vars($this->get_name()) as $x => $y)
        {
            echo $this->$x->label,': <',$this->$x->editor,' name="',$x,"\">n";
        }
    }

    function make_form_table()
    {
        echo "  <table class=\"mytable\">n";
        foreach ( get_class_vars($this->get_name()) as $x => $y)
        {
            echo '    <tr><th>',$this->$x->label,'</th><td><',
                $this->$x->editor,' name="',$x,'" value="'.$this->$x->value.
                "\"></td></tr>n";
        }
        echo "  </table>n";
    }
}

class Person extends Model
{
    var $name, $address, $phone;

    function __construct()
    {
        $this->name = new CharField(40, 'Name');
        $this->address = new CharField(40, 'Address');
        $this->phone = new CharField(15, 'Phone');
    }
}

$j = new Person();

$j->name->value = "Erkki Tapola";

$j->make_create_sql();

echo "<form action='.' method='POST'>n";
$j->make_form_table();
echo "</form>n";

?>


You can easily add more field types according to the example CharField. Other features also available in Django, but this example doesn’t (yet) implement:

  • Query mechanisms to abstract SQL SELECT.
  • Persistence; save mechanisms to abstract UPDATE and INSERT.
  • Database table relation, fetch rows based on foreign keys and many-to-many relations

In addition, what I would like to see in Django:

  • Methods to update the current database schema with the added and modified field information. (Note: Check out Django-evolution, which is doing exactly this, and it even maintains a history of modifications.

Such a functionality would be safe for added columns and even column truncation and removal, as long as you indicate that this is actually your intent.