hamster-tools/hamstertools/app.py
2023-10-27 21:01:45 +01:00

209 lines
6.4 KiB
Python

from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.widgets import Header, Footer, DataTable, Input
from textual.containers import Horizontal, Vertical
from textual.coordinate import Coordinate
from textual.screen import Screen
from .db import DatabaseManager, Category, Activity
class ListScreen(Screen):
def __init__(self, db_manager):
self.db_manager = db_manager
super().__init__()
def compose(self) -> ComposeResult:
"""create child widgets for the app."""
yield Header()
with Vertical():
yield DataTable()
with Horizontal():
yield Input(id="filter")
yield Footer()
def action_refresh(self) -> None:
self._refresh()
def action_sort(self) -> None:
self.table.cursor_type = "column"
def on_data_table_column_selected(self, event):
self.sort = (event.column_key,)
event.data_table.sort(*self.sort)
event.data_table.cursor_type = "row"
def action_filter(self) -> None:
filter_input = self.query_one("#filter")
filter_input.display = True
self._refresh(filter_input.value)
filter_input.focus()
def on_input_submitted(self, event):
self.table.focus()
def action_cancelfilter(self) -> None:
filter_input = self.query_one("#filter")
filter_input.display = False
filter_input.clear()
self.table.focus()
self._refresh()
def on_input_changed(self, event):
self._refresh(event.value)
class ActivitiesScreen(ListScreen):
BINDINGS = [
("q", "quit", "Quit"),
("s", "sort", "Sort"),
("r", "refresh", "Refresh"),
("/", "filter", "Search"),
("d", "delete", "Delete activity"),
("f", "move_facts", "Move facts"),
Binding(key="escape", action="cancelfilter", show=False),
]
def _refresh(self, filter_query=None):
self.table.clear()
# List activities with the count of facts
activities = Activity.list(self.db_manager, filter_query)
self.table.add_rows(
[
[
activity.category_id,
activity.category_name,
activity.id,
activity.name,
activity.facts_count,
]
for activity in activities
]
)
self.table.sort(*self.sort)
def on_mount(self) -> None:
self.table = self.query_one(DataTable)
self.table.cursor_type = "row"
self.columns = self.table.add_columns(
"category id", "category", "activity id", "activity", "entries"
)
self.sort = (self.columns[1], self.columns[3])
self._refresh()
def action_delete(self) -> None:
# get the keys for the row and column under the cursor.
row_key, _ = self.table.coordinate_to_cell_key(self.table.cursor_coordinate)
activity_id = self.table.get_cell_at(
Coordinate(self.table.cursor_coordinate.row, 2),
)
activity = Activity.get_by_id(self.db_manager, activity_id)
activity.delete()
# supply the row key to `remove_row` to delete the row.
self.table.remove_row(row_key)
def action_move_facts(self) -> None:
row_idx: int = self.table.cursor_row
row_cells = self.table.get_row_at(row_idx)
self.move_from_activity = Activity.get_by_id(self.db_manager, row_cells[2])
for col_idx, cell_value in enumerate(row_cells):
cell_coordinate = Coordinate(row_idx, col_idx)
self.table.update_cell_at(
cell_coordinate,
f"[red]{cell_value}[/red]",
)
self.table.move_cursor(row=self.table.cursor_coordinate[0] + 1)
def on_data_table_row_selected(self, event):
if getattr(self, "move_from_activity", None) is not None:
move_to_activity = Activity.get_by_id(
self.db_manager, self.table.get_cell_at(Coordinate(event.cursor_row, 2))
)
self.move_from_activity.move_facts(move_to_activity)
filter_input = self.query_one("#filter")
self._refresh(filter_input.value)
del self.move_from_activity
class CategoriesScreen(ListScreen):
BINDINGS = [
("q", "quit", "Quit"),
("s", "sort", "Sort"),
("r", "refresh", "Refresh"),
("/", "filter", "Search"),
("d", "delete", "Delete category"),
Binding(key="escape", action="cancelfilter", show=False),
]
def _refresh(self, filter_query=None):
self.table.clear()
categories = Category.list(
self.db_manager, filter_query=filter_query
)
self.table.add_rows(
[
[
category.id,
category.name,
category.activity_count,
]
for category in categories
]
)
self.table.sort(self.sort)
def on_mount(self) -> None:
self.table = self.query_one(DataTable)
self.table.cursor_type = "row"
self.columns = self.table.add_columns("category id", "category", "activities")
self.sort = self.columns[1]
self._refresh()
def action_delete(self) -> None:
# get the keys for the row and column under the cursor.
row_key, _ = self.table.coordinate_to_cell_key(self.table.cursor_coordinate)
category_id = self.table.get_cell_at(
Coordinate(self.table.cursor_coordinate.row, 0),
)
category = Category.get_by_id(self.db_manager, category_id)
category.delete()
# supply the row key to `remove_row` to delete the row.
self.table.remove_row(row_key)
class HamsterToolsApp(App):
CSS_PATH = "app.tcss"
BINDINGS = [
("a", "switch_mode('activities')", "Activities"),
("c", "switch_mode('categories')", "Categories"),
("q", "quit", "Quit"),
]
def __init__(self):
self.db_manager = DatabaseManager("hamster-testing.db")
self.MODES = {
"categories": CategoriesScreen(self.db_manager),
"activities": ActivitiesScreen(self.db_manager)
}
super().__init__()
def on_mount(self) -> None:
self.switch_mode("activities")
def action_quit(self) -> None:
self.exit()
self.db_manager.close()