diff --git a/README.md b/README.md index 1915809..5b14093 100644 --- a/README.md +++ b/README.md @@ -108,8 +108,10 @@ 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 -* When we really really want to release: - * `just release {Release Name can be several words}` +* push commits +* use lazygit to create the new version tag + * remember to prefix it with "v" +* push the tags, using lazygit ---- diff --git a/__snapshots__/tests/test_initial_layout.raw b/__snapshots__/tests/test_initial_layout.raw index ab8578c..7c612d4 100644 --- a/__snapshots__/tests/test_initial_layout.raw +++ b/__snapshots__/tests/test_initial_layout.raw @@ -19,208 +19,208 @@ font-weight: 700; } - .terminal-2753228840-matrix { + .terminal-1669918775-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2753228840-title { + .terminal-1669918775-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2753228840-r1 { fill: #c5c8c6 } -.terminal-2753228840-r2 { fill: #e0e0e0 } -.terminal-2753228840-r3 { fill: #121212 } -.terminal-2753228840-r4 { fill: #0178d4 } -.terminal-2753228840-r5 { fill: #7ae998 } -.terminal-2753228840-r6 { fill: #2d2d2d } -.terminal-2753228840-r7 { fill: #797979 } -.terminal-2753228840-r8 { fill: #0a180e;font-weight: bold } -.terminal-2753228840-r9 { fill: #e0e0e0;font-weight: bold } -.terminal-2753228840-r10 { fill: #008139 } -.terminal-2753228840-r11 { fill: #0d0d0d } -.terminal-2753228840-r12 { fill: #000000 } -.terminal-2753228840-r13 { fill: #495259 } -.terminal-2753228840-r14 { fill: #ffa62b;font-weight: bold } + .terminal-1669918775-r1 { fill: #c5c8c6 } +.terminal-1669918775-r2 { fill: #e0e0e0 } +.terminal-1669918775-r3 { fill: #121212 } +.terminal-1669918775-r4 { fill: #0178d4 } +.terminal-1669918775-r5 { fill: #7ae998 } +.terminal-1669918775-r6 { fill: #2d2d2d } +.terminal-1669918775-r7 { fill: #797979 } +.terminal-1669918775-r8 { fill: #0a180e;font-weight: bold } +.terminal-1669918775-r9 { fill: #e0e0e0;font-weight: bold } +.terminal-1669918775-r10 { fill: #008139 } +.terminal-1669918775-r11 { fill: #0d0d0d } +.terminal-1669918775-r12 { fill: #000000 } +.terminal-1669918775-r13 { fill: #495259 } +.terminal-1669918775-r14 { fill: #ffa62b;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - SammlerApp + SammlerApp - - - - SammlerApp -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -This is a name. "This is the description" #these #are #tags Add  Search  -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - pk                                    Name   Description   Number  Tags         - 019c5d37-d43b-72d0-a4dd-b4816f4c5b3a  name0  description0  9001    tag0 tag0-0  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -▆▆ - -^p palette + + + + SammlerApp +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +Enter Teilchen information: name, description, #tags Add  Search  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + pk                                    Name   Description   Number  Tags         + 019c5d37-d43b-72d0-a4dd-b4816f4c5b3a  name0  description0  9001    tag0 tag0-0  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +▆▆ + +^p palette diff --git a/justfile b/justfile index 0c8c3d7..4a25b54 100644 --- a/justfile +++ b/justfile @@ -9,6 +9,14 @@ 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 @@ -21,16 +29,16 @@ run-console: run: uv run python -m {{ the_app }} -# 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 +# Update dependencies to new versions +update-deps: + uv lock --upgrade + uv sync + # Run tests, ARGS are passed-through to pytest test *ARGS: uv run pytest tests.py -m "not final" {{ ARGS }} @@ -42,18 +50,26 @@ alltests *ARGS: coverage: uv run pytest tests.py --cov=src/ --cov-report term --cov-report xml --cov-report html --cov-config pyproject.toml -# lint python, markdown, wheel file names, prek config +# run python and markdown lint: uv run ruff check . markdownlint-cli2 . uv run twine check --strict dist/* - prek validate-config prek.toml -release *release_name: +# 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: #!/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) + set -l tag (uv version --short --output-format text --bump {{ segment }}) git add pyproject.toml uv.lock git commit -m "Release version $tag" @@ -62,15 +78,7 @@ release *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 -# upload to Package Registry -upload: - uv run twine upload --repository code.cbo --config-file ~/.config/pypirc dist/*.whl + 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 diff --git a/mise.toml b/mise.toml index cc420a6..ca3c768 100644 --- a/mise.toml +++ b/mise.toml @@ -1,7 +1,6 @@ [env] '_'.python.venv = { path = ".venv", create = true } DATABASE_URL = "sqlite:///database.db" -PREK_COLOR = "never" [tools] difftastic = "latest" diff --git a/prek.toml b/prek.toml index e22ef62..083a1dc 100644 --- a/prek.toml +++ b/prek.toml @@ -1,7 +1,5 @@ #:tombi schema.strict = false -# see also: https://prek.j178.dev/builtin/ - exclude = { glob = ["__snapshots__/**", "__pycache__", "dist/**"] } [[repos]] @@ -9,10 +7,19 @@ 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" ] }, - { id = "trailing-whitespace", args = [ "--markdown-linebreak-ext=md" ] }, + { 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 + ] }, ] [[repos]] diff --git a/pyproject.toml b/pyproject.toml index 6356117..2fd07df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "teilchensammler-cli" -version = "0.5.0" +version = "0.4.9" description = "Build up and maintain an inventory of electronics parts and tools." readme = "README.md" requires-python = ">=3.14,<4.0" diff --git a/src/teilchensammler_cli/main.py b/src/teilchensammler_cli/main.py index 42feda2..c89c61a 100644 --- a/src/teilchensammler_cli/main.py +++ b/src/teilchensammler_cli/main.py @@ -4,16 +4,11 @@ This is where the application is implemented. import logging -from textual import on -from textual.app import App, ComposeResult -from textual.containers import HorizontalGroup +from textual.app import App 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 .models import add_to_database, load_initial_data, make_teilchen_input +from .tui import AddInventoryScreen logging.basicConfig( level="NOTSET", @@ -21,8 +16,6 @@ logging.basicConfig( ) logger = logging.getLogger(__name__) -TEILCHEN_DATA_HEADER = "pk Name Description Number Tags".split() - class SammlerApp(App): async def on_mount(self) -> None: @@ -30,68 +23,6 @@ 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() diff --git a/src/teilchensammler_cli/models.py b/src/teilchensammler_cli/models.py index bca6c42..77619a8 100644 --- a/src/teilchensammler_cli/models.py +++ b/src/teilchensammler_cli/models.py @@ -3,14 +3,12 @@ import uuid from sqlmodel import ( Field, - Sequence, - Session, SQLModel, + Session, select, + Sequence, ) -from .database import engine - logger = logging.getLogger(__name__) @@ -37,7 +35,6 @@ async def make_teilchen_input(text: str) -> TeilchenCreate | None: Returns `None` otherwise. """ import re - from natsort import natsorted text = text.strip() @@ -91,13 +88,3 @@ 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 diff --git a/src/teilchensammler_cli/tui.py b/src/teilchensammler_cli/tui.py new file mode 100644 index 0000000..fd5cd32 --- /dev/null +++ b/src/teilchensammler_cli/tui.py @@ -0,0 +1,59 @@ +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() diff --git a/uv.lock b/uv.lock index 0c0c8bc..e6597d8 100644 --- a/uv.lock +++ b/uv.lock @@ -1153,7 +1153,7 @@ wheels = [ [[package]] name = "teilchensammler-cli" -version = "0.5.0" +version = "0.4.9" source = { editable = "." } dependencies = [ { name = "ciso8601" },