Skip to content
ari edited this page May 11, 2011 · 9 revisions

Task queues is our response to question “What should I do next?”

Every task is given a ‘score’.

Task score will be recalculated each night across the whole system, and as a trigger on saving each task. Also we can create trigger to recalculate score when task is created/updated.

Nil score

Any tasks which is is not resolution ‘open’ or is snoozed will be given a nil score.

Triggering changes

Score calculation is performed on every task when it is changed for any reason (including by having a trigger change it). It is also performed on all open tasks every night by a cron job, since many score factors change daily by age.

Schema

Add two fields to [tasks] table:

  • score
  • score_adjustment

User interface: Panel

  • Widget on the right side of the task list view which shows task names.

= next =
Some task
Another task
A third task
A 4th task
One more
[more]

  • Each element is link to task, clicking on it opens the task by ajax
  • First task is in larger font than the rest
  • you can drag a task into a different order, which adjusts the score_adjustment and score
  • 5 tasks are displayed. Only show tasks which are assigned to logged in user

The algorithm

Basic design goals

  • cannot rely on Ruby code (security holes, too hard for users to adjust)
  • flexible parameters, definable per company
  • criteria based on task attributes and custom attributes
  • think about linear, exponential and other scaling factors

Schema

[score_rules]
id
name
score
type (enum)
exponent (default is 1)
controlled_by

The options for the type enum are:

  • task_age (days)
  • last_public_comment_age (days)
  • overdue (days)
  • fixed

Controlled_by is polymorphic relation to one of:

  • client (or maybe SLA?)
  • project
  • company
  • property_value

All fields must be not null.

Formula

For any given task, all score_rule records are found which join via controlled_by to the task’s clients, property_value or project. Also all score_rules linked to the current company are loaded. Then each of these records are iterated through:

def calculate_weight(task) {
  if (!task.open) return nil
  if (task.snooze_for_customer) return nil
  if (task.snooze_until_date > today) return nil

  score = task.score_adjustment

  sr_list = find all score_rules records joined to the task in some way

  foreach sr_list as s {
    if (s.type == "fixed")
      score = score + s.score
    if (s.type == "task_age")
      score = score + (s.weight * (task_age ^ s.exponent))
    if (s.type == "last_comment_age")
      score = score + (s.weight * (last_comment_age ^ s.exponent))
  }
  return score
}

User interface: formula entry

Company preferences

Within the company preferences screen it will be possible to add one or more score_rule lines. Each line will have the following:

[ Name: about 30 chars ]
[ type menu ] [ score int ] [ exponent ]

We also need the explanatory text:

Each line you add below will apply to the score of every task. Larger scores make those tasks more important. Use negative scores to decrease the importance of tasks. The exponent designates how quickly the score ramps up as the age increases; normal values would be between 1.2 and 2.5.

Type menu options:

  • Fixed score
  • Score per day since creation
  • Score per day since last public comment
  • Score per day overdue

Project

The same user interface, except the text reads:

Each line you add below will apply to the score of every task attached to this project. Larger scores make those tasks more important. Use negative scores to decrease the importance of tasks. The exponent designates how quickly the score ramps up as the age increases; normal values would be between 1.2 and 2.5.

Client

Property value