Compare commits

...

11 commits

Author SHA1 Message Date
bronsen
98ebb69fa4 release: bump version to 0.5.0
All checks were successful
ci/woodpecker/push/workflow Pipeline was successful
ci/woodpecker/tag/workflow Pipeline was successful
2026-02-22 16:48:31 +01:00
bronsen
f4f2e98c94 tests: update snapshot after changing placeholder text for input field 2026-02-22 16:45:27 +01:00
bronsen
5d10f25c39 main: absorb former tui module's responsibilities 2026-02-22 16:43:07 +01:00
bronsen
db47501cd0 models: we can now store TeilchenCreate data in the database thus creating a new Teilchen
closes #3
2026-02-22 16:41:42 +01:00
bronsen
1b565e5a4e delete tui module
Its code is in main now
2026-02-22 13:04:20 +01:00
bronsen
dd42e6119c just: congregate releated recipes; reuse recipes in "release" 2026-02-22 12:17:34 +01:00
bronsen
84f488f06a prek: we don't have yamls to check 2026-02-22 12:17:12 +01:00
bronsen
3e898d1771 doc: update notes about releasing since the script now works for the most part 2026-02-22 12:09:53 +01:00
bronsen
13239d6f73 prek: never use colors
Otherwise some parts become unreadable on dark backgrounded terminals
2026-02-22 12:09:06 +01:00
bronsen
90388ed48c prek: clean up config file 2026-02-22 12:08:17 +01:00
bronsen
387a6f4e34 just: assign arguments to local fish var, so we don't mix interpolation from just and fish 2026-02-22 11:34:00 +01:00
10 changed files with 216 additions and 209 deletions

View file

@ -108,10 +108,8 @@ Please don't: this is my little human-created thing.
Where `segment` is one of: major, minor, patch, stable, alpha, beta, rc,
post, dev
* commit
* push commits
* use lazygit to create the new version tag
* remember to prefix it with "v"
* push the tags, using lazygit
* When we really really want to release:
* `just release {Release Name can be several words}`
----

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before After
Before After

View file

@ -9,14 +9,6 @@ default:
setup:
uv sync
# builds a package
build:
uv build --wheel --clear
# upload to Package Registry
upload: build
uv run twine upload --repository code.cbo --config-file ~/.config/pypirc dist/*.whl
# console to observe log messages
console:
uv run textual console
@ -29,16 +21,16 @@ run-console:
run:
uv run python -m {{ the_app }}
# export dependencies into requirements files
exports-deps:
uv export {{ uv_export_options }} --output-file requirements.txt
uv export {{ uv_export_options }} --output-file requirements.dev.txt --only-dev
# Update dependencies to new versions
update-deps:
uv lock --upgrade
uv sync
# export dependencies into requirements files
exports-deps:
uv export {{ uv_export_options }} --output-file requirements.txt
uv export {{ uv_export_options }} --output-file requirements.dev.txt --only-dev
# Run tests, ARGS are passed-through to pytest
test *ARGS:
uv run pytest tests.py -m "not final" {{ ARGS }}
@ -50,26 +42,18 @@ alltests *ARGS:
coverage:
uv run pytest tests.py --cov=src/ --cov-report term --cov-report xml --cov-report html --cov-config pyproject.toml
# run python and markdown
# lint python, markdown, wheel file names, prek config
lint:
uv run ruff check .
markdownlint-cli2 .
uv run twine check --strict dist/*
prek validate-config prek.toml
# pretend we are CI
ci: lint
prek run --all-files
# woodpecker-cli exec "whatever"
[private]
bump segment:
uv version --bump {{ segment }}
[private]
release segment *release_name:
release *release_name:
#!/usr/bin/env fish
set -l release_name {{ release_name }}
test -f releasenotes.md; and set -l body "$(cat releasenotes.md)"; or set -l body ""
set -l tag (uv version --short --output-format text --bump {{ segment }})
set -l tag (uv version --short --output-format text)
git add pyproject.toml uv.lock
git commit -m "Release version $tag"
@ -78,7 +62,15 @@ release segment *release_name:
git push
git push origin tag "v$tag"
just build
fj release create "v$tag: $release_name" --tag "v$tag" --attach dist/*.whl --body "$body"
# just upload
# builds a package
build:
uv build --wheel --clear
fj release create "v$tag: $release_name" --prerelease --tag "v$tag" --attach dist/*.whl --body "$body"
# uv run twine upload --config-file ~/.config/pypirc --repository code.cbo dist/*.whl
# upload to Package Registry
upload:
uv run twine upload --repository code.cbo --config-file ~/.config/pypirc dist/*.whl

View file

@ -1,6 +1,7 @@
[env]
'_'.python.venv = { path = ".venv", create = true }
DATABASE_URL = "sqlite:///database.db"
PREK_COLOR = "never"
[tools]
difftastic = "latest"

View file

@ -1,5 +1,7 @@
#:tombi schema.strict = false
# see also: https://prek.j178.dev/builtin/
exclude = { glob = ["__snapshots__/**", "__pycache__", "dist/**"] }
[[repos]]
@ -7,19 +9,10 @@ repo = "builtin"
hooks = [
{ id = "check-case-conflict" },
{ id = "check-merge-conflict" },
{ id = "check-json" },
{ id = "check-symlinks" },
{ id = "check-toml" },
{ id = "check-yaml", args = [
# "--allow-multiple-documents", #https://prek.j178.dev/builtin/#check-yaml
] },
{ id = "end-of-file-fixer" },
{ id = "mixed-line-ending", args = [
"--fix=lf", # https://prek.j178.dev/builtin/#mixed-line-ending
] },
{ id = "trailing-whitespace", args = [
"--markdown-linebreak-ext=md", # https://prek.j178.dev/builtin/#trailing-whitespace
] },
{ id = "mixed-line-ending", args = [ "--fix=lf" ] },
{ id = "trailing-whitespace", args = [ "--markdown-linebreak-ext=md" ] },
]
[[repos]]

View file

@ -1,6 +1,6 @@
[project]
name = "teilchensammler-cli"
version = "0.4.9"
version = "0.5.0"
description = "Build up and maintain an inventory of electronics parts and tools."
readme = "README.md"
requires-python = ">=3.14,<4.0"

View file

@ -4,11 +4,16 @@ This is where the application is implemented.
import logging
from textual.app import App
from textual import on
from textual.app import App, ComposeResult
from textual.containers import HorizontalGroup
from textual.logging import TextualHandler
from textual.screen import Screen
from textual.widget import Widget
from textual.widgets import Button, DataTable, Footer, Header, Input, Static
from .database import create_db_and_tables
from .tui import AddInventoryScreen
from .models import add_to_database, load_initial_data, make_teilchen_input
logging.basicConfig(
level="NOTSET",
@ -16,6 +21,8 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
TEILCHEN_DATA_HEADER = "pk Name Description Number Tags".split()
class SammlerApp(App):
async def on_mount(self) -> None:
@ -23,6 +30,68 @@ class SammlerApp(App):
self.push_screen(AddInventoryScreen())
class SearchBar(Static):
DEFAULT_CSS = """
#teilchen-input {
width: 4fr;
}
#button-search, #button-add {
width: 1fr;
}
"""
def compose(self) -> ComposeResult:
with HorizontalGroup(id="search-bar-widget"):
yield Input(
placeholder='This is a name. "This is the description" #these #are #tags',
tooltip=(
"Enter a name followed by a period, then a description "
'enclosed in double quotes ("). You should use #hashtags for any meta information. '
"Hashtags can be placed anywhere."
),
id="teilchen-input",
type="text",
)
yield Button("Add", variant="success", classes="search-bar-buttons", id="button-add")
yield Button(
"Search",
variant="default",
classes="search-bar-buttons",
id="button-search",
)
@on(Input.Submitted)
async def parse_input(self, event: Input.Submitted) -> None:
if not (tc := await make_teilchen_input(event.value)):
return
event.input.value = ""
teilchen = await add_to_database(tc)
self.screen.query_one(DataTable).add_row(
teilchen.id, teilchen.name, teilchen.description, teilchen.number, teilchen.tags
)
class SearchResults(Widget):
def compose(self) -> ComposeResult:
yield DataTable(id="table-search-result", cursor_type="row", zebra_stripes=True)
async def on_mount(self) -> None:
table: DataTable = self.query_one(DataTable)
table.add_columns(*TEILCHEN_DATA_HEADER)
table.add_rows(await load_initial_data())
class AddInventoryScreen(Screen):
def compose(self) -> ComposeResult:
yield Header()
yield SearchBar()
yield SearchResults()
yield Footer()
# so we can import it without running it
app = SammlerApp()

View file

@ -3,12 +3,14 @@ import uuid
from sqlmodel import (
Field,
SQLModel,
Session,
select,
Sequence,
Session,
SQLModel,
select,
)
from .database import engine
logger = logging.getLogger(__name__)
@ -35,6 +37,7 @@ async def make_teilchen_input(text: str) -> TeilchenCreate | None:
Returns `None` otherwise.
"""
import re
from natsort import natsorted
text = text.strip()
@ -88,3 +91,13 @@ async def load_initial_data() -> Sequence[Teilchen]:
) # ty:ignore[no-matching-overload]
all_teilchen = session.exec(statement).all()
return all_teilchen
async def add_to_database(tc: TeilchenCreate) -> Teilchen:
with Session(engine) as session:
teilchen = Teilchen.model_validate(tc)
session.add(teilchen)
session.commit()
session.refresh(teilchen)
return teilchen

View file

@ -1,59 +0,0 @@
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
TEILCHEN_DATA_HEADER = "pk Name Description Number Tags".split()
class SearchBar(Static):
DEFAULT_CSS = """
#teilchen-input {
width: 4fr;
}
#button-search, #button-add {
width: 1fr;
}
"""
def compose(self) -> ComposeResult:
with HorizontalGroup(id="search-bar-widget"):
yield Input(
placeholder='This is a name. "This is the description" #these #are #tags',
tooltip=(
"Enter a name followed by a period, then a description "
'enclosed in double quotes ("). You should use #hashtags for any meta information. '
"Hashtags can be placed anywhere."
),
id="teilchen-input",
type="text",
)
yield Button("Add", variant="success", classes="search-bar-buttons", id="button-add")
yield Button(
"Search",
variant="default",
classes="search-bar-buttons",
id="button-search",
)
class SearchResults(Widget):
def compose(self) -> ComposeResult:
yield DataTable(id="table-search-result", cursor_type="row", zebra_stripes=True)
async def on_mount(self) -> None:
table: DataTable = self.query_one(DataTable)
table.add_columns(*TEILCHEN_DATA_HEADER)
table.add_rows(await load_initial_data())
class AddInventoryScreen(Screen):
def compose(self) -> ComposeResult:
yield Header()
yield SearchBar()
yield SearchResults()
yield Footer()

2
uv.lock generated
View file

@ -1153,7 +1153,7 @@ wheels = [
[[package]]
name = "teilchensammler-cli"
version = "0.4.9"
version = "0.5.0"
source = { editable = "." }
dependencies = [
{ name = "ciso8601" },