improved overview

This commit is contained in:
James Turk 2025-02-10 20:04:31 -06:00
parent ccd0178de1
commit 9de289d43b
3 changed files with 53 additions and 46 deletions

View File

@ -5,8 +5,9 @@ from typing_extensions import Annotated
from .controller.tasks import add_task from .controller.tasks import add_task
from .import_csv import import_tasks_from_csv from .import_csv import import_tasks_from_csv
from .db import initialize_db from .db import initialize_db
from .tui import tasks from .tui.tasks import run as tasks_tui
from .tui import recurring from .tui.overview import run as overview_tui
from .tui.recurring import run as recurring_tui
app = typer.Typer() app = typer.Typer()
@ -42,17 +43,23 @@ def new(
@app.command() @app.command()
def browse( def tasks(
view: Annotated[str, typer.Option("-v", "--view", help="saved view")] = "default", view: Annotated[str, typer.Option("-v", "--view", help="saved view")] = "default",
): ):
initialize_db() initialize_db()
tasks.run(view) tasks_tui(view)
@app.command() @app.command()
def generators(): def generators():
initialize_db() initialize_db()
recurring.run() recurring_tui()
@app.command()
def overview():
initialize_db()
overview_tui()
@app.command() @app.command()

View File

@ -140,28 +140,31 @@ def get_due_soon(
""" """
now = datetime.now() now = datetime.now()
# Base query # unfinished tasks w/ due date
query = ( query = (
Task.select(Task, Category.name.alias("category_name")) Task.select(Task)
.join(Category, JOIN.LEFT_OUTER) .join(Category, JOIN.LEFT_OUTER)
.where( .where(
(~Task.deleted) (~Task.deleted)
& (Task.due.is_null(False)) & (Task.due.is_null(False))
& (Task.due != "")
& (Task.status != TaskStatus.DONE.value) & (Task.status != TaskStatus.DONE.value)
) )
) )
# filter by category
if category: if category:
query = query.where(Category.name == category) query = query.where(Category.name == category)
# Handle overdue tasks based on all_overdue parameter
if all_overdue: if all_overdue:
# grab all overdue & append a few more on
overdue_tasks = list(query.where(Task.due < now).order_by(Task.due)) overdue_tasks = list(query.where(Task.due < now).order_by(Task.due))
upcoming_tasks = list( upcoming_tasks = list(
query.where(Task.due >= now).order_by(Task.due).limit(num) query.where(Task.due >= now).order_by(Task.due).limit(num)
) )
tasks = overdue_tasks + upcoming_tasks tasks = overdue_tasks + upcoming_tasks
else: else:
# just most due tasks
tasks = list(query.order_by(Task.due).limit(num)) tasks = list(query.order_by(Task.due).limit(num))
return [ return [

View File

@ -1,6 +1,6 @@
from textual.app import App, ComposeResult from textual.app import App, ComposeResult
from textual.containers import ScrollableContainer, Horizontal 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 textual.binding import Binding
from datetime import datetime from datetime import datetime
@ -10,6 +10,16 @@ from ..controller.summaries import (
get_due_soon, get_due_soon,
) )
####
# Due Soon # Task Summary
# #
# #
##################################
# WIP # Recently active
# #
# #
# #
class CategoryTable(DataTable): class CategoryTable(DataTable):
"""Table showing category summaries""" """Table showing category summaries"""
@ -35,54 +45,46 @@ class CategoryTable(DataTable):
) )
class TaskList(DataTable): def format_due_date(due_date: datetime | None) -> str:
"""Base class for task list tables""" if not due_date:
return "No due date"
def on_mount(self): return due_date.strftime("%Y-%m-%d")
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")
class DueTaskList(TaskList): class DueTaskList(DataTable):
"""Table showing upcoming and overdue tasks""" """Table showing upcoming and overdue tasks"""
def on_mount(self): def on_mount(self):
super().on_mount() self.add_columns("Task", "Category", "Due Date")
self.refresh_data() self.refresh_data()
def refresh_data(self): def refresh_data(self):
self.clear() 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: for task in tasks:
self.add_row( self.add_row(
task["text"], task["text"],
task["status"], task["category"],
task["category"] or "No category", format_due_date(task["due"]),
self.format_due_date(task["due"]),
key=str(task["id"]), key=str(task["id"]),
) )
class RecentTaskList(TaskList): class RecentTaskList(DataTable):
"""Table showing recently active tasks""" """Table showing recently active tasks"""
def on_mount(self): def on_mount(self):
super().on_mount() self.add_columns("Task", "Category", "Due Date")
self.refresh_data() self.refresh_data()
def refresh_data(self): def refresh_data(self):
self.clear() self.clear()
tasks = get_recently_active(10) # Show 10 most recent tasks = get_recently_active(10)
for task in tasks: for task in tasks:
self.add_row( self.add_row(
task["text"], task["text"],
task["status"], task["category"] or "-",
task["category"] or "No category", format_due_date(task["due"]),
self.format_due_date(task["due"]),
key=str(task["id"]), key=str(task["id"]),
) )
@ -90,27 +92,20 @@ class RecentTaskList(TaskList):
class Overview(App): class Overview(App):
"""Task overview application""" """Task overview application"""
TITLE = "Task Overview"
CSS = """ CSS = """
CategoryTable { CategoryTable {
height: 40%; height: 40%;
margin: 1 1; margin: 1 1;
} }
Label {
align: center middle;
background: purple;
}
#lists { #lists {
height: 60%; height: 60%;
} }
TaskList {
width: 50%;
margin: 1 1;
}
Static {
content-align: center middle;
background: $panel;
padding: 1;
}
""" """
BINDINGS = [ BINDINGS = [
@ -119,12 +114,14 @@ class Overview(App):
] ]
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield CategoryTable() with Horizontal(id="top"):
yield Static("Upcoming and Recent Tasks") yield CategoryTable()
with Horizontal(id="lists"): with Horizontal(id="lists"):
with ScrollableContainer(): with ScrollableContainer():
yield Label("Due Soon")
yield DueTaskList() yield DueTaskList()
with ScrollableContainer(): with ScrollableContainer():
yield Label("Activity")
yield RecentTaskList() yield RecentTaskList()
def action_refresh(self): def action_refresh(self):
@ -134,6 +131,6 @@ class Overview(App):
self.query_one(RecentTaskList).refresh_data() self.query_one(RecentTaskList).refresh_data()
if __name__ == "__main__": def run():
app = Overview() app = Overview()
app.run() app.run()