hamster-tools/hamstertools/screens/kimai.py

180 lines
5.2 KiB
Python

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 ..utils import truncate
from ..sync import sync
from ..db import (
KimaiProject,
KimaiCustomer,
KimaiActivity,
)
from .list import ListPane
class KimaiCustomerList(ListPane):
pass
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):
self.table.clear()
filter_search = self.query_one("#filter #search").value
projects = (
KimaiProject.select(
KimaiProject,
KimaiCustomer.id,
fn.COALESCE(KimaiCustomer.name, "").alias("customer_name"),
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_search:
projects = projects.where(
KimaiProject.name.contains(filter_search)
| KimaiCustomer.name.contains(filter_search)
)
self.table.add_rows(
[
[
str(project.customer_id) if project.customer_id is not None else "",
project.customer_name,
project.id,
project.name,
project.activities_count,
project.visible,
]
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", "visible"
)
self.sort = (self.columns[1], self.columns[3])
self._refresh()
class KimaiActivityList(ListPane):
BINDINGS = [
("s", "sort", "Sort"),
("r", "refresh", "Refresh"),
("g", "get", "Get data"),
("/", "filter", "Search"),
Binding(key="escape", action="cancelfilter", show=False),
]
def _refresh(self):
self.table.clear()
filter_search = self.query_one("#filter #search").value
activities = (
KimaiActivity.select(
KimaiActivity,
fn.COALESCE(KimaiProject.name, "").alias("project_name"),
fn.COALESCE(KimaiCustomer.name, "").alias("customer_name"),
)
.join(KimaiProject, JOIN.LEFT_OUTER)
.join(KimaiCustomer, JOIN.LEFT_OUTER)
.group_by(KimaiActivity)
)
if filter_search:
activities = activities.where(KimaiActivity.name.contains(filter_search))
self.table.add_rows(
[
[
# activity.project.customer_id if activity.project is not None else '',
# activity.customer_name,
str(activity.project_id) if activity.project_id is not None else "",
truncate(activity.project_name, 40),
activity.id,
truncate(activity.name, 40),
activity.visible,
]
for activity in activities
]
)
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",
"id",
"name",
"visible",
)
self.sort = (self.columns[1], self.columns[3])
self._refresh()
class KimaiScreen(Screen):
BINDINGS = [
("c", "show_tab('customers')", "Customers"),
("p", "show_tab('projects')", "Projects"),
("a", "show_tab('activities')", "Activities"),
]
SUB_TITLE = "Kimai"
def compose(self) -> ComposeResult:
yield Header()
with TabbedContent(initial="projects"):
with TabPane("Customers", id="customers"):
yield KimaiCustomerList()
with TabPane("Projects", id="projects"):
yield KimaiProjectList()
with TabPane("Activities", id="activities"):
yield KimaiActivityList()
yield Footer()
def on_mount(self) -> None:
self.query_one("TabbedContent Tabs").can_focus = False
self.query_one("#projects DataTable").focus()
def action_show_tab(self, tab: str) -> None:
"""Switch to a new tab."""
self.get_child_by_type(TabbedContent).active = tab
self.query_one(f"#{tab} DataTable").focus()