diff --git a/src/tt/cli.py b/src/tt/cli.py index 492cded..ede1237 100644 --- a/src/tt/cli.py +++ b/src/tt/cli.py @@ -2,12 +2,11 @@ import typer import httpx import lxml.html from typing_extensions import Annotated -from .controller.tasks import add_task from .db import initialize_db -from .tui.tasks import run as tasks_tui from .tui.things import run as things_tui -from .tui.overview import run as overview_tui +#from .tui.overview import run as overview_tui from .tui.recurring import run as recurring_tui +from .controller.things import add_thing app = typer.Typer() @@ -38,16 +37,8 @@ def new( # due = typer.prompt("Due (YYYY-MM-DD):") # TODO: validate/allow blank - add_task(name, category, due) - typer.echo("Created new task!") - - -@app.command() -def tasks( - view: Annotated[str, typer.Option("-v", "--view", help="saved view")] = "default", -): - initialize_db() - tasks_tui(view) + add_thing(name, category, due) + typer.echo("Created new thing!") @app.command() def things( diff --git a/src/tt/controller/generators.py b/src/tt/controller/generators.py index 3dc21f1..b7c5e3c 100644 --- a/src/tt/controller/generators.py +++ b/src/tt/controller/generators.py @@ -1,15 +1,15 @@ import json from datetime import date, timedelta -from ..db import db, TaskGenerator -from .tasks import add_task +from ..db import db, ThingGenerator +from .things import add_thing -def get_generator(item_id: int) -> TaskGenerator: - return TaskGenerator.get_by_id(item_id) +def get_generator(item_id: int) -> ThingGenerator: + return ThingGenerator.get_by_id(item_id) -def get_generators() -> list[TaskGenerator]: - query = TaskGenerator.select().where(~TaskGenerator.deleted) +def get_generators() -> list[ThingGenerator]: + query = ThingGenerator.select().where(~ThingGenerator.deleted) return query.order_by("type", "template") @@ -17,11 +17,11 @@ def add_generator( template: str, type: str, val: str, -) -> TaskGenerator: +) -> ThingGenerator: # JSON for future expansion config = json.dumps({"val": val}) with db.atomic(): - task = TaskGenerator.create( + task = ThingGenerator.create( template=template, type=type, config=config, @@ -46,7 +46,7 @@ def generate_needed_tasks(): ) for c in to_create: - add_task(**c) + add_thing(**c) return to_create @@ -54,13 +54,13 @@ def generate_needed_tasks(): def update_generator( item_id: int, **kwargs, -) -> TaskGenerator: +) -> ThingGenerator: # replace "val" with JSON if "val" in kwargs: config = {"val": kwargs.pop("val")} kwargs["config"] = json.dumps(config) with db.atomic(): - query = TaskGenerator.update(kwargs).where(TaskGenerator.id == item_id) + query = ThingGenerator.update(kwargs).where(ThingGenerator.id == item_id) query.execute() - task = TaskGenerator.get_by_id(item_id) + task = ThingGenerator.get_by_id(item_id) return task diff --git a/src/tt/controller/summaries.py b/src/tt/controller/summaries.py deleted file mode 100644 index 227f371..0000000 --- a/src/tt/controller/summaries.py +++ /dev/null @@ -1,107 +0,0 @@ -from datetime import datetime, timedelta -from peewee import fn, JOIN -from ..db import Task - - -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 - """ - return [] - -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).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 if task.category else None, - "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() - - # unfinished tasks w/ due date - query = ( - Task.select(Task) - .join(Category, JOIN.LEFT_OUTER) - .where( - (~Task.deleted) - & (Task.due.is_null(False)) - & (Task.due != "") - & (Task.status != "done") - ) - ) - - # filter by category - if category: - query = query.where(Category.name == category) - - 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 [ - { - "id": task.id, - "text": task.text, - "status": task.status, - "type": task.type, - "category": task.category.name if task.category else None, - "due": task.due, - "days_until_due": (task.due - now).days if task.due else None, - } - for task in tasks - ] diff --git a/src/tt/controller/tasks.py b/src/tt/controller/tasks.py deleted file mode 100644 index f9d93da..0000000 --- a/src/tt/controller/tasks.py +++ /dev/null @@ -1,114 +0,0 @@ -import json -from datetime import datetime -from peewee import fn -from peewee import Case, Value -from ..db import db, Task, SavedSearch -from .. import config -from ..constants import SPECIAL_DATES_PIECES - - -def get_task(item_id: int) -> Task: - return Task.get_by_id(item_id) - - -def add_task( - text: str, - status: str, - due: datetime | str = SPECIAL_DATES_PIECES["unclassified"], - type: str = "", - project: str = "", -) -> Task: - """ - Add a new task to the database. - Returns the created task instance. - """ - with db.atomic(): - task = Task.create( - text=text, type=type, status=status, due=due, project=project - ) - return task - - -def update_task( - item_id: int, - **kwargs, -) -> Task: - with db.atomic(): - query = Task.update(kwargs).where(Task.id == item_id) - query.execute() - task = Task.get_by_id(item_id) - return task - - -def _parse_sort_string(sort_string, status_order): - """ - Convert sort string like 'field1,-field2' to peewee order_by expressions. - """ - sort_expressions = [] - - if not sort_string: - return sort_expressions - - for field in sort_string.split(","): - is_desc = field.startswith("-") - field_name = field[1:] if is_desc else field - - if field == "status": - if not status_order: - status_order = list(config.STATUSES.keys()) - # CASE statement that maps each status to its position in the order - order_case = Case( - Task.status, - [(s, Value(i)) for i, s in enumerate(status_order)], - ) - sort_expressions.append(order_case.desc() if is_desc else order_case.asc()) - elif field_name == "due_date": - expr = fn.COALESCE(getattr(Task, field_name), datetime(3000, 12, 31)) - sort_expressions.append(expr.desc() if is_desc else expr) - else: - field_expr = getattr(Task, field_name) - sort_expressions.append(field_expr.desc() if is_desc else field_expr) - - return sort_expressions - - -def get_tasks( - search_text: str | None = None, - statuses: tuple[str] | None = None, - projects: tuple[str] | None = None, - sort: str = "", -) -> list[Task]: - query = Task.select().where(~Task.deleted) - - if search_text: - query = query.where(fn.Lower(Task.text).contains(search_text.lower())) - if statuses: - query = query.where(Task.status.in_(statuses)) - if projects: - query = query.where(Task.project.in_(projects)) - - sort_expressions = _parse_sort_string(sort, statuses) - query = query.order_by(*sort_expressions) - - return list(query) - - -def save_view(name: str, *, filters: dict, sort_string: str) -> SavedSearch: - filters_json = json.dumps(filters) - - with db.atomic(): - if SavedSearch.select().where(SavedSearch.name == name).exists(): - query = SavedSearch.update( - filters=filters_json, sort_string=sort_string - ).where(SavedSearch.name == name) - query.execute() - else: - SavedSearch.create(name=name, filters=filters_json, sort_string=sort_string) - - -def get_saved_view_names() -> list[str]: - return [search.name for search in SavedSearch.select()] - - -def get_saved_view(name: str) -> SavedSearch: - return SavedSearch.get(SavedSearch.name == name) diff --git a/src/tt/db.py b/src/tt/db.py index d0b854a..627edd2 100644 --- a/src/tt/db.py +++ b/src/tt/db.py @@ -40,25 +40,9 @@ class Thing(BaseModel): self.updated_at = datetime.now() return super(Thing, self).save(*args, **kwargs) - - -class Task(BaseModel): - text = TextField() - status = CharField() - due = DateTimeField(null=True) - type = CharField() - project = CharField() - created_at = DateTimeField(default=datetime.now) - updated_at = DateTimeField(default=datetime.now) - deleted = BooleanField(default=False) - - def save(self, *args, **kwargs): - self.updated_at = datetime.now() - return super(Task, self).save(*args, **kwargs) - - @property - def due_week(self): - return self.due.isocalendar()[1] - 12 + # @property + # def due_week(self): + # return self.due.isocalendar()[1] - 12 class SavedSearch(BaseModel): @@ -70,7 +54,7 @@ class SavedSearch(BaseModel): return self.name -class TaskGenerator(BaseModel): +class ThingGenerator(BaseModel): template = CharField() type = CharField() config = TextField() # JSON @@ -124,5 +108,5 @@ class TaskGenerator(BaseModel): def initialize_db(): db.connect() - db.create_tables([Task, SavedSearch, TaskGenerator, Thing]) + db.create_tables([SavedSearch, ThingGenerator, Thing]) db.close() diff --git a/src/tt/tui/recurring.py b/src/tt/tui/recurring.py index 9d45a47..45f8fac 100644 --- a/src/tt/tui/recurring.py +++ b/src/tt/tui/recurring.py @@ -10,7 +10,7 @@ from ..controller.generators import ( from .editor import ( TableEditor, ) -class TaskGenEditor(TableEditor): +class GenEditor(TableEditor): def __init__(self): super().__init__() self.update_item_callback = update_generator @@ -34,5 +34,5 @@ class TaskGenEditor(TableEditor): def run(): - app = TaskGenEditor() + app = GenEditor() app.run() diff --git a/src/tt/tui/things.py b/src/tt/tui/things.py index f9f50fa..f26dee3 100644 --- a/src/tt/tui/things.py +++ b/src/tt/tui/things.py @@ -43,7 +43,7 @@ class ThingTable(TableEditor): # event.prevent_default() # elif self.mode == "load-view": # self._load_view(event.value) - # self.refresh_tasks(restore_cursor=False) + # self.refresh_things(restore_cursor=False) # self._hide_input() # # if event isn't handled here it will bubble to parent # event.prevent_default()