hamster-tools/hamstertools/screens/kimai/__init__.py

194 lines
5.7 KiB
Python
Raw Normal View History

2023-11-03 21:52:08 +00:00
from textual.app import ComposeResult
2023-11-17 22:54:25 +00:00
from textual.coordinate import Coordinate
2023-11-03 21:52:08 +00:00
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 (
2023-11-03 21:52:08 +00:00
KimaiProject,
KimaiCustomer,
KimaiActivity,
)
from ...kimaiapi import Timesheet as KimaiAPITimesheet
2023-11-03 21:52:08 +00:00
from ..list import ListPane
2023-11-03 21:52:08 +00:00
class KimaiCustomerList(ListPane):
pass
2023-11-03 21:52:08 +00:00
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):
2023-11-03 21:52:08 +00:00
self.table.clear()
filter_search = self.query_one("#filter #search").value
2023-11-03 21:52:08 +00:00
projects = (
KimaiProject.select(
KimaiProject,
2023-11-04 00:01:53 +00:00
KimaiCustomer.id,
fn.COALESCE(KimaiCustomer.name, "").alias("customer_name"),
2023-11-03 21:52:08 +00:00
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:
2023-11-03 21:52:08 +00:00
projects = projects.where(
KimaiProject.name.contains(filter_search)
| KimaiCustomer.name.contains(filter_search)
2023-11-03 21:52:08 +00:00
)
self.table.add_rows(
[
[
2023-11-04 00:01:53 +00:00
str(project.customer_id) if project.customer_id is not None else "",
project.customer_name,
2023-11-03 21:52:08 +00:00
project.id,
project.name,
project.activities_count,
project.visible,
2023-11-03 21:52:08 +00:00
]
for project in projects
]
)
2023-11-04 00:01:53 +00:00
self.table.sort(*self.sort)
2023-11-03 21:52:08 +00:00
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"
2023-11-03 21:52:08 +00:00
)
2023-11-04 00:01:53 +00:00
self.sort = (self.columns[1], self.columns[3])
2023-11-03 21:52:08 +00:00
self._refresh()
class KimaiActivityList(ListPane):
BINDINGS = [
("s", "sort", "Sort"),
("r", "refresh", "Refresh"),
("g", "get", "Get data"),
2023-11-17 22:54:25 +00:00
("#", "count", "Count"),
("/", "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,
2023-11-04 00:01:53 +00:00
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 "",
2023-11-04 00:01:53 +00:00
truncate(activity.project_name, 40),
activity.id,
2023-11-04 00:01:53 +00:00
truncate(activity.name, 40),
activity.visible,
2023-11-18 11:23:54 +00:00
"?",
]
for activity in activities
]
)
2023-11-04 00:01:53 +00:00
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",
2023-11-17 22:54:25 +00:00
"times",
)
2023-11-04 00:01:53 +00:00
self.sort = (self.columns[1], self.columns[3])
self._refresh()
2023-11-17 22:54:25 +00:00
def action_count(self) -> None:
row_idx: int = self.table.cursor_row
row_cells = self.table.get_row_at(row_idx)
activity_id = row_cells[2]
count = len(KimaiAPITimesheet.list_by(self.app.api, activity=activity_id))
2023-11-18 11:23:54 +00:00
self.table.update_cell_at(Coordinate(row_idx, 5), count)
2023-11-17 22:54:25 +00:00
2023-11-03 21:52:08 +00:00
class KimaiScreen(Screen):
BINDINGS = [
("c", "show_tab('customers')", "Customers"),
2023-11-03 21:52:08 +00:00
("p", "show_tab('projects')", "Projects"),
("a", "show_tab('activities')", "Activities"),
2023-11-03 21:52:08 +00:00
]
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"):
2023-11-03 21:52:08 +00:00
yield KimaiProjectList()
with TabPane("Activities", id="activities"):
yield KimaiActivityList()
2023-11-03 21:52:08 +00:00
yield Footer()
def on_mount(self) -> None:
self.query_one("TabbedContent Tabs").can_focus = False
self.query_one("#projects DataTable").focus()
2023-11-03 21:52:08 +00:00
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()