Add kimai "visible" field, moar kimai screens
This commit is contained in:
parent
d3c2da74e6
commit
fd28955da0
@ -25,7 +25,7 @@ from .db import (
|
||||
from .sync import sync
|
||||
|
||||
HAMSTER_DIR = Path.home() / ".local/share/hamster"
|
||||
HAMSTER_FILE = HAMSTER_DIR / 'hamster.db'
|
||||
HAMSTER_FILE = HAMSTER_DIR / "hamster.db"
|
||||
|
||||
db.init(HAMSTER_FILE)
|
||||
|
||||
|
@ -43,6 +43,7 @@ class HamsterFact(Model):
|
||||
|
||||
|
||||
class KimaiCustomer(Model):
|
||||
visible = BooleanField(default=True)
|
||||
name = CharField()
|
||||
|
||||
class Meta:
|
||||
@ -53,6 +54,7 @@ class KimaiCustomer(Model):
|
||||
class KimaiProject(Model):
|
||||
name = CharField()
|
||||
customer = ForeignKeyField(KimaiCustomer, backref="projects")
|
||||
visible = BooleanField(default=True)
|
||||
allow_global_activities = BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
@ -63,6 +65,7 @@ class KimaiProject(Model):
|
||||
class KimaiActivity(Model):
|
||||
name = CharField()
|
||||
project = ForeignKeyField(KimaiProject, backref="activities", null=True)
|
||||
visible = BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
|
@ -44,24 +44,26 @@ class BaseAPI(object):
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Customer(BaseAPI):
|
||||
def __init__(self, api, id, name):
|
||||
super().__init__(api, id=id, name=name)
|
||||
api: KimaiAPI = field(repr=False)
|
||||
id: int
|
||||
name: str
|
||||
visible: bool = field(default=True)
|
||||
|
||||
@staticmethod
|
||||
def list(api):
|
||||
return [Customer(api, c["id"], c["name"]) for c in api.customers_json]
|
||||
return [
|
||||
Customer(api, c["id"], c["name"], c["visible"]) for c in api.customers_json
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(api, id):
|
||||
for value in api.customers_json:
|
||||
if value["id"] == id:
|
||||
return Customer(api, value["id"], value["name"])
|
||||
for c in api.customers_json:
|
||||
if c["id"] == id:
|
||||
return Customer(api, c["id"], c["name"], c["visible"])
|
||||
raise NotFound()
|
||||
|
||||
def __repr__(self):
|
||||
return f"Customer (id={self.id}, name={self.name})"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Project(BaseAPI):
|
||||
@ -70,6 +72,7 @@ class Project(BaseAPI):
|
||||
name: str
|
||||
customer: Customer
|
||||
allow_global_activities: bool = field(default=True)
|
||||
visible: bool = field(default=True)
|
||||
|
||||
@staticmethod
|
||||
def list(api):
|
||||
@ -80,20 +83,22 @@ class Project(BaseAPI):
|
||||
p["name"],
|
||||
Customer.get_by_id(api, p["customer"]),
|
||||
p["globalActivities"],
|
||||
p["visible"],
|
||||
)
|
||||
for p in api.projects_json
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(api, id, none=False):
|
||||
for value in api.projects_json:
|
||||
if value["id"] == id:
|
||||
for p in api.projects_json:
|
||||
if p["id"] == id:
|
||||
return Project(
|
||||
api,
|
||||
value["id"],
|
||||
value["name"],
|
||||
Customer.get_by_id(api, value["customer"]),
|
||||
value["globalActivities"],
|
||||
p["id"],
|
||||
p["name"],
|
||||
Customer.get_by_id(api, p["customer"]),
|
||||
p["globalActivities"],
|
||||
p["visible"],
|
||||
)
|
||||
if not none:
|
||||
raise NotFound()
|
||||
@ -105,25 +110,31 @@ class Activity(BaseAPI):
|
||||
id: int
|
||||
name: str
|
||||
project: Project
|
||||
visible: bool = field(default=True)
|
||||
|
||||
@staticmethod
|
||||
def list(api):
|
||||
return [
|
||||
Activity(
|
||||
api, a["id"], a["name"], Project.get_by_id(api, a["project"], none=True)
|
||||
api,
|
||||
a["id"],
|
||||
a["name"],
|
||||
Project.get_by_id(api, a["project"], none=True),
|
||||
a["visible"],
|
||||
)
|
||||
for a in api.activities_json
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(api, id, none=False):
|
||||
for value in api.activities_json:
|
||||
if value["id"] == id:
|
||||
for a in api.activities_json:
|
||||
if a["id"] == id:
|
||||
return Activity(
|
||||
api,
|
||||
value["id"],
|
||||
value["name"],
|
||||
Project.get_by_id(api, value["project"]),
|
||||
a["id"],
|
||||
a["name"],
|
||||
Project.get_by_id(api, a["project"]),
|
||||
a["visible"],
|
||||
)
|
||||
if not none:
|
||||
raise NotFound()
|
||||
|
@ -7,7 +7,16 @@ from textual.coordinate import Coordinate
|
||||
from textual.containers import Horizontal, Vertical
|
||||
from textual.events import DescendantBlur
|
||||
from textual.screen import Screen, ModalScreen
|
||||
from textual.widgets import Header, Footer, DataTable, Input, Label, Checkbox, TabbedContent, TabPane
|
||||
from textual.widgets import (
|
||||
Header,
|
||||
Footer,
|
||||
DataTable,
|
||||
Input,
|
||||
Label,
|
||||
Checkbox,
|
||||
TabbedContent,
|
||||
TabPane,
|
||||
)
|
||||
|
||||
from peewee import fn, JOIN
|
||||
|
||||
@ -558,6 +567,11 @@ class HamsterScreen(Screen):
|
||||
yield ActivityList()
|
||||
yield Footer()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.query_one("TabbedContent Tabs").can_focus = False
|
||||
self.query_one("#activities 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()
|
||||
|
@ -15,6 +15,10 @@ from ..db import (
|
||||
from .list import ListPane
|
||||
|
||||
|
||||
class KimaiCustomerList(ListPane):
|
||||
pass
|
||||
|
||||
|
||||
class KimaiProjectList(ListPane):
|
||||
BINDINGS = [
|
||||
("s", "sort", "Sort"),
|
||||
@ -24,9 +28,11 @@ class KimaiProjectList(ListPane):
|
||||
Binding(key="escape", action="cancelfilter", show=False),
|
||||
]
|
||||
|
||||
def _refresh(self, filter_query=None):
|
||||
def _refresh(self):
|
||||
self.table.clear()
|
||||
|
||||
filter_search = self.query_one("#filter #search").value
|
||||
|
||||
projects = (
|
||||
KimaiProject.select(
|
||||
KimaiProject,
|
||||
@ -40,10 +46,10 @@ class KimaiProjectList(ListPane):
|
||||
.group_by(KimaiProject)
|
||||
)
|
||||
|
||||
if filter_query:
|
||||
if filter_search:
|
||||
projects = projects.where(
|
||||
KimaiProject.name.contains(filter_query)
|
||||
| KimaiCustomer.name.contains(filter_query)
|
||||
KimaiProject.name.contains(filter_search)
|
||||
| KimaiCustomer.name.contains(filter_search)
|
||||
)
|
||||
|
||||
self.table.add_rows(
|
||||
@ -54,6 +60,7 @@ class KimaiProjectList(ListPane):
|
||||
project.id,
|
||||
project.name,
|
||||
project.activities_count,
|
||||
project.visible,
|
||||
]
|
||||
for project in projects
|
||||
]
|
||||
@ -69,29 +76,104 @@ class KimaiProjectList(ListPane):
|
||||
self.table = self.query_one(DataTable)
|
||||
self.table.cursor_type = "row"
|
||||
self.columns = self.table.add_columns(
|
||||
"customer id", "customer", "project id", "project", "activities"
|
||||
"customer id", "customer", "project id", "project", "activities", "visible"
|
||||
)
|
||||
# self.sort = (self.columns[1], self.columns[3])
|
||||
self.sort = self.columns[1]
|
||||
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, "None").alias("project_name"),
|
||||
fn.COALESCE(KimaiCustomer.name, "None").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 "",
|
||||
activity.project_name,
|
||||
activity.id,
|
||||
activity.name,
|
||||
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.sort = 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="activities"):
|
||||
with TabPane("Categories", id="categories"):
|
||||
with TabbedContent(initial="projects"):
|
||||
with TabPane("Customers", id="customers"):
|
||||
yield KimaiCustomerList()
|
||||
with TabPane("Projects", id="projects"):
|
||||
yield KimaiProjectList()
|
||||
with TabPane("Activities", id="activities"):
|
||||
yield KimaiProjectList()
|
||||
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()
|
||||
|
@ -22,7 +22,14 @@ def sync() -> None:
|
||||
customers = KimaiAPICustomer.list(api)
|
||||
with db.atomic():
|
||||
KimaiCustomer.insert_many(
|
||||
[{"id": customer.id, "name": customer.name} for customer in customers]
|
||||
[
|
||||
{
|
||||
"id": customer.id,
|
||||
"name": customer.name,
|
||||
"visible": customer.visible,
|
||||
}
|
||||
for customer in customers
|
||||
]
|
||||
).execute()
|
||||
|
||||
projects = KimaiAPIProject.list(api)
|
||||
@ -34,6 +41,7 @@ def sync() -> None:
|
||||
"name": project.name,
|
||||
"customer_id": project.customer.id,
|
||||
"allow_global_activities": project.allow_global_activities,
|
||||
"visible": project.visible,
|
||||
}
|
||||
for project in projects
|
||||
]
|
||||
@ -46,7 +54,8 @@ def sync() -> None:
|
||||
{
|
||||
"id": activity.id,
|
||||
"name": activity.name,
|
||||
"project_id": (activity.project and activity.project.id or None),
|
||||
"project_id": (activity.project.id if activity.project else None),
|
||||
"visible": activity.visible,
|
||||
}
|
||||
for activity in activities
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user