Skip to content

Kanban App

The kanban Django app provides the core functionality for project and task management.

Models

Models for the Kanban application, including Task and related logic.

Sprint

Bases: Model

This holds information about a sprint

Task

Bases: Model

This holds information about a task

get_due_date_for_working_day(year, month, bank_holidays=None)

Returns the date for the Nth working day (Monday-Friday, excluding bank holidays) starting from the given month and year. If bau_working_day exceeds the number of working days in the month, continues into the next month(s). bau_working_day is 1-based (WD1 = first working day). bank_holidays: optional set/list of datetime.date objects to skip as working days.

Source code in kanban\models.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def get_due_date_for_working_day(self, year, month, bank_holidays=None):
    """
    Returns the date for the Nth working day (Monday-Friday, excluding bank holidays)
    starting from the given month and year.
    If bau_working_day exceeds the number of working days in the month, continues into the next
     month(s).
    bau_working_day is 1-based (WD1 = first working day).
    bank_holidays: optional set/list of datetime.date objects to skip as working days.
    """
    day = 1
    count = 0
    current_year = year
    current_month = month
    try:
        wd_number = int(str(self.bau_working_day).replace("WD", ""))
    except (ValueError, AttributeError):  # Fix: catch only relevant exceptions
        return None
    if bank_holidays is None:
        bank_holidays = set()
    else:
        bank_holidays = set(bank_holidays)
    while True:
        try:
            date = datetime.date(current_year, current_month, day)
        except ValueError:
            # Move to next month
            if current_month == 12:
                current_month = 1
                current_year += 1
            else:
                current_month += 1
            day = 1
            continue
        if date.weekday() < 5 and date not in bank_holidays:  # 0=Monday, 4=Friday
            count += 1
            if count == wd_number:
                return date
        day += 1

get_due_display()

Return the appropriate due value based on task_type.

Source code in kanban\models.py
66
67
68
69
70
def get_due_display(self):
    """Return the appropriate due value based on task_type."""
    if self.task_type.lower() == "bau" and self.bau_working_day:
        return self.bau_working_day
    return self.due_date

list_ordered() classmethod

Return queryset ordered by 'list_order'.

Source code in kanban\models.py
116
117
118
119
@classmethod
def list_ordered(cls):
    """Return queryset ordered by 'list_order'."""
    return cls.objects.order_by("list_order")

ordered() classmethod

Return queryset ordered by 'order'.

Source code in kanban\models.py
111
112
113
114
@classmethod
def ordered(cls):
    """Return queryset ordered by 'order'."""
    return cls.objects.order_by("order")

Views

Views for the Kanban application.

backlog(request)

Render the task list, ordered by list_order.

Source code in kanban\views.py
16
17
18
19
def backlog(request):
    """Render the task list, ordered by list_order."""
    tasks = Task.list_ordered()
    return render(request, "kanban/backlog.html", {"tasks": tasks})

create_task(request)

Create a new task.

Source code in kanban\views.py
22
23
24
25
26
27
28
29
30
31
def create_task(request):
    """Create a new task."""
    if request.method == "POST":
        form = TaskForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("backlog")
    else:
        form = TaskForm()
    return render(request, "kanban/create_task.html", {"form": form})

edit_task(request, pk)

Edit an existing task.

Source code in kanban\views.py
70
71
72
73
74
75
76
77
78
79
80
def edit_task(request, pk):
    """Edit an existing task."""
    task = Task.objects.get(pk=pk)
    if request.method == "POST":
        form = TaskForm(request.POST, instance=task)
        if form.is_valid():
            form.save()
            return redirect("backlog")
    else:
        form = TaskForm(instance=task)
    return render(request, "kanban/edit_task.html", {"form": form, "task": task})

home(request)

Render the home page.

Source code in kanban\views.py
11
12
13
def home(request):
    """Render the home page."""
    return render(request, "kanban/home.html")

kanban_board(request)

Render the Kanban board, grouping tasks by status and ordering by 'order'.

Source code in kanban\views.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def kanban_board(request):
    """Render the Kanban board, grouping tasks by status and ordering by 'order'."""
    statuses = ["todo", "in_progress", "help", "done"]
    tasks_by_status = {status: [] for status in statuses}
    for task in Task.objects.exclude(task_type="story").order_by("order"):
        if task.status in tasks_by_status:
            tasks_by_status[task.status].append(task)
    return render(
        request,
        "kanban/kanban_board.html",
        {
            "tasks_by_status": tasks_by_status,
            "statuses": statuses,
            "task_type": "All Tasks",
        },
    )

reorder_list_tasks(request)

Reorder tasks by 'list_order'.

Source code in kanban\views.py
 98
 99
100
101
102
103
104
105
106
107
108
109
def reorder_list_tasks(request):
    """Reorder tasks by 'list_order'."""
    if request.method == "POST":
        data = json.loads(request.body)
        ordered_ids = data.get("ordered_ids", [])

        # Update the list_order for each task in the new order
        for idx, task_id in enumerate(ordered_ids):
            Task.objects.filter(id=task_id).update(list_order=idx)

        return JsonResponse({"success": True})
    return JsonResponse({"success": False}, status=400)

reorder_tasks(request)

Reorder tasks by 'order' and update their status.

Source code in kanban\views.py
83
84
85
86
87
88
89
90
91
92
93
94
95
def reorder_tasks(request):
    """Reorder tasks by 'order' and update their status."""
    if request.method == "POST":
        data = json.loads(request.body)
        status = data.get("status")
        ordered_ids = data.get("ordered_ids", [])

        # Update the order and status for each task in the new order
        for idx, task_id in enumerate(ordered_ids):
            Task.objects.filter(id=task_id).update(order=idx, status=status)

        return JsonResponse({"success": True})
    return JsonResponse({"success": False}, status=400)

story_board(request)

Render the story board, grouping tasks by status and ordering by 'order'.

Source code in kanban\views.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def story_board(request):
    """Render the story board, grouping tasks by status and ordering by 'order'."""
    statuses = ["todo", "in_progress", "help", "done"]
    tasks_by_status = {status: [] for status in statuses}
    for task in Task.objects.filter(task_type="story").order_by("order"):
        if task.status in tasks_by_status:
            tasks_by_status[task.status].append(task)
    return render(
        request,
        "kanban/kanban_board.html",
        {
            "tasks_by_status": tasks_by_status,
            "statuses": statuses,
            "task_type": "All Stories",
        },
    )

Templates

  • kanban_board.html: Kanban board UI.
  • task_list.html: Task list table.
  • create_task.html: Task creation form.
  • edit_task.html: Task editing form.
  • sidebar.html: Navigation sidebar.

Static

  • colors.css: Color scheme.
  • JS/CSS for AG Grid, Bootstrap, SortableJS.