initial -> things migration
This commit is contained in:
parent
076f43b082
commit
5de9914341
@ -5,6 +5,7 @@ from typing_extensions import Annotated
|
|||||||
from .controller.tasks import add_task
|
from .controller.tasks import add_task
|
||||||
from .db import initialize_db
|
from .db import initialize_db
|
||||||
from .tui.tasks import run as tasks_tui
|
from .tui.tasks import run as tasks_tui
|
||||||
|
from .tui.things import run as things_tui
|
||||||
from .tui.overview import run as overview_tui
|
from .tui.overview import run as overview_tui
|
||||||
from .tui.recurring import run as recurring_tui
|
from .tui.recurring import run as recurring_tui
|
||||||
|
|
||||||
@ -48,6 +49,13 @@ def tasks(
|
|||||||
initialize_db()
|
initialize_db()
|
||||||
tasks_tui(view)
|
tasks_tui(view)
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def things(
|
||||||
|
view: Annotated[str, typer.Option("-v", "--view", help="saved view")] = "default",
|
||||||
|
):
|
||||||
|
initialize_db()
|
||||||
|
things_tui(view)
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def generators():
|
def generators():
|
||||||
|
90
src/tt/controller/things.py
Normal file
90
src/tt/controller/things.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from peewee import fn
|
||||||
|
from peewee import Case, Value
|
||||||
|
from ..db import db, Thing
|
||||||
|
from .. import config
|
||||||
|
|
||||||
|
|
||||||
|
def get_thing(item_id: int) -> Thing:
|
||||||
|
return Thing.get_by_id(item_id)
|
||||||
|
|
||||||
|
|
||||||
|
def add_thing(
|
||||||
|
type: str = "?",
|
||||||
|
**kwargs,
|
||||||
|
) -> Thing:
|
||||||
|
"""
|
||||||
|
Add a new Thing to the database.
|
||||||
|
Returns the created Thing instance.
|
||||||
|
"""
|
||||||
|
with db.atomic():
|
||||||
|
thing = Thing.create(
|
||||||
|
type=type, data=kwargs
|
||||||
|
)
|
||||||
|
return thing
|
||||||
|
|
||||||
|
|
||||||
|
def update_thing(
|
||||||
|
item_id: int,
|
||||||
|
**kwargs,
|
||||||
|
) -> Thing:
|
||||||
|
with db.atomic():
|
||||||
|
query = Thing.update(data=kwargs).where(Thing.id == item_id)
|
||||||
|
query.execute()
|
||||||
|
thing = Thing.get_by_id(item_id)
|
||||||
|
return thing
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_sort_string(sort_string):
|
||||||
|
"""
|
||||||
|
Convert sort string like 'field1,-field2' to peewee order_by expressions.
|
||||||
|
"""
|
||||||
|
sort_expressions = []
|
||||||
|
|
||||||
|
if not sort_string:
|
||||||
|
return sort_expressions
|
||||||
|
|
||||||
|
for field_name in sort_string.split(","):
|
||||||
|
is_desc = field_name.startswith("-")
|
||||||
|
field_name = field_name[1:] if is_desc else field_name
|
||||||
|
|
||||||
|
# TODO: look up field type
|
||||||
|
field_type = "text"
|
||||||
|
|
||||||
|
if field_type == "enum":
|
||||||
|
# TODO: allow dynamic ordering
|
||||||
|
order = config.get_enum(field_name)
|
||||||
|
# CASE statement that maps each status to its position in the order
|
||||||
|
order_case = Case(
|
||||||
|
Thing.data[field_name],
|
||||||
|
[(s, Value(i)) for i, s in enumerate(order)],
|
||||||
|
)
|
||||||
|
sort_expressions.append(order_case.desc() if is_desc else order_case.asc())
|
||||||
|
elif field_type == "date":
|
||||||
|
expr = fn.COALESCE(Thing.data[field_name], datetime(3000, 12, 31))
|
||||||
|
sort_expressions.append(expr.desc() if is_desc else expr)
|
||||||
|
else:
|
||||||
|
field_expr = Thing.data[field_name]
|
||||||
|
sort_expressions.append(field_expr.desc() if is_desc else field_expr)
|
||||||
|
|
||||||
|
return sort_expressions
|
||||||
|
|
||||||
|
|
||||||
|
def get_things(
|
||||||
|
search_text: str | None = None,
|
||||||
|
filters: dict[str, str] | None = None,
|
||||||
|
sort: str = "",
|
||||||
|
) -> list[Thing]:
|
||||||
|
query = Thing.select().where(~Thing.deleted)
|
||||||
|
|
||||||
|
if search_text:
|
||||||
|
# TODO: which fields are searchable?
|
||||||
|
query = query.where(fn.Lower(Thing.data['text']).contains(search_text.lower()))
|
||||||
|
|
||||||
|
for param, val in filters.items():
|
||||||
|
query = query.where(Thing.data[param] == val)
|
||||||
|
|
||||||
|
sort_expressions = _parse_sort_string(sort)
|
||||||
|
query = query.order_by(*sort_expressions)
|
||||||
|
|
||||||
|
return list(query)
|
15
src/tt/db.py
15
src/tt/db.py
@ -9,6 +9,7 @@ from peewee import (
|
|||||||
SqliteDatabase,
|
SqliteDatabase,
|
||||||
TextField,
|
TextField,
|
||||||
)
|
)
|
||||||
|
from playhouse.sqlite_ext import JSONField
|
||||||
|
|
||||||
# This module defines the core data types.
|
# This module defines the core data types.
|
||||||
#
|
#
|
||||||
@ -28,6 +29,18 @@ class BaseModel(Model):
|
|||||||
database = db
|
database = db
|
||||||
|
|
||||||
|
|
||||||
|
class Thing(BaseModel):
|
||||||
|
type = CharField()
|
||||||
|
data = JSONField()
|
||||||
|
created_at = DateTimeField(default=datetime.now)
|
||||||
|
updated_at = DateTimeField(default=datetime.now)
|
||||||
|
deleted = BooleanField(default=False)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
return super(Task, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Task(BaseModel):
|
class Task(BaseModel):
|
||||||
text = TextField()
|
text = TextField()
|
||||||
@ -111,5 +124,5 @@ class TaskGenerator(BaseModel):
|
|||||||
|
|
||||||
def initialize_db():
|
def initialize_db():
|
||||||
db.connect()
|
db.connect()
|
||||||
db.create_tables([Task, SavedSearch, TaskGenerator])
|
db.create_tables([Task, SavedSearch, TaskGenerator, Thing])
|
||||||
db.close()
|
db.close()
|
||||||
|
72
src/tt/tui/things.py
Normal file
72
src/tt/tui/things.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
from ..controller.things import (
|
||||||
|
get_thing,
|
||||||
|
get_things,
|
||||||
|
add_thing,
|
||||||
|
update_thing,
|
||||||
|
)
|
||||||
|
from .editor import TableEditor
|
||||||
|
|
||||||
|
|
||||||
|
class ThingTable(TableEditor):
|
||||||
|
|
||||||
|
# BINDINGS = [
|
||||||
|
# # saved views
|
||||||
|
# ("ctrl+s", "save_view", "save current view"),
|
||||||
|
# ("ctrl+o", "load_view", "load saved view"),
|
||||||
|
# ]
|
||||||
|
|
||||||
|
def __init__(self, default_view="default"):
|
||||||
|
super().__init__("things")
|
||||||
|
self.update_item_callback = update_thing
|
||||||
|
self.add_item_callback = add_thing
|
||||||
|
self.get_item_callback = get_thing
|
||||||
|
|
||||||
|
# def _load_db_view(self, name):
|
||||||
|
# try:
|
||||||
|
# saved = get_saved_view(name)
|
||||||
|
# self.filters = json.loads(saved.filters)
|
||||||
|
# self.sort_string = saved.sort_string
|
||||||
|
# except Exception:
|
||||||
|
# self.notify(f"Could not load {name}")
|
||||||
|
|
||||||
|
# def action_save_view(self):
|
||||||
|
# self._show_input("save-view", "default")
|
||||||
|
|
||||||
|
# def action_load_view(self):
|
||||||
|
# self._show_input("load-view", "")
|
||||||
|
|
||||||
|
# def on_input_submitted(self, event: Input.Submitted):
|
||||||
|
# # Override to add save/load view
|
||||||
|
# if self.mode == "save-view":
|
||||||
|
# save_view(event.value, filters=self.filters, sort_string=self.sort_string)
|
||||||
|
# self._hide_input()
|
||||||
|
# event.prevent_default()
|
||||||
|
# elif self.mode == "load-view":
|
||||||
|
# self._load_view(event.value)
|
||||||
|
# self.refresh_tasks(restore_cursor=False)
|
||||||
|
# self._hide_input()
|
||||||
|
# # if event isn't handled here it will bubble to parent
|
||||||
|
# event.prevent_default()
|
||||||
|
|
||||||
|
def refresh_items(self):
|
||||||
|
items = get_things(
|
||||||
|
self.search_query,
|
||||||
|
filters={},# key: self._filters_to_list(key) for key in self._filters},
|
||||||
|
sort=self.sort_string,
|
||||||
|
)
|
||||||
|
for item in items:
|
||||||
|
self.table.add_row(
|
||||||
|
*self._db_item_to_row(item),
|
||||||
|
key=str(item.id),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _filters_to_list(self, key):
|
||||||
|
filters = self.filters.get(key)
|
||||||
|
if filters:
|
||||||
|
return filters.split(",")
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def run(default_view):
|
||||||
|
app = ThingTable(default_view)
|
||||||
|
app.run()
|
Loading…
Reference in New Issue
Block a user