Initial version of pykanban allowing for tasks to be read in frm a .md file and for the tasks to be moved between columns

feature/make_board_screen
Alex Selimov 1 year ago
parent daa59af825
commit 9c2449f4b7

@ -1,18 +1,24 @@
""" This module contains classes and functions to contain the kanban board information """ """ This module contains classes and functions to contain the kanban board information """
class Board: class Board:
def __init__(self): def __init__(self, file = None):
""" Initialize the Board class, this class has three important class variables. """ Initialize the Board class, this class has three important class variables.
These are: These are:
self.sprint | str - name of the current sprint self.sprint | str - name of the current sprint
self.columns | list(str) - columns in kanban board self.columns | list(str) - columns in kanban board
self.tasks | list(list(str)) - tasks in each column self.tasks | list(list()) - tasks in each column
""" """
self.sprint = None self.sprint = None
self.columns = list() self.columns = list()
self.tasks = list() self.tasks = list()
self.file = ''
self.file = file
if file:
self.parse_md(file)
def parse_md(self, file): def parse_md(self, file):
""" Upon starting the code we need to parse the markdown file which contains our board """ Upon starting the code we need to parse the markdown file which contains our board
@ -48,11 +54,56 @@ class Board:
# Now add the task to the list structures # Now add the task to the list structures
elif item_type == "-": elif item_type == "-":
self.tasks[-1].append(' '.join(line.split(' ')[1:])) # The tasks are a list of [col index, task name]
self.tasks[-1].append(""+' '.join(line.split(' ')[1:]))
def write_md(self):
with open(self.file, 'w') as f:
f.write('#\n\n')
for i,col in enumerate(self.columns):
f.write('## {}\n\n'.format(col))
for task in self.tasks[i]:
f.write('- {}\n'.format(task[1][1:]))
f.write('\n')
def move_task(self, col_index, task_index, direction):
""" This class method moves tasks between columns by incrementing/decrementing the column
index
Arguments:
col_index - index of the column we are in
task_index - index of the task we are changing in the column
direction - direction to move the task
Returns:
moved - True if a task was moved else false
"""
task = self.tasks[col_index][task_index]
if col_index+direction >= 0 and col_index+direction < len(self.columns):
self.tasks[col_index+direction].append(task)
del self.tasks[col_index][task_index]
return True
else:
return False
def print_board_items(self): def print_board_items(self):
for col, tasks in zip(self.columns, self.tasks): for i, col in enumerate(self.columns):
print(col, tasks) print(col)
print(self.tasks[i])
def get_columns(self):
""" Return columns"""
return self.columns
def get_tasks(self):
""" Return tasks"""
return self.tasks
def get_task(self, icol, itask):
""" Return a task based on column and task index"""
return self.tasks[icol][itask]

@ -0,0 +1,24 @@
Screen {
layout: horizontal;
overflow-x: auto;
}
.column {
width: 1fr;
height: 100%;
padding: 2 2 2 2;
border-right: dashed #458588;
}
.last-column{
width: 1fr;
height: 100%;
padding: 2 2 2 2;
}
.header {
content-align: center top;
color: #458588;
text-style: bold underline;
}

@ -0,0 +1,119 @@
from textual.app import App, ComposeResult
from textual.widgets import Static, Label, ListItem, ListView
from textual.containers import Horizontal, Vertical
from textual.binding import Binding
from board import Board
class TaskList(ListView):
"""
Inherited widget from Listview to use as the kanban board columns
"""
# Keybinds
BINDINGS = [
Binding("k", "cursor_up", "Cursor Up", show=False, priority=True),
Binding("j", "cursor_down", "Cursor Down", show=False, priority=True),
]
class KanbanForm(App):
CSS_PATH = 'layout.tcss'
BINDINGS = [
Binding("l", "fnext", "Focus Next", show=False, priority=True),
Binding("h", "fprev", "Focus Prev", show=False, priority=True),
Binding("L", "move_up", "Focus Next", show=False, priority=True),
Binding("H", "move_down", "Focus Prev", show=False, priority=True),
Binding('q', 'exit', "Exit", priority=True, show=False)
]
def compose(self):
"""
Initialization function for form
"""
# Initialize our board class
self.board = Board(file = '.board.md')
self.cols = list()
self.col_widgets = list()
with Horizontal():
for i,col in enumerate(self.board.get_columns()):
if i < len(self.board.get_columns())-1:
col_class = 'column'
else:
col_class = 'last-column'
with Vertical(classes=col_class):
yield Static(col, classes='header')
yield TaskList(
*[ListItem(Label(task)) for task in self.board.get_tasks()[i]])
# Now make all TaskLists except the first have no highlights
def action_fnext(self):
""" Focus next column"""
self.children[0].focus_next()
def action_move_up(self):
icol, itask = self.get_col_task()
text = self.board.get_task(icol, itask)
moved = self.board.move_task(icol, itask, 1)
if moved:
query = self.query(selector=TaskList)
self.focused.highlighted_child.remove()
query.nodes[icol+1].append(ListItem(Label(text)))
self.focused.action_cursor_down()
self.action_fnext()
self.focused.action_cursor_down()
def action_fprev(self):
""" Focus previous column """
self.children[0].focus_previous()
def action_move_down(self):
icol, itask = self.get_col_task()
text = self.board.get_task(icol, itask)
moved = self.board.move_task(icol, itask, -1)
if moved:
query = self.query(selector=TaskList)
self.focused.highlighted_child.remove()
query.nodes[icol-1].append(ListItem(Label(text)))
self.focused.action_cursor_down()
self.action_fprev()
self.focused.action_cursor_down()
def action_exit(self):
""" Exit the application """
self.exit()
def get_col_task(self):
"""
This function gets the relevant column and task from the Board object for the current
selected item in the tui.
"""
focused_col = self.focused
query = self.query(selector=TaskList)
# First get the column index
for i, child in enumerate(query.nodes):
if focused_col == child:
col_index = i
# Now get the indext of the item in the list
to_move = focused_col.highlighted_child
for i, child in enumerate(focused_col.children):
if to_move == child:
task_index = i
return col_index, task_index
# def on_key(self):
# with open('log','a') as f:
# f.write("{}".format(self.children[0].focus_next))
if __name__ == "__main__":
kb = KanbanForm()
kb.run()