saving and loading views
This commit is contained in:
parent
20b95d087b
commit
35aaf17de2
@ -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()
|
||||||
|
@ -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)
|
||||||
|
11
src/tt/db.py
11
src/tt/db.py
@ -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()
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user