Fuckkk yeah, working mapping-adding

This commit is contained in:
3wc 2023-10-29 21:49:26 +00:00
parent 6b8b4c380e
commit f7edf18391
2 changed files with 236 additions and 9 deletions

View File

@ -1,9 +1,15 @@
from textual import on
from textual.app import App, ComposeResult from textual.app import App, ComposeResult
from textual.binding import Binding from textual.binding import Binding
from textual.widgets import Header, Footer, DataTable, Input from textual.containers import Grid
from textual.events import DescendantBlur
from textual.widgets import Header, Footer, DataTable, Input, Button, Label, Checkbox
from textual.containers import Horizontal, Vertical from textual.containers import Horizontal, Vertical
from textual.coordinate import Coordinate from textual.coordinate import Coordinate
from textual.screen import Screen from textual.reactive import reactive
from textual.screen import Screen, ModalScreen
from textual_autocomplete import AutoComplete, Dropdown, DropdownItem
from peewee import fn, JOIN from peewee import fn, JOIN
@ -66,13 +72,174 @@ class ListScreen(Screen):
self._refresh(event.value) self._refresh(event.value)
class ActivitiesScreen(ListScreen): class ActivityEditScreen(ModalScreen):
BINDINGS = [
("escape", "cancel", "Cancel")
]
def compose(self) -> ComposeResult:
yield Grid(
Label("Are you sure you want to quit?", id="question"),
Button("Quit", variant="error", id="quit"),
Button("Cancel", variant="primary", id="cancel"),
id="dialog",
)
def action_cancel(self):
self.dismiss(None)
class ActivityMappingScreen(ModalScreen):
BINDINGS = [
("ctrl+g", "global", "Toggle global"),
("ctrl+s", "save", "Save"),
("escape", "cancel", "Cancel")
]
customer_id = None
project_id = None
activity_id = None
def __init__(self, category, activity):
self.category = category
self.activity = activity
super().__init__()
@staticmethod
def _filter_dropdowns(options, value):
matches = [c for c in options if value.lower() in c.main.plain.lower()]
return sorted(matches, key=lambda v: v.main.plain.startswith(value.lower()))
def _get_customers(self, input_state):
customers = [
DropdownItem(c.name, str(c.id))
for c in KimaiCustomer.select()
]
return ActivityMappingScreen._filter_dropdowns(customers,
input_state.value)
def _get_projects(self, input_state):
projects = [
DropdownItem(p.name, str(p.id))
for p in KimaiProject.select().where(
KimaiProject.customer_id == self.customer_id
)
]
return ActivityMappingScreen._filter_dropdowns(projects,
input_state.value)
def _get_activities(self, input_state):
activities = KimaiActivity.select()
if self.query_one('#global').value:
activities = activities.where(
KimaiActivity.project_id.is_null(),
)
else:
activities = activities.where(
KimaiActivity.project_id == self.project_id
)
return ActivityMappingScreen._filter_dropdowns([
DropdownItem(a.name, str(a.id))
for a in activities], input_state.value)
def compose(self) -> ComposeResult:
yield Vertical(
Horizontal(
Label(f"Mapping for {self.activity}@{self.category}"),
),
Horizontal(
Label("Customer"),
AutoComplete(
Input(placeholder="Type to search...", id="customer"),
Dropdown(items=self._get_customers),
)
),
Horizontal(
Label("Project"),
AutoComplete(
Input(placeholder="Type to search...", id='project'),
Dropdown(items=self._get_projects),
)
),
Horizontal(
Label("Activity"),
AutoComplete(
Input(placeholder="Type to search...", id='activity'),
Dropdown(items=self._get_activities),
)
),
Horizontal(
Label("Description"),
Input(id='description'),
),
Horizontal(
Label("Tags"),
Input(id='tags'),
),
Horizontal(Checkbox("Global", id='global')),
)
@on(Input.Submitted, '#customer')
def customer_submitted(self, event):
if event.control.parent.dropdown.selected_item is not None:
self.customer_id = str(event.control.parent.dropdown.selected_item.left_meta)
self.query_one('#project').focus()
@on(DescendantBlur, '#customer')
def customer_blur(self, event):
if event.control.parent.dropdown.selected_item is not None:
self.customer_id = str(event.control.parent.dropdown.selected_item.left_meta)
@on(Input.Submitted, '#project')
def project_submitted(self, event):
if event.control.parent.dropdown.selected_item is not None:
self.project_id = str(event.control.parent.dropdown.selected_item.left_meta)
self.query_one('#activity').focus()
@on(DescendantBlur, '#project')
def project_blur(self, event):
if event.control.parent.dropdown.selected_item is not None:
self.project_id = str(event.control.parent.dropdown.selected_item.left_meta)
@on(Input.Submitted, '#activity')
def activity_submitted(self, event):
if event.control.parent.dropdown.selected_item is not None:
self.activity_id = str(event.control.parent.dropdown.selected_item.left_meta)
self.query_one('#activity').focus()
@on(DescendantBlur, '#activity')
def activity_blur(self, event):
if event.control.parent.dropdown.selected_item is not None:
self.activity_id = str(event.control.parent.dropdown.selected_item.left_meta)
def action_global(self):
self.query_one('#global').value = not self.query_one('#global').value
def action_save(self):
self.dismiss({
'kimai_customer_id': self.customer_id,
'kimai_project_id': self.project_id,
'kimai_activity_id': self.activity_id,
'kimai_description': self.query_one('#description').value,
'kimai_tags': self.query_one('#tags').value,
'global': self.query_one('#global').value,
})
def action_cancel(self):
self.dismiss(None)
class ActivityListScreen(ListScreen):
BINDINGS = [ BINDINGS = [
("s", "sort", "Sort"), ("s", "sort", "Sort"),
("r", "refresh", "Refresh"), ("r", "refresh", "Refresh"),
("/", "filter", "Search"), ("/", "filter", "Search"),
("d", "delete", "Delete activity"), ("d", "delete", "Delete"),
("f", "move_facts", "Move facts"), ("f", "move_facts", "Move facts"),
("e", "edit", "Edit"),
("m", "mapping", "Mapping"),
Binding(key="escape", action="cancelfilter", show=False), Binding(key="escape", action="cancelfilter", show=False),
] ]
@ -191,8 +358,37 @@ class ActivitiesScreen(ListScreen):
self._refresh(filter_input.value) self._refresh(filter_input.value)
del self.move_from_activity del self.move_from_activity
def action_edit(self):
def handle_edit(properties):
print(properties)
class CategoriesScreen(ListScreen): self.app.push_screen(ActivityEditScreen(), handle_edit)
def action_mapping(self):
selected_activity = HamsterActivity.select(
HamsterActivity,
fn.COALESCE(HamsterCategory.name, "None").alias("category_name"),
).join(HamsterCategory, JOIN.LEFT_OUTER).where(
HamsterActivity.id == self.table.get_cell_at(
Coordinate(self.table.cursor_coordinate.row, 2),
)
).get()
def handle_mapping(mapping):
if mapping is None:
return
m = HamsterKimaiMapping.create(hamster_activity=selected_activity, **mapping)
m.save()
filter_input = self.query_one("#filter")
self._refresh(filter_input.value)
self.app.push_screen(ActivityMappingScreen(
category=selected_activity.category_name,
activity=selected_activity.name
), handle_mapping)
class CategoryListScreen(ListScreen):
BINDINGS = [ BINDINGS = [
("s", "sort", "Sort"), ("s", "sort", "Sort"),
("r", "refresh", "Refresh"), ("r", "refresh", "Refresh"),
@ -249,7 +445,7 @@ class CategoriesScreen(ListScreen):
self.table.remove_row(row_key) self.table.remove_row(row_key)
class KimaiScreen(ListScreen): class KimaiProjectListScreen(ListScreen):
BINDINGS = [ BINDINGS = [
("s", "sort", "Sort"), ("s", "sort", "Sort"),
("r", "refresh", "Refresh"), ("r", "refresh", "Refresh"),
@ -362,9 +558,9 @@ class HamsterToolsApp(App):
db.init("hamster-testing.db") db.init("hamster-testing.db")
self.MODES = { self.MODES = {
"categories": CategoriesScreen(), "categories": CategoryListScreen(),
"activities": ActivitiesScreen(), "activities": ActivityListScreen(),
"kimai": KimaiScreen(), "kimai": KimaiProjectListScreen(),
} }
super().__init__() super().__init__()

View File

@ -13,3 +13,34 @@ DataTable:focus .datatable--cursor {
#filter { #filter {
display: none; display: none;
} }
ActivityEditScreen, ActivityMappingScreen {
align: center middle;
}
ActivityMappingScreen > Vertical {
padding: 0 1;
width: auto;
height: 30;
border: thick $background 80%;
background: $surface;
}
ActivityMappingScreen Horizontal {
align: left middle;
width: auto;
}
ActivityMappingScreen Label {
padding: 0 1;
width: auto;
border: blank;
}
ActivityMappingScreen AutoComplete {
width: 80;
}
#description, #tags {
width: 30;
}