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, Where `segment` is one of: major, minor, patch, stable, alpha, beta, rc,
post, dev post, dev
* commit * commit
* push commits * When we really really want to release:
* use lazygit to create the new version tag * `just release {Release Name can be several words}`
* remember to prefix it with "v"
* push the tags, using lazygit
---- ----

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: setup:
uv sync 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 to observe log messages
console: console:
uv run textual console uv run textual console
@ -29,16 +21,16 @@ run-console:
run: run:
uv run python -m {{ the_app }} 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 dependencies to new versions
update-deps: update-deps:
uv lock --upgrade uv lock --upgrade
uv sync 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 # Run tests, ARGS are passed-through to pytest
test *ARGS: test *ARGS:
uv run pytest tests.py -m "not final" {{ ARGS }} uv run pytest tests.py -m "not final" {{ ARGS }}
@ -50,26 +42,18 @@ alltests *ARGS:
coverage: coverage:
uv run pytest tests.py --cov=src/ --cov-report term --cov-report xml --cov-report html --cov-config pyproject.toml 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: lint:
uv run ruff check . uv run ruff check .
markdownlint-cli2 . markdownlint-cli2 .
uv run twine check --strict dist/* uv run twine check --strict dist/*
prek validate-config prek.toml
# pretend we are CI release *release_name:
ci: lint
prek run --all-files
# woodpecker-cli exec "whatever"
[private]
bump segment:
uv version --bump {{ segment }}
[private]
release segment *release_name:
#!/usr/bin/env fish #!/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 "" 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 add pyproject.toml uv.lock
git commit -m "Release version $tag" git commit -m "Release version $tag"
@ -78,7 +62,15 @@ release segment *release_name:
git push git push
git push origin tag "v$tag" 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 uv build --wheel --clear
fj release create "v$tag: $release_name" --prerelease --tag "v$tag" --attach dist/*.whl --body "$body" # upload to Package Registry
# uv run twine upload --config-file ~/.config/pypirc --repository code.cbo dist/*.whl upload:
uv run twine upload --repository code.cbo --config-file ~/.config/pypirc dist/*.whl

View file

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

View file

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

View file

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

View file

@ -4,11 +4,16 @@ This is where the application is implemented.
import logging 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.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 .database import create_db_and_tables
from .tui import AddInventoryScreen from .models import add_to_database, load_initial_data, make_teilchen_input
logging.basicConfig( logging.basicConfig(
level="NOTSET", level="NOTSET",
@ -16,6 +21,8 @@ logging.basicConfig(
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
TEILCHEN_DATA_HEADER = "pk Name Description Number Tags".split()
class SammlerApp(App): class SammlerApp(App):
async def on_mount(self) -> None: async def on_mount(self) -> None:
@ -23,6 +30,68 @@ class SammlerApp(App):
self.push_screen(AddInventoryScreen()) 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 # so we can import it without running it
app = SammlerApp() app = SammlerApp()

View file

@ -3,12 +3,14 @@ import uuid
from sqlmodel import ( from sqlmodel import (
Field, Field,
SQLModel,
Session,
select,
Sequence, Sequence,
Session,
SQLModel,
select,
) )
from .database import engine
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -35,6 +37,7 @@ async def make_teilchen_input(text: str) -> TeilchenCreate | None:
Returns `None` otherwise. Returns `None` otherwise.
""" """
import re import re
from natsort import natsorted from natsort import natsorted
text = text.strip() text = text.strip()
@ -88,3 +91,13 @@ async def load_initial_data() -> Sequence[Teilchen]:
) # ty:ignore[no-matching-overload] ) # ty:ignore[no-matching-overload]
all_teilchen = session.exec(statement).all() all_teilchen = session.exec(statement).all()
return all_teilchen 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]] [[package]]
name = "teilchensammler-cli" name = "teilchensammler-cli"
version = "0.4.9" version = "0.5.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "ciso8601" }, { name = "ciso8601" },