diff --git a/src/tt/cli.py b/src/tt/cli.py index 9fb2c07..2c534b7 100644 --- a/src/tt/cli.py +++ b/src/tt/cli.py @@ -5,8 +5,9 @@ from typing_extensions import Annotated from .controller.tasks import add_task from .import_csv import import_tasks_from_csv from .db import initialize_db -from .tui import tasks -from .tui import recurring +from .tui.tasks import run as tasks_tui +from .tui.overview import run as overview_tui +from .tui.recurring import run as recurring_tui app = typer.Typer() @@ -42,17 +43,23 @@ def new( @app.command() -def browse( +def tasks( view: Annotated[str, typer.Option("-v", "--view", help="saved view")] = "default", ): initialize_db() - tasks.run(view) + tasks_tui(view) @app.command() def generators(): initialize_db() - recurring.run() + recurring_tui() + + +@app.command() +def overview(): + initialize_db() + overview_tui() @app.command() diff --git a/src/tt/controller/summaries.py b/src/tt/controller/summaries.py index 9e0b021..1925d89 100644 --- a/src/tt/controller/summaries.py +++ b/src/tt/controller/summaries.py @@ -140,28 +140,31 @@ def get_due_soon( """ now = datetime.now() - # Base query + # unfinished tasks w/ due date query = ( - Task.select(Task, Category.name.alias("category_name")) + Task.select(Task) .join(Category, JOIN.LEFT_OUTER) .where( (~Task.deleted) & (Task.due.is_null(False)) + & (Task.due != "") & (Task.status != TaskStatus.DONE.value) ) ) + # filter by category if category: query = query.where(Category.name == category) - # Handle overdue tasks based on all_overdue parameter if all_overdue: + # grab all overdue & append a few more on overdue_tasks = list(query.where(Task.due < now).order_by(Task.due)) upcoming_tasks = list( query.where(Task.due >= now).order_by(Task.due).limit(num) ) tasks = overdue_tasks + upcoming_tasks else: + # just most due tasks tasks = list(query.order_by(Task.due).limit(num)) return [ diff --git a/src/tt/tui/overview.py b/src/tt/tui/overview.py index 3278818..cf30543 100644 --- a/src/tt/tui/overview.py +++ b/src/tt/tui/overview.py @@ -1,6 +1,6 @@ from textual.app import App, ComposeResult from textual.containers import ScrollableContainer, Horizontal -from textual.widgets import DataTable, Static +from textual.widgets import DataTable, Static, Label from textual.binding import Binding from datetime import datetime @@ -10,6 +10,16 @@ from ..controller.summaries import ( get_due_soon, ) +#### +# Due Soon # Task Summary +# # +# # +################################## +# WIP # Recently active +# # +# # +# # + class CategoryTable(DataTable): """Table showing category summaries""" @@ -35,54 +45,46 @@ class CategoryTable(DataTable): ) -class TaskList(DataTable): - """Base class for task list tables""" - - def on_mount(self): - self.add_columns("Task", "Status", "Category", "Due Date") - - def format_due_date(self, due_date: datetime | None) -> str: - if not due_date: - return "No due date" - return due_date.strftime("%Y-%m-%d") +def format_due_date(due_date: datetime | None) -> str: + if not due_date: + return "No due date" + return due_date.strftime("%Y-%m-%d") -class DueTaskList(TaskList): +class DueTaskList(DataTable): """Table showing upcoming and overdue tasks""" def on_mount(self): - super().on_mount() + self.add_columns("Task", "Category", "Due Date") self.refresh_data() def refresh_data(self): self.clear() - tasks = get_due_soon(10, all_overdue=True) # Show all overdue + next 10 + tasks = get_due_soon(10, all_overdue=True) for task in tasks: self.add_row( task["text"], - task["status"], - task["category"] or "No category", - self.format_due_date(task["due"]), + task["category"], + format_due_date(task["due"]), key=str(task["id"]), ) -class RecentTaskList(TaskList): +class RecentTaskList(DataTable): """Table showing recently active tasks""" def on_mount(self): - super().on_mount() + self.add_columns("Task", "Category", "Due Date") self.refresh_data() def refresh_data(self): self.clear() - tasks = get_recently_active(10) # Show 10 most recent + tasks = get_recently_active(10) for task in tasks: self.add_row( task["text"], - task["status"], - task["category"] or "No category", - self.format_due_date(task["due"]), + task["category"] or "-", + format_due_date(task["due"]), key=str(task["id"]), ) @@ -90,27 +92,20 @@ class RecentTaskList(TaskList): class Overview(App): """Task overview application""" - TITLE = "Task Overview" CSS = """ CategoryTable { height: 40%; margin: 1 1; } + + Label { + align: center middle; + background: purple; + } #lists { height: 60%; } - - TaskList { - width: 50%; - margin: 1 1; - } - - Static { - content-align: center middle; - background: $panel; - padding: 1; - } """ BINDINGS = [ @@ -119,12 +114,14 @@ class Overview(App): ] def compose(self) -> ComposeResult: - yield CategoryTable() - yield Static("Upcoming and Recent Tasks") + with Horizontal(id="top"): + yield CategoryTable() with Horizontal(id="lists"): with ScrollableContainer(): + yield Label("Due Soon") yield DueTaskList() with ScrollableContainer(): + yield Label("Activity") yield RecentTaskList() def action_refresh(self): @@ -134,6 +131,6 @@ class Overview(App): self.query_one(RecentTaskList).refresh_data() -if __name__ == "__main__": +def run(): app = Overview() app.run()