194 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from textual.app import ComposeResult
 | 
						|
from textual.coordinate import Coordinate
 | 
						|
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 ...kimaiapi import Timesheet as KimaiAPITimesheet
 | 
						|
 | 
						|
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"),
 | 
						|
        ("#", "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,
 | 
						|
                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",
 | 
						|
            "times",
 | 
						|
        )
 | 
						|
        self.sort = (self.columns[1], self.columns[3])
 | 
						|
        self._refresh()
 | 
						|
 | 
						|
    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))
 | 
						|
 | 
						|
        self.table.update_cell_at(Coordinate(row_idx, 5), count)
 | 
						|
 | 
						|
 | 
						|
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()
 |