saving and loading views

This commit is contained in:
James Turk 2025-01-04 04:12:02 -06:00
parent 20b95d087b
commit 35aaf17de2
4 changed files with 84 additions and 17 deletions

View File

@ -39,8 +39,10 @@ def new(
@app.command() @app.command()
def browse(): def browse(
tui.run() view: Annotated[str, typer.Option("-v", "--view", help="saved view")] = "default",
):
tui.run(view)
@app.command() @app.command()

View File

@ -1,6 +1,14 @@
import json
from datetime import datetime from datetime import datetime
from peewee import fn from peewee import fn
from .db import Task, Category, TaskStatus, db from .db import db, Task, Category, TaskStatus, SavedSearch
def category_lookup(category):
category_id = None
if category:
category, _ = Category.get_or_create(name=category)
return category.id
def add_task( def add_task(
@ -15,13 +23,10 @@ def add_task(
Returns the created task instance. Returns the created task instance.
""" """
with db.atomic(): with db.atomic():
category_id = None category_id = category_lookup(category)
if category: task = Task.create(
category, _ = Category.get_or_create(name=category) text=text, type=type, status=status, due=due, category_id=category_id
category_id = category.id )
task = Task.create(
text=text, type=type, status=status, due=due, category_id=category_id
)
return task return task
@ -30,6 +35,8 @@ def update_task(
**kwargs, **kwargs,
) -> Task: ) -> Task:
with db.atomic(): with db.atomic():
if category := kwargs.pop("category", None):
kwargs["category_id"] = category_lookup(category)
task = Task.get_by_id(task_id) task = Task.get_by_id(task_id)
query = Task.update(kwargs).where(Task.id == task_id) query = Task.update(kwargs).where(Task.id == task_id)
query.execute() query.execute()
@ -84,3 +91,17 @@ def get_tasks(
def get_categories() -> list[Category]: def get_categories() -> list[Category]:
return list(Category.select().order_by(Category.name)) return list(Category.select().order_by(Category.name))
def save_view(name: str, *, filters: dict, sort_string: str) -> SavedSearch:
filters_json = json.dumps(filters)
return 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)

View File

@ -58,9 +58,18 @@ class Task(BaseModel):
return super(Task, self).save(*args, **kwargs) return super(Task, self).save(*args, **kwargs)
class SavedSearch(BaseModel):
name = CharField(unique=True)
filters = CharField()
sort_string = CharField()
def __str__(self):
return self.name
def initialize_db(): def initialize_db():
db.connect() db.connect()
db.create_tables([Category, Task]) db.create_tables([Category, Task, SavedSearch])
if not Category.select().exists(): if not Category.select().exists():
Category.create(name="default") Category.create(name="default")
db.close() db.close()

View File

@ -1,3 +1,4 @@
import json
from textual.app import App from textual.app import App
from textual.screen import ModalScreen from textual.screen import ModalScreen
from rich.table import Table from rich.table import Table
@ -10,7 +11,14 @@ from textual.widgets import (
from textual.containers import Container from textual.containers import Container
from datetime import datetime from datetime import datetime
from .controller import get_tasks, add_task, update_task, TaskStatus from .controller import (
get_tasks,
add_task,
update_task,
TaskStatus,
save_view,
get_saved_view,
)
from .db import initialize_db from .db import initialize_db
from .utils import ( from .utils import (
remove_rich_tag, remove_rich_tag,
@ -97,19 +105,31 @@ class TT(App):
("a", "add_task", "add task"), ("a", "add_task", "add task"),
("t", "toggle_cell", "toggle status"), ("t", "toggle_cell", "toggle status"),
("d", "delete_task", "delete (must be on row mode)"), ("d", "delete_task", "delete (must be on row mode)"),
# saved views
("ctrl+s", "save_view", "save current view"),
("ctrl+o", "load_view", "load saved view"),
# other # other
("q", "quit", "quit"), ("q", "quit", "quit"),
("?", "show_keys", "show keybindings"), ("?", "show_keys", "show keybindings"),
] ]
def __init__(self): def __init__(self, default_view="default"):
super().__init__() super().__init__()
self.search_query = ""
self.filters = {} self.filters = {}
self.sort_string = "due,status" self.sort_string = "due,status"
self.saved_cursor_pos = (0, 0) self._load_view(default_view)
self.search_query = ""
self.saved_cursor_pos = (1, 0)
self.save_on_move = None self.save_on_move = None
def _load_view(self, name):
try:
saved = get_saved_view(name)
self.filters = json.loads(saved.filters)
self.sort_string = saved.sort_string
except Exception:
self.notify(f"Could not load {name}")
def compose(self): def compose(self):
self.header = Header() self.header = Header()
self.table = DataTable() self.table = DataTable()
@ -271,6 +291,10 @@ class TT(App):
self.input_label.update("sort: ") self.input_label.update("sort: ")
elif mode == "filter": elif mode == "filter":
self.input_label.update("filter: ") self.input_label.update("filter: ")
elif mode == "save-view":
self.input_label.update("view name: ")
elif mode == "load-view":
self.input_label.update("view name: ")
else: else:
raise ValueError(f"unknown mode: {mode}") raise ValueError(f"unknown mode: {mode}")
self.set_focus(self.input_widget) self.set_focus(self.input_widget)
@ -286,6 +310,12 @@ class TT(App):
def action_start_search(self): def action_start_search(self):
self._show_input("search", "") self._show_input("search", "")
def action_save_view(self):
self._show_input("save-view", "default")
def action_load_view(self):
self._show_input("load-view", "")
def action_start_filter(self): def action_start_filter(self):
# filter the currently selected column # filter the currently selected column
cur_col = self.table.cursor_column cur_col = self.table.cursor_column
@ -330,6 +360,11 @@ class TT(App):
self.right_status.update(self.sort_string) self.right_status.update(self.sort_string)
elif self.mode == "edit": elif self.mode == "edit":
self.apply_change(event.value) self.apply_change(event.value)
elif self.mode == "save-view":
save_view(event.value, filters=self.filters, sort_string=self.sort_string)
elif self.mode == "load-view":
self._load_view(event.value)
self.refresh_tasks(restore_cursor=False)
else: else:
raise ValueError(f"unknown mode: {self.mode}") raise ValueError(f"unknown mode: {self.mode}")
self._hide_input() self._hide_input()
@ -411,9 +446,9 @@ class KeyBindingsScreen(ModalScreen):
self.app.pop_screen() self.app.pop_screen()
def run(): def run(default_view):
initialize_db() initialize_db()
app = TT() app = TT(default_view)
app.run() app.run()