From 6ed4e669ef7cb857c7b0ac774c41e8f9c7758217 Mon Sep 17 00:00:00 2001 From: Joris Date: Fri, 8 May 2020 19:35:08 +0200 Subject: Add duration to tasks --- src/db/init.py | 1 + src/db/tasks.py | 54 +++++++++++++++++++++++++++++++++----------- src/gui/tasks/form/state.py | 46 ++++++++++++++++++++++++++----------- src/gui/tasks/form/widget.py | 33 +++++++++++---------------- src/gui/tasks/table/menu.py | 8 +++---- src/gui/tasks/table/model.py | 20 ++++++++++++---- src/gui/tasks/widget.py | 8 +++---- src/model/task.py | 4 +++- 8 files changed, 115 insertions(+), 59 deletions(-) diff --git a/src/db/init.py b/src/db/init.py index 9517714..c1835eb 100644 --- a/src/db/init.py +++ b/src/db/init.py @@ -12,6 +12,7 @@ def init(path): " created_at INTEGER NOT NULL," " modified_at INTEGER NOT NULL," " name TEXT NOT NULL," + " duration INTEGER," " tag TEXT" " )") database.commit() diff --git a/src/db/tasks.py b/src/db/tasks.py index 26a430a..d03877b 100644 --- a/src/db/tasks.py +++ b/src/db/tasks.py @@ -1,10 +1,18 @@ from sqlite3 import Cursor import time -from model.task import Task, TaskForm +from model.task import Task, ValidTaskForm def get(cursor: Cursor) -> Task: - cursor.execute('SELECT id, created_at, modified_at, name, tag FROM tasks') + cursor.execute( + " SELECT" + " id," + " created_at," + " modified_at," + " name," + " duration," + " tag" + " FROM tasks") res = [] @@ -14,37 +22,57 @@ def get(cursor: Cursor) -> Task: created_at = task[1], modified_at = task[2], name = task[3], - tag = task[4] + duration = task[4], + tag = task[5] )) return res -def insert(cursor: Cursor, taskForm): +def insert(cursor: Cursor, form: ValidTaskForm): now = int(time.time()) - cursor.execute('INSERT INTO tasks(created_at, modified_at, name, tag) VALUES (?, ?, ?, ?)', (now, now, taskForm.name, taskForm.tag)) + cursor.execute( + " INSERT INTO tasks(" + " created_at," + " modified_at," + " name," + " duration," + " tag" + " ) VALUES (?, ?, ?, ?, ?)", + (now, now, form.name, form.duration, form.tag)) + return Task( id = cursor.lastrowid, created_at = now, modified_at = now, - name = taskForm.name, - tag = taskForm.tag + name = form.name, + duration = form.duration, + tag = form.tag ) -def update(cursor: Cursor, task: Task, taskForm: TaskForm): +def update(cursor: Cursor, task: Task, form: ValidTaskForm): now = int(time.time()) cursor.execute( - 'UPDATE tasks SET modified_at = ?, name = ?, tag = ? WHERE id = ?', - (now, taskForm.name, taskForm.tag, task.id)) + " UPDATE tasks" + " SET" + " modified_at = ?," + " name = ?," + " duration = ?," + " tag = ?" + " WHERE id = ?", + (now, form.name, form.duration, form.tag, task.id)) return Task( id = task.id, created_at = task.created_at, modified_at = now, - name = taskForm.name, - tag = taskForm.tag + name = form.name, + duration = form.duration, + tag = form.tag ) def delete(cursor: Cursor, ids): if len(ids) >= 1: - cursor.execute('DELETE FROM tasks WHERE id IN (%s)' % ','.join('?'*len(ids)), ids) + cursor.execute( + 'DELETE FROM tasks WHERE id IN (%s)' % ','.join('?'*len(ids)), + ids) diff --git a/src/gui/tasks/form/state.py b/src/gui/tasks/form/state.py index 6aedb95..a47aec7 100644 --- a/src/gui/tasks/form/state.py +++ b/src/gui/tasks/form/state.py @@ -1,39 +1,59 @@ from PyQt5 import QtCore +from typing import NamedTuple, Optional -from model.task import TaskForm +from model.task import ValidTaskForm class TaskFormEdition: - def __init__(self, name, name_signal, tag, tag_signal): + def __init__(self, name, name_signal, duration, duration_signal, tag, tag_signal): self._name = name + self._duration = duration self._tag = tag - self._signal = TaskFormSignal() + self._signal = ValidTaskFormSignal() name_signal.connect(lambda name: self.on_name_signal(name)) + duration_signal.connect(lambda duration: self.on_duration_signal(duration)) tag_signal.connect(lambda tag: self.on_tag_signal(tag)) - def get(self): - return TaskForm( - name = self._name, - tag = self._tag) + def get(self) -> Optional[ValidTaskForm]: + name = self._name.strip() + duration = self._duration.strip() + tag = self._tag.strip() + + if name and (duration == '' or duration.isdigit()): + return ValidTaskForm( + name = name, + duration = 0 if duration == '' else int(duration), + tag = tag) + else: + return None def on_name_signal(self, name: str): self._name = name - self._signal.emit(self.get()) + self.emit() + + def on_duration_signal(self, duration: str): + self._duration = duration + self.emit() def on_tag_signal(self, tag: str): self._tag = tag - self._signal.emit(self.get()) + self.emit() + + def emit(self): + validForm = self.get() + if validForm: + self._signal.emit(validForm) def signal(self): return self._signal -class TaskFormSignal(QtCore.QObject): - _signal = QtCore.pyqtSignal(TaskForm, name = 'taskForm') +class ValidTaskFormSignal(QtCore.QObject): + _signal = QtCore.pyqtSignal(ValidTaskForm, name = 'validTaskForm') def __init__(self): QtCore.QObject.__init__(self) - def emit(self, taskForm): - self._signal.emit(taskForm) + def emit(self, form: Optional[ValidTaskForm]): + self._signal.emit(form) def connect(self, f): self._signal.connect(f) diff --git a/src/gui/tasks/form/widget.py b/src/gui/tasks/form/widget.py index 61fa24d..8063d8d 100644 --- a/src/gui/tasks/form/widget.py +++ b/src/gui/tasks/form/widget.py @@ -1,7 +1,8 @@ from PyQt5 import QtWidgets +from typing import Optional import db.tasks -from model.task import Task, TaskForm +from model.task import Task, ValidTaskForm import gui.icons import gui.tasks.form.state @@ -21,6 +22,10 @@ def widget( (name_labelled_input, name_input) = labelled_input(widget, 'Name', init_name) layout.addWidget(name_labelled_input) + init_duration = str(task.duration) if task is not None else '' + (duration_labelled_input, duration_input) = labelled_input(widget, 'Duration', init_duration) + layout.addWidget(duration_labelled_input) + init_tag = task.tag if task is not None else '' (tag_labelled_input, tag_input) = labelled_input(widget, 'Tag', init_tag) layout.addWidget(tag_labelled_input) @@ -28,6 +33,8 @@ def widget( task_form_edition = gui.tasks.form.state.TaskFormEdition( init_name, name_input.textChanged, + init_duration, + duration_input.textChanged, init_tag, tag_input.textChanged) @@ -40,7 +47,6 @@ def widget( return widget -# Use grid ? def labelled_input(parent, label: str, default_value: str): widget = QtWidgets.QWidget(parent) @@ -67,8 +73,8 @@ def buttons(parent, action_title, task_form_signal, on_validate, on_cancel): validate.clicked.connect(on_validate); layout.addWidget(validate) - def on_task_form_signal(task_form): - if validate_form(task_form): + def on_task_form_signal(form: Optional[ValidTaskForm]): + if form: validate.setEnabled(True) else: validate.setDisabled(True) @@ -82,19 +88,6 @@ def buttons(parent, action_title, task_form_signal, on_validate, on_cancel): return widget -def validate(task_form: TaskForm, on_validated): - valid_form = validate_form(task_form) - if valid_form: - on_validated(valid_form) - -def clean_form(task_form: TaskForm): - return TaskForm( - name = task_form.name.strip(), - tag = task_form.tag.strip()) - -def validate_form(task_form: TaskForm): - task_form = clean_form(task_form) - if task_form.name: - return task_form - else: - return None +def validate(form: Optional[ValidTaskForm], on_validated): + if form: + on_validated(form) diff --git a/src/gui/tasks/table/menu.py b/src/gui/tasks/table/menu.py index 4366c25..f89ec92 100644 --- a/src/gui/tasks/table/menu.py +++ b/src/gui/tasks/table/menu.py @@ -2,7 +2,7 @@ from PyQt5 import QtWidgets import db.tasks import gui.tasks.modal -from model.task import Task, TaskForm +from model.task import Task, ValidTaskForm def open(database, table, update_task_signal, position): rows = set([index.row() for index in table.selectedIndexes()]) @@ -34,10 +34,10 @@ def show_update_dialog(database, parent_widget, update_task_signal, row, task): 'Modify a task', 'modify', task, - lambda taskForm: on_update(database, update_task_signal, row, task, taskForm)) + lambda form: on_update(database, update_task_signal, row, task, form)) dialog.exec_() -def on_update(database, update_task_signal, row, task: Task, taskForm: TaskForm): - task = db.tasks.update(database.cursor(), task, taskForm) +def on_update(database, update_task_signal, row, task: Task, form: ValidTaskForm): + task = db.tasks.update(database.cursor(), task, form) update_task_signal.emit(row, task) database.commit() diff --git a/src/gui/tasks/table/model.py b/src/gui/tasks/table/model.py index 90bcc4c..6b8133d 100644 --- a/src/gui/tasks/table/model.py +++ b/src/gui/tasks/table/model.py @@ -7,9 +7,9 @@ import math import util.array import util.range -columns = 3 +columns = 4 -headers = ['Age', 'Name', 'Tag'] +headers = ['Age', 'Name', 'Duration', 'Tag'] default_sort = (0, Qt.AscendingOrder) @@ -31,9 +31,11 @@ class TableModel(QtCore.QAbstractTableModel): task = self._tasks[index.row()] if index.column() == 0: return age_since(task.created_at) - if index.column() == 1: + elif index.column() == 1: return task.name - if index.column() == 2: + elif index.column() == 2: + return pp_duration(task.duration) + elif index.column() == 3: return task.tag else: return QtCore.QVariant() @@ -101,12 +103,22 @@ def age_since(timestamp): else: return '1m' +def pp_duration(minutes: int): + if minutes >= 60 * 24: + return '' + str(math.floor(minutes / 60 / 24)) + 'd' + elif minutes >= 60: + return '' + str(math.floor(minutes / 60)) + 'h' + else: + return '' + str(minutes) + 'm' + def sort_key(task: Task, row: int): if row == 0: return task.created_at elif row == 1: return task.name.lower() elif row == 2: + return task.duration + elif row == 3: return task.tag.lower() def is_reversed(row: int, order: Qt.SortOrder) -> bool: diff --git a/src/gui/tasks/widget.py b/src/gui/tasks/widget.py index bca6585..6fa8bf0 100644 --- a/src/gui/tasks/widget.py +++ b/src/gui/tasks/widget.py @@ -4,7 +4,7 @@ import db.tasks import gui.tasks.signal import gui.tasks.table.widget import gui.icons -from model.task import TaskForm +from model.task import ValidTaskForm def widget(database, parent): widget = QtWidgets.QWidget(parent) @@ -30,10 +30,10 @@ def show_add_dialog(database, parent_widget, add_task_signal): 'Add a task', 'add', None, - lambda taskForm: on_add(database, taskForm, add_task_signal)) + lambda form: on_add(database, form, add_task_signal)) dialog.exec_() -def on_add(database, taskForm: TaskForm, add_task_signal): - task = db.tasks.insert(database.cursor(), taskForm) +def on_add(database, form: ValidTaskForm, add_task_signal): + task = db.tasks.insert(database.cursor(), form) database.commit() add_task_signal.emit(task) diff --git a/src/model/task.py b/src/model/task.py index 26496c4..afbb2ca 100644 --- a/src/model/task.py +++ b/src/model/task.py @@ -6,8 +6,10 @@ class Task(NamedTuple): created_at: int modified_at: int name: str + duration: int tag: str -class TaskForm(NamedTuple): +class ValidTaskForm(NamedTuple): name: str + duration: int tag: str -- cgit v1.2.3