from textual.app import ComposeResult from textual.binding import Binding from textual.screen import Screen from textual.widgets import DataTable, TabbedContent, TabPane, Header, Footer from peewee import fn, JOIN from ..sync import sync from ..db import ( KimaiProject, KimaiCustomer, KimaiActivity, ) from .list import ListPane class KimaiProjectList(ListPane): BINDINGS = [ ("s", "sort", "Sort"), ("r", "refresh", "Refresh"), ("g", "get", "Get data"), ("/", "filter", "Search"), Binding(key="escape", action="cancelfilter", show=False), ] def _refresh(self, filter_query=None): self.table.clear() projects = ( KimaiProject.select( KimaiProject, KimaiCustomer, fn.Count(KimaiActivity.id).alias("activities_count"), ) .join(KimaiCustomer, JOIN.LEFT_OUTER) .switch(KimaiProject) .join(KimaiActivity, JOIN.LEFT_OUTER) .where(KimaiActivity.project.is_null(False)) .group_by(KimaiProject) ) if filter_query: projects = projects.where( KimaiProject.name.contains(filter_query) | KimaiCustomer.name.contains(filter_query) ) self.table.add_rows( [ [ project.customer.id, project.customer.name, project.id, project.name, project.activities_count, ] for project in projects ] ) self.table.sort(self.sort) def action_get(self) -> None: sync() self._refresh() def on_mount(self) -> None: self.table = self.query_one(DataTable) self.table.cursor_type = "row" self.columns = self.table.add_columns( "customer id", "customer", "project id", "project", "activities" ) # self.sort = (self.columns[1], self.columns[3]) self.sort = self.columns[1] self._refresh() class KimaiScreen(Screen): BINDINGS = [ ("p", "show_tab('projects')", "Projects"), ] SUB_TITLE = "Kimai" def compose(self) -> ComposeResult: yield Header() with TabbedContent(initial="activities"): with TabPane("Categories", id="categories"): yield KimaiProjectList() with TabPane("Activities", id="activities"): yield KimaiProjectList() yield Footer() def action_show_tab(self, tab: str) -> None: """Switch to a new tab.""" self.get_child_by_type(TabbedContent).active = tab