Compare commits

..

No commits in common. "cf192c7d021972fac45035494f697707e6574cc2" and "444839b8e651eb47b925a92ea8fe0980be0fdcd3" have entirely different histories.

8 changed files with 405 additions and 578 deletions

View file

@ -1,7 +0,0 @@
[env]
'_'.python.venv = { path = ".venv", create = true }
[tools]
python = "3.14"
ruff = "latest"
uv = "latest"

View file

@ -49,7 +49,6 @@ teilchensammler-cli = "teilchensammler_cli.main:main"
[dependency-groups]
dev = [
"ipython>=9.10.0",
"pytest>=9.0.1",
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",

View file

@ -1,6 +1,6 @@
from sqlmodel import SQLModel, create_engine
sqlite_url = "sqlite:///:memory:" # TODO: get url from environment
sqlite_url = "sqlite:///:memory:"
engine = create_engine(sqlite_url, echo=True)

View file

@ -19,6 +19,13 @@ logging.basicConfig(
logger = logging.getLogger(__name__)
class SammlerApp(App[None]):
async def on_mount(self) -> None:
create_db_and_tables()
try_this()
_ = self.push_screen(AddInventoryScreen())
FAKE_INPUT = """
Ein Teilchen. #Tag03 #tag12 "Dieses Teilchen ist nur zum testen" #tag1 #tag2
""".strip(" \n")
@ -38,17 +45,11 @@ def try_this():
logger.info(f"{db_teilchen=}")
class SammlerApp(App[None]):
async def on_mount(self) -> None:
create_db_and_tables()
try_this()
_ = self.push_screen(AddInventoryScreen())
app = SammlerApp()
def main() -> None:
# app = SammlerApp()
app.run()

View file

@ -1,6 +1,8 @@
import logging
import re
import uuid
from natsort import natsorted
from sqlmodel import (
Field,
SQLModel,
@ -9,25 +11,24 @@ from sqlmodel import (
logger = logging.getLogger(__name__)
TeilchenDatum = tuple[int, str, str, str, str]
class TeilchenBase(SQLModel):
text: str
class TeilchenCreate(SQLModel):
class TeilchenCreate(TeilchenBase):
description: str | None
name: str = Field(index=True)
number: int = Field(default=1)
text: str
tags: str | None
text: str # The original input as entered by the user
class Teilchen(TeilchenCreate, table=True):
id: uuid.UUID = Field(default_factory=uuid.uuid7, primary_key=True) # ty:ignore[unresolved-attribute]
id: uuid.UUID = Field(default_factory=uuid.uuid7, primary_key=True)
async def make_teilchen_input(text: str) -> TeilchenCreate | None:
import re
from natsort import natsorted
def make_teilchen_input(text: str) -> TeilchenCreate | None:
if not text:
logger.error("Empty text.")
return None
@ -40,14 +41,13 @@ async def make_teilchen_input(text: str) -> TeilchenCreate | None:
description_start = text.find('"', name_end + 1)
description_end = text.find('"', description_start + 1)
if description_end > description_start:
description = text[description_start:description_end]
else:
description = ""
if not description:
logger.warning("Could not extract description.")
tags = re.findall(r"#\w+", text.lower())
if not tags:
logger.info("No tags found in text.")
logger.warning("No tags found in text")
return TeilchenCreate(
name=name,
@ -55,14 +55,3 @@ async def make_teilchen_input(text: str) -> TeilchenCreate | None:
tags=" ".join(natsorted(tags)),
text=text,
)
async def load_initial_data() -> list[TeilchenDatum]:
return [
(0, "Name0", "Description0", "9000", "#tag0 #tag00 #tag000"),
(1, "Name1", "Description1", "9001", "#tag1 #tag11 #tag111"),
(2, "Name2", "Description2", "9002", "#tag2 #tag22 #tag222"),
(3, "Name3", "Description3", "9003", "#tag3 #tag33 #tag333"),
(4, "Name4", "Description4", "9004", "#tag4 #tag44 #tag444"),
(5, "Name5", "Description5", "9005", "#tag5 #tag55 #tag555"),
]

View file

@ -1,15 +1,13 @@
from typing import Literal, final, override
from textual.app import ComposeResult
from textual.containers import HorizontalGroup
from textual.screen import Screen
from textual.widget import Widget
from textual.widgets import Button, DataTable, Footer, Header, Input, Static
from .models import load_initial_data
FAKE_DATA_HEADER = "pk Name Description Number Tags"
from textual.widgets import DataTable, Footer, Header, Static, Input, Button
@final
class SearchBar(Static):
DEFAULT_CSS = """
#teilchen-input {
@ -20,6 +18,7 @@ class SearchBar(Static):
}
"""
@override
def compose(self) -> ComposeResult:
with HorizontalGroup(id="search-bar-widget"):
yield Input(
@ -41,17 +40,41 @@ class SearchBar(Static):
)
TeilchenDatum = tuple[int, str, str, str, str]
TeilchenHeader = tuple[
Literal["pk"],
Literal["Name"],
Literal["Description"],
Literal["Number"],
Literal["Tags"],
]
FAKE_DATA: list[TeilchenHeader | TeilchenDatum] = [
("pk", "Name", "Description", "Number", "Tags"),
(0, "Name0", "Description0", "9000", "#tag0 #tag00 #tag000"),
(1, "Name1", "Description1", "9001", "#tag1 #tag11 #tag111"),
(2, "Name2", "Description2", "9002", "#tag2 #tag22 #tag222"),
(3, "Name3", "Description3", "9003", "#tag3 #tag33 #tag333"),
(4, "Name4", "Description4", "9004", "#tag4 #tag44 #tag444"),
(5, "Name5", "Description5", "9005", "#tag5 #tag55 #tag555"),
]
@final
class SearchResults(Widget):
@override
def compose(self) -> ComposeResult:
yield DataTable(id="table-search-result", cursor_type="row", zebra_stripes=True)
async def on_mount(self) -> None:
table: DataTable[None] = self.query_one(DataTable[None])
_ = table.add_columns(FAKE_DATA_HEADER)
_ = table.add_rows(await load_initial_data()) # ty:ignore[invalid-argument-type]
_ = table.add_columns(*FAKE_DATA[0]) # pyright: ignore[reportArgumentType]
_ = table.add_rows(FAKE_DATA[1:]) # pyright: ignore[reportArgumentType]
class AddInventoryScreen(Screen[None]):
@override
def compose(self) -> ComposeResult:
yield Header()
yield SearchBar()

View file

@ -1,4 +1,2 @@
def test_initial_layout(snap_compare): # pyright: ignore[reportMissingParameterType, reportUnknownParameterType]
from teilchensammler_cli.main import app
assert snap_compare(app, terminal_size=(130, 40))
assert snap_compare("src/teilchensammler_cli/main.py", terminal_size=(130, 40))

878
uv.lock generated

File diff suppressed because it is too large Load diff