Improve the task editing panes, swap the backend from an md file to a yaml file, add column editing functionality
This commit is contained in:
parent
b19fe10a0d
commit
779ddca11f
@ -1,18 +0,0 @@
|
||||
#
|
||||
|
||||
## To Do
|
||||
|
||||
|
||||
- Update the task information
|
||||
- Task 2
|
||||
|
||||
## Review
|
||||
|
||||
|
||||
- Task 3
|
||||
|
||||
## Done
|
||||
|
||||
|
||||
- Task 4
|
||||
|
80
src/board.py
80
src/board.py
@ -1,5 +1,20 @@
|
||||
""" This module contains classes and functions to contain the kanban board information """
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
class Task:
|
||||
""" This class represents each task,
|
||||
"""
|
||||
def __init__(self, summary, score, description):
|
||||
""" Initialize the task class
|
||||
"""
|
||||
# Each task has the following properties
|
||||
self.summary = summary # Summary of the task
|
||||
self.score = score # Score for ticket
|
||||
self.description = description # Description of ticket
|
||||
|
||||
|
||||
class Board:
|
||||
def __init__(self, file = None):
|
||||
""" Initialize the Board class, this class has three important class variables.
|
||||
@ -13,59 +28,30 @@ class Board:
|
||||
self.columns = list()
|
||||
self.tasks = list()
|
||||
|
||||
self.file = ''
|
||||
self.file = file
|
||||
|
||||
if file:
|
||||
self.parse_md(file)
|
||||
self.read_yaml(file)
|
||||
|
||||
|
||||
def parse_md(self, file):
|
||||
""" Upon starting the code we need to parse the markdown file which contains our board
|
||||
information
|
||||
def read_yaml(self, file='.board.yaml'):
|
||||
""" Read the yaml file in and set up the data
|
||||
|
||||
Arguments:
|
||||
file - the path to the markdown file containing the board information
|
||||
Arguments:
|
||||
file - yaml file to read in
|
||||
"""
|
||||
|
||||
with open(file,'r') as f:
|
||||
for line in f:
|
||||
item_type = line.split(' ')[0]
|
||||
# Assign sprint
|
||||
if item_type == '#':
|
||||
# If sprint has already been defined we should exit the loop
|
||||
if self.sprint:
|
||||
break
|
||||
|
||||
# Otherwise assign it
|
||||
try:
|
||||
self.sprint = ' '.join(line.split(' ')[1:])
|
||||
# Read in the data
|
||||
with open(file, 'r') as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
except IndexError:
|
||||
# If a sprint title is not defined we default it to ' ' which we process
|
||||
# later
|
||||
self.sprint=' '
|
||||
|
||||
# Define a new column and add a list to the tasks variable that corresponds to that
|
||||
# column
|
||||
elif item_type == "##":
|
||||
self.columns.append(' '.join(line.split(' ')[1:]))
|
||||
self.tasks.append(list())
|
||||
# Assign the data to board variables
|
||||
self.columns = data['columns']
|
||||
self.tasks = [[] for col in self.columns]
|
||||
for task in data['tasks']:
|
||||
self.tasks[self.columns.index(task['column'])].append(
|
||||
Task(task['summary'], task['score'], task['description']))
|
||||
|
||||
# Now add the task to the list structures
|
||||
elif item_type == "-":
|
||||
# The tasks are a list of [col index, task name]
|
||||
self.tasks[-1].append(' '.join(line.split(' ')[1:]).strip())
|
||||
|
||||
|
||||
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))
|
||||
f.write('\n')
|
||||
|
||||
|
||||
def move_task(self, col_index, task_index, direction):
|
||||
@ -108,11 +94,11 @@ class Board:
|
||||
""" Return a task based on column and task index"""
|
||||
return self.tasks[icol][itask]
|
||||
|
||||
def update_task( self, icol, itask, text):
|
||||
def update_task( self, icol, itask, task):
|
||||
""" Update the task based on text """
|
||||
self.tasks[icol][itask] = text
|
||||
self.tasks[icol][itask] = task
|
||||
|
||||
def add_task( self, icol, text):
|
||||
def add_task( self, icol, task):
|
||||
"""Add a task to icol"""
|
||||
self.tasks[icol].append(text)
|
||||
self.tasks[icol].append(task)
|
||||
|
||||
|
@ -6,13 +6,19 @@ Screen {
|
||||
background: $bg;
|
||||
}
|
||||
|
||||
EditScreen {
|
||||
EditTaskScreen {
|
||||
align: center middle;
|
||||
overflow-x: hidden;
|
||||
layout: vertical;
|
||||
background: #000000 25%;
|
||||
}
|
||||
|
||||
EditColScreen {
|
||||
align: center middle;
|
||||
overflow-x: hidden;
|
||||
layout: vertical;
|
||||
background: #000000 25%;
|
||||
}
|
||||
.column {
|
||||
width: 1fr;
|
||||
height: 100%;
|
||||
|
118
src/tui.py
118
src/tui.py
@ -3,7 +3,7 @@ from textual.widgets import Static, Label, ListItem, ListView, TextArea, Input
|
||||
from textual.containers import Horizontal, Vertical
|
||||
from textual.screen import Screen
|
||||
from textual.binding import Binding
|
||||
from board import Board
|
||||
from board import Board, Task
|
||||
|
||||
|
||||
class TaskList(ListView):
|
||||
@ -16,20 +16,38 @@ class TaskList(ListView):
|
||||
Binding("j", "cursor_down", "Cursor Down", show=False, priority=True),
|
||||
]
|
||||
|
||||
class EditScreen(Screen):
|
||||
class EditTaskScreen(Screen):
|
||||
"""
|
||||
This is a screen used to edit the name of a task
|
||||
"""
|
||||
CSS="""
|
||||
Label{
|
||||
width:50%;
|
||||
background: #282828;
|
||||
padding: 1;
|
||||
}
|
||||
Input{
|
||||
width:50%;
|
||||
background: #282828;
|
||||
padding: 0 0;
|
||||
border: #ebdbb9;
|
||||
}
|
||||
|
||||
Input:focus{
|
||||
border: #458588;
|
||||
}
|
||||
TextArea{
|
||||
width: 50%;
|
||||
height: 25%;
|
||||
background: #282828;
|
||||
border: #ebdbb9;
|
||||
}
|
||||
TextArea:focus{
|
||||
border: #458588;
|
||||
}
|
||||
"""
|
||||
BINDINGS = [
|
||||
Binding('enter', 'save', 'Save Changes', priority=True)
|
||||
Binding('ctrl+s', 'save', 'Save Changes', priority=True),
|
||||
]
|
||||
def __init__(self,text):
|
||||
"""
|
||||
@ -43,6 +61,60 @@ class EditScreen(Screen):
|
||||
Compose the widgets on the screen, this screen doesn't need dynamic layout changes
|
||||
"""
|
||||
yield Label('Task Name:')
|
||||
yield Input(value=self.text.summary)
|
||||
yield Label('Score:')
|
||||
if self.text.score:
|
||||
yield Input(value=self.text.score)
|
||||
else:
|
||||
yield Input(value="")
|
||||
yield Label('Description:')
|
||||
if self.text.description:
|
||||
yield TextArea(self.text.description, language='markdown')
|
||||
else:
|
||||
yield TextArea(language='markdown')
|
||||
|
||||
|
||||
def action_save(self):
|
||||
query = self.query(selector=Input)
|
||||
self.text.summary = query.nodes[0].value
|
||||
self.text.score = query.nodes[1].value
|
||||
query = self.query(selector=TextArea)
|
||||
self.text.description = query.nodes[0].text
|
||||
self.dismiss(self.text)
|
||||
|
||||
class EditColScreen(Screen):
|
||||
"""
|
||||
This is a screen used to edit the name of a task
|
||||
"""
|
||||
CSS="""
|
||||
Label{
|
||||
width:50%;
|
||||
background: #282828;
|
||||
padding: 1;
|
||||
}
|
||||
Input{
|
||||
width:50%;
|
||||
background: #282828;
|
||||
padding: 0 0;
|
||||
border: #ebdbb9;
|
||||
}
|
||||
"""
|
||||
BINDINGS = [
|
||||
Binding('ctrl+s', 'save', 'Save Changes', priority=True),
|
||||
Binding('enter', 'save', 'Save Changes', priority=True),
|
||||
]
|
||||
def __init__(self,text):
|
||||
"""
|
||||
Initialize the screen
|
||||
"""
|
||||
super().__init__()
|
||||
self.text = text
|
||||
|
||||
def compose(self):
|
||||
"""
|
||||
Compose the widgets on the screen, this screen doesn't need dynamic layout changes
|
||||
"""
|
||||
yield Label('Column Name:')
|
||||
yield Input(value=self.text)
|
||||
|
||||
|
||||
@ -60,6 +132,7 @@ class KanbanForm(App):
|
||||
Binding("L", "move_up", "Focus Next", show=False),
|
||||
Binding("H", "move_down", "Focus Prev", show=False),
|
||||
Binding("e", "edit_task", "Edit Task", show=False,),
|
||||
Binding("r", "edit_column", "Edit Column Name", show=False,),
|
||||
Binding('q', 'exit', "Exit")
|
||||
]
|
||||
|
||||
@ -68,7 +141,7 @@ class KanbanForm(App):
|
||||
Initialization function for form
|
||||
"""
|
||||
# Initialize our board class
|
||||
self.board = Board(file = '.board.md')
|
||||
self.board = Board(file = '.board.yaml')
|
||||
self.cols = list()
|
||||
|
||||
self.col_widgets = list()
|
||||
@ -83,7 +156,7 @@ class KanbanForm(App):
|
||||
with Vertical(classes=col_class):
|
||||
yield Static(col, classes='header')
|
||||
yield TaskList(
|
||||
*[ListItem(Label(task)) for task in self.board.get_tasks()[i]])
|
||||
*[ListItem(Label(task.summary)) for task in self.board.get_tasks()[i]])
|
||||
|
||||
# Now make all TaskLists except the first have no highlights
|
||||
def action_fnext(self):
|
||||
@ -92,7 +165,7 @@ class KanbanForm(App):
|
||||
|
||||
def action_move_up(self):
|
||||
icol, itask = self.get_col_task()
|
||||
text = self.board.get_task(icol, itask)
|
||||
text = self.board.get_task(icol, itask).summary
|
||||
moved = self.board.move_task(icol, itask, 1)
|
||||
if moved:
|
||||
query = self.query(selector=TaskList)
|
||||
@ -109,7 +182,7 @@ class KanbanForm(App):
|
||||
|
||||
def action_move_down(self):
|
||||
icol, itask = self.get_col_task()
|
||||
text = self.board.get_task(icol, itask)
|
||||
text = self.board.get_task(icol, itask).summary
|
||||
moved = self.board.move_task(icol, itask, -1)
|
||||
if moved:
|
||||
query = self.query(selector=TaskList)
|
||||
@ -122,10 +195,25 @@ class KanbanForm(App):
|
||||
def action_edit_task(self):
|
||||
icol, itask = self.get_col_task()
|
||||
task = self.board.get_task(icol, itask)
|
||||
self.push_screen(EditScreen(task), self.update_task)
|
||||
self.push_screen(EditTaskScreen(task), self.update_task)
|
||||
|
||||
def action_new_task(self):
|
||||
self.push_screen(EditScreen(""), self.new_task)
|
||||
self.push_screen(EditTaskScreen(Task(None,None,None)), self.new_task)
|
||||
|
||||
def action_edit_column(self):
|
||||
icol, itask = self.get_col_task()
|
||||
text = self.board.get_columns()[icol]
|
||||
self.push_screen(EditColScreen(text), self.update_col)
|
||||
|
||||
def update_col(self, text):
|
||||
""" Update the column
|
||||
"""
|
||||
icol, itask = self.get_col_task()
|
||||
query = self.query(selector=Static)
|
||||
query = [node for node in query.nodes if str(node) == 'Static()']
|
||||
query[icol].update(text)
|
||||
self.board.get_columns()[icol] = text
|
||||
|
||||
|
||||
def action_exit(self):
|
||||
""" Exit the application """
|
||||
@ -153,21 +241,21 @@ class KanbanForm(App):
|
||||
|
||||
return col_index, task_index
|
||||
|
||||
def update_task(self, text):
|
||||
def update_task(self, task):
|
||||
""" This function gets the text inputted in the edit screen and updates the underlying
|
||||
task and the board class
|
||||
|
||||
"""
|
||||
icol, itask = self.get_col_task()
|
||||
self.focused.highlighted_child.children[0].update(text)
|
||||
self.board.update_task(icol, itask, text)
|
||||
self.focused.highlighted_child.children[0].update(task.summary)
|
||||
self.board.update_task(icol, itask, task)
|
||||
|
||||
def new_task(self, text):
|
||||
def new_task(self, task):
|
||||
""" This function adds a new task to our board
|
||||
"""
|
||||
icol,_ = self.get_col_task()
|
||||
self.focused.mount(ListItem(Label(text)))
|
||||
self.board.add_task(icol, text)
|
||||
self.focused.mount(ListItem(Label(task.summary)))
|
||||
self.board.add_task(icol, task)
|
||||
self.focused.highlighted_child
|
||||
|
||||
# def on_key(self):
|
||||
|
Reference in New Issue
Block a user