Source code for items.cli
# SPDX-FileCopyrightText: 2023 Veit Schiele
#
# SPDX-License-Identifier: BSD-3-Clause
"""The module provides a command-line interface for managing items.
You can run the commands using the ``items`` command followed by
the specific subcommand:
.. code-block:: console
items add "My task description" --owner "Veit"
items list
items list --owner "Veit" --state "todo"
items update 1 --owner "Veit" --summary "Update description"
items start 1
items finish 1
items delete 1
items count
items config
items version
If no subcommand is specified, the ``list`` command is executed by default.
"""
import os
import pathlib
from contextlib import contextmanager
from io import StringIO
import rich
import typer
from rich.table import Table
import items
app = typer.Typer(add_completion=False)
[docs]
@app.command()
def version():
"""Return the version of the items application.
Returns:
str: The version string of the items package.
"""
print(items.__version__)
[docs]
@app.command()
def add(summary: list[str], owner: str = typer.Option(None, "-o", "--owner")):
"""Add an item to the database.
Args:
summary (list[str]): The summary of the new item.
owner (str, optional): The owner of the new item. Defaults to None.
"""
summary = " ".join(summary) if summary else None
with items_db() as db:
db.add_item(items.Item(summary, owner, state="todo"))
[docs]
@app.command()
def delete(item_id: int):
"""Remove an item from the database.
Args:
item_id (int): The ID of the item to delete.
Raises:
InvalidItemIdError: If no item with the given ID exists.
"""
with items_db() as db:
try:
db.delete_item(item_id)
except items.InvalidItemIdError:
print(f"Error: Invalid item id {item_id}")
[docs]
@app.command("list")
def list_items(
owner: str = typer.Option(None, "-o", "--owner"),
state: str = typer.Option(None, "-s", "--state"),
):
"""List items in the database, optionally filtered by owner and/or state.
Args:
owner (str, optional): Filter items by this owner. Defaults to None.
state (str, optional): Filter items by this state. Defaults to None.
"""
with items_db() as db:
the_items = db.list_items(owner=owner, state=state)
table = Table(box=rich.box.SIMPLE)
table.add_column("ID")
table.add_column("state")
table.add_column("owner")
table.add_column("summary")
for t in the_items:
owner = "" if t.owner is None else t.owner
table.add_row(str(t.id), t.state, owner, t.summary)
out = StringIO()
rich.print(table, file=out)
print(out.getvalue())
[docs]
@app.command()
def update(
item_id: int,
owner: str = typer.Option(None, "-o", "--owner"),
summary: list[str] = typer.Option(None, "-s", "--summary"),
):
"""Update an item in the database.
Args:
item_id (int): The ID of the item to update.
owner (str, optional): The new owner of the item. Defaults to None.
summary (list[str], optional): The new summary of the item. Defaults to
None.
Raises:
InvalidItemIdError: If no item with the given ID exists.
"""
summary = " ".join(summary) if summary else None
with items_db() as db:
try:
db.update_item(item_id, items.Item(summary, owner, state=None))
except items.InvalidItemIdError:
print(f"Error: Invalid item id {item_id}.")
[docs]
@app.command()
def start(item_id: int):
"""Set an item's state to 'in progress'.
Args:
item_id (int): The ID of the item to update.
Raises:
InvalidItemIdError: If no item with the given ID exists.
"""
with items_db() as db:
try:
db.start(item_id)
except items.InvalidItemIdError:
print(f"Error: Invalid item id {item_id}.")
[docs]
@app.command()
def finish(item_id: int):
"""Set an item's state to 'done'.
Args:
item_id (int): The ID of the item to update.
Raises:
InvalidItemIdError: If no item with the given ID exists.
"""
with items_db() as db:
try:
db.finish(item_id)
except items.InvalidItemIdError:
print(f"Error: Invalid item id {item_id}.")
[docs]
@app.command()
def config():
"""Return the path to the Items database.
Returns:
str: Path to the Items database.
"""
with items_db() as db:
print(db.path())
[docs]
@app.command()
def count():
"""Return the number of items in the database.
Returns:
int: Number of items in the database.
"""
with items_db() as db:
print(db.count())
@app.callback(invoke_without_command=True)
def main(ctx: typer.Context):
"""Items is a small command line task tracking application."""
if ctx.invoked_subcommand is None:
list_items(owner=None, state=None)
[docs]
def get_path():
"""Determine the path to the database.
The path is determined from the environment variable ITEMS_DB_DIR.
If it is not defined, $HOME/items_db is used.
Returns:
pathlib.Path: Path to the database directory.
"""
db_path_env = os.getenv("ITEMS_DB_DIR", "")
if db_path_env:
db_path = pathlib.Path(db_path_env)
else:
db_path = pathlib.Path.home() / "items_db"
return db_path
[docs]
@contextmanager
def items_db():
"""Open and close the database connection.
Yields:
ItemsDB: An ItemsDB instance connected to the database.
"""
db_path = get_path()
db = items.ItemsDB(db_path)
yield db
db.close()