split up controllers; new summaries

This commit is contained in:
James Turk 2025-01-05 21:42:39 -06:00
parent 9510f5b01b
commit c9e8571a85
4 changed files with 182 additions and 2 deletions

View File

@ -2,7 +2,7 @@ import typer
import httpx
import lxml.html
from typing_extensions import Annotated
from .controller import add_task
from .controller.tasks import add_task
from .import_csv import import_tasks_from_csv
from .db import initialize_db
from .tui import tasks

View File

@ -0,0 +1,180 @@
from datetime import datetime, timedelta
from peewee import fn, JOIN
from .db import Task, Category, TaskStatus
def get_category_summary(num: int = 5) -> list[dict]:
"""
Returns summary of top categories with task counts by status and due dates.
Args:
num: Number of categories to return, ordered by total task count
Returns:
List of dicts containing category name and task statistics
"""
now = datetime.now()
week_from_now = now + timedelta(days=7)
# Subquery for overdue tasks
overdue_count = (
Task.select(Task.category, fn.COUNT(Task.id).alias("overdue"))
.where(
(~Task.deleted) & (Task.due < now) & (Task.status != TaskStatus.DONE.value)
)
.group_by(Task.category)
)
# Subquery for tasks due in next 7 days
due_soon_count = (
Task.select(Task.category, fn.COUNT(Task.id).alias("due_soon"))
.where(
(~Task.deleted)
& (Task.due >= now)
& (Task.due <= week_from_now)
& (Task.status != TaskStatus.DONE.value)
)
.group_by(Task.category)
)
# Main query joining all the information
query = (
Category.select(
Category.name,
fn.COALESCE(fn.SUM(Task.status == TaskStatus.ZERO.value), 0).alias(
"zero_count"
),
fn.COALESCE(fn.SUM(Task.status == TaskStatus.WIP.value), 0).alias(
"wip_count"
),
fn.COALESCE(fn.SUM(Task.status == TaskStatus.BLOCKED.value), 0).alias(
"blocked_count"
),
fn.COALESCE(fn.SUM(Task.status == TaskStatus.DONE.value), 0).alias(
"done_count"
),
fn.COALESCE(overdue_count.c.overdue, 0).alias("overdue"),
fn.COALESCE(due_soon_count.c.due_soon, 0).alias("due_soon"),
)
.join(Task, JOIN.LEFT_OUTER)
.join(
overdue_count,
JOIN.LEFT_OUTER,
on=(Category.id == overdue_count.c.category_id),
)
.join(
due_soon_count,
JOIN.LEFT_OUTER,
on=(Category.id == due_soon_count.c.category_id),
)
.where(~Task.deleted)
.group_by(Category.id)
.order_by(fn.COUNT(Task.id).desc())
.limit(num)
)
return [
{
"category": cat.name,
"tasks": {
"zero": cat.zero_count,
"wip": cat.wip_count,
"blocked": cat.blocked_count,
"done": cat.done_count,
"overdue": cat.overdue,
"due_soon": cat.due_soon,
},
}
for cat in query
]
def get_recently_active(num: int = 5, category: str | None = None) -> list[dict]:
"""
Returns most recently active tasks, optionally filtered by category.
Args:
num: Number of tasks to return
category: Optional category name to filter by
Returns:
List of tasks ordered by last activity (updated_at)
"""
query = (
Task.select(Task, Category.name.alias("category_name"))
.join(Category, JOIN.LEFT_OUTER)
.where(~Task.deleted)
)
if category:
query = query.where(Category.name == category)
query = query.order_by(Task.updated_at.desc()).limit(num)
return [
{
"id": task.id,
"text": task.text,
"status": task.status,
"type": task.type,
"category": task.category_name,
"due": task.due,
"updated_at": task.updated_at,
}
for task in query
]
def get_due_soon(
num: int = 5, all_overdue: bool = True, category: str | None = None
) -> list[dict]:
"""
Returns tasks ordered by due date, optionally including all overdue tasks.
Args:
num: Number of non-overdue tasks to return
all_overdue: If True, returns all overdue tasks plus num more tasks
If False, includes overdue tasks in the count
category: Optional category name to filter by
Returns:
List of tasks ordered by due date
"""
now = datetime.now()
# Base query
query = (
Task.select(Task, Category.name.alias("category_name"))
.join(Category, JOIN.LEFT_OUTER)
.where(
(~Task.deleted)
& (Task.due.is_null(False))
& (Task.status != TaskStatus.DONE.value)
)
)
if category:
query = query.where(Category.name == category)
# Handle overdue tasks based on all_overdue parameter
if all_overdue:
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:
tasks = list(query.order_by(Task.due).limit(num))
return [
{
"id": task.id,
"text": task.text,
"status": task.status,
"type": task.type,
"category": task.category_name,
"due": task.due,
"days_until_due": (task.due - now).days if task.due else None,
}
for task in tasks
]

View File

@ -2,7 +2,7 @@ import json
from textual.widgets import Input
from datetime import datetime
from ..controller import (
from ..controller.tasks import (
get_task,
get_tasks,
add_task,