# Order Query Tool Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build a Python CLI tool that queries Azure Table Storage for orders by userId and displays results in a Textual TUI. **Architecture:** Single-module app (`order_query/main.py`) with config loading from `~/.order-query/config.toml`, Azure Table Storage queries, and a 3-screen Textual TUI (input → list → detail). **Tech Stack:** Python 3.13+, uv, azure-data-tables, textual, tomllib (stdlib) --- ## File Structure | File | Responsibility | |------|---------------| | `pyproject.toml` | Project metadata, dependencies, `[project.scripts]` entry point | | `order_query/__init__.py` | Package marker (empty) | | `order_query/main.py` | All logic: config, Azure client, Textual TUI app | --- ### Task 1: Scaffold project with uv - [ ] **Step 1: Initialize uv project** Run: `cd /Users/tech/workspace/n3-world/handy-tools/order-query && uv init --name order-query --python 3.13` - [ ] **Step 2: Update pyproject.toml with dependencies and entry point** Replace the generated `pyproject.toml` with: ```toml [project] name = "order-query" version = "0.1.0" description = "Query order details from Azure Table Storage" requires-python = ">=3.11" dependencies = [ "azure-data-tables>=12.9.0", "textual>=3.0.0", ] [project.scripts] order-query = "order_query.main:main" ``` - [ ] **Step 3: Install dependencies** Run: `uv sync` Expected: dependencies installed successfully - [ ] **Step 4: Create package directory and init file** ```bash mkdir -p order_query touch order_query/__init__.py ``` - [ ] **Step 5: Commit** ```bash git add pyproject.toml uv.lock order_query/__init__.py git commit -m "chore: scaffold uv project with dependencies" ``` --- ### Task 2: Implement config loading **Files:** - Modify: `order_query/main.py` - [ ] **Step 1: Write config module in main.py** ```python import tomllib from pathlib import Path from dataclasses import dataclass CONFIG_DIR = Path.home() / ".order-query" CONFIG_FILE = CONFIG_DIR / "config.toml" @dataclass class Config: connection_string: str table_name: str = "order" def load_config() -> Config: if not CONFIG_FILE.exists(): CONFIG_DIR.mkdir(parents=True, exist_ok=True) CONFIG_FILE.write_text( '# Order Query Tool Configuration\n' 'connection_string = "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;TableEndpoint=https://..."\n' 'table_name = "order"\n' ) raise SystemExit( f"Config file created at {CONFIG_FILE}\n" f"Please edit it with your Azure Storage connection string and run again." ) with open(CONFIG_FILE, "rb") as f: data = tomllib.load(f) return Config( connection_string=data["connection_string"], table_name=data.get("table_name", "order"), ) ``` - [ ] **Step 2: Test config auto-creation by running temporarily** Add a temporary `if __name__ == "__main__":` block to call `load_config()` and verify file is created at `~/.order-query/config.toml`. - [ ] **Step 3: Commit** ```bash git add order_query/main.py git commit -m "feat: add config loading with auto-creation" ``` --- ### Task 3: Implement Azure Table Storage client **Files:** - Modify: `order_query/main.py` - [ ] **Step 1: Add dataclass for Order and query function** ```python from dataclasses import dataclass, field from datetime import datetime from azure.data.tables import TableServiceClient @dataclass class Order: order_id: str prod_id: str = "" prod_count: int = 0 prod_price: float = 0.0 revenue: float = 0.0 state: int = 0 order_time: str = "" receive_time: str = "" platform_order: str = "" custom_data: str = "" ver: int = 0 @property def state_text(self) -> str: return "Payed" if self.state == 0 else "Recived" def query_orders(config: Config, user_id: str) -> list[Order]: client = TableServiceClient.from_connection_string(config.connection_string) table = client.get_table_client(config.table_name) entities = table.query_entities(query_filter=f"PartitionKey eq '{user_id}'") orders = [] for entity in entities: order = Order( order_id=entity.get("RowKey", ""), prod_id=entity.get("prodId", ""), prod_count=entity.get("prodCount", 0) or 0, prod_price=entity.get("prodPrice", 0.0) or 0.0, revenue=entity.get("revenue", 0.0) or 0.0, state=entity.get("state", 0), order_time=str(entity.get("OrderTime", "")), receive_time=str(entity.get("ReceiveTime", "")), platform_order=entity.get("platformOrder", ""), custom_data=entity.get("customData", ""), ver=entity.get("ver", 0) or 0, ) orders.append(order) return orders ``` - [ ] **Step 2: Commit** ```bash git add order_query/main.py git commit -m "feat: add Azure Table Storage query client" ``` --- ### Task 4: Implement Textual TUI **Files:** - Modify: `order_query/main.py` - [ ] **Step 1: Implement the 3-screen Textual app** The app has three screens: 1. `InputScreen` — text input for userId 2. `OrderListScreen` — DataTable showing all orders 3. `OrderDetailScreen` — detailed view of a single order ```python from textual.app import App, ComposeResult from textual.widgets import Header, Footer, Input, Button, DataTable, Static from textual.screen import ModalScreen from textual.containers import Horizontal, Vertical, VerticalScroll class InputScreen(ModalScreen[str]): """Screen for entering userId.""" def compose(self) -> ComposeResult: yield Vertical( Static("Enter User ID:", classes="label"), Input(placeholder="userId", id="user_id_input"), Horizontal( Button("Query", variant="primary", id="query_btn"), Button("Quit", variant="default", id="quit_btn"), classes="buttons", ), classes="input-container", ) def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == "query_btn": user_id = self.query_one("#user_id_input", Input).value.strip() if user_id: self.dismiss(user_id) elif event.button.id == "quit_btn": self.app.exit() class OrderListScreen(ModalScreen[str | None]): """Screen showing order list as a DataTable.""" def __init__(self, orders: list[Order]) -> None: super().__init__() self.orders = orders def compose(self) -> ComposeResult: yield Vertical( Static(f"Found {len(self.orders)} orders. Select one for details, or press Q to quit."), DataTable(id="order_table"), Button("Back", id="back_btn"), classes="list-container", ) def on_mount(self) -> None: table = self.query_one("#order_table", DataTable) table.add_columns( "Order ID", "Product ID", "Count", "Price", "Revenue", "State", "Order Time" ) for order in self.orders: table.add_row( order.order_id, order.prod_id, str(order.prod_count), f"{order.prod_price:.2f}", f"{order.revenue:.2f}", order.state_text, order.order_time[:19] if order.order_time else "", key=order.order_id, ) def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None: order_id = event.row_key.value self.dismiss(order_id) def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == "back_btn": self.dismiss(None) class OrderDetailScreen(ModalScreen[None]): """Screen showing order details.""" def __init__(self, order: Order) -> None: super().__init__() self.order = order def compose(self) -> ComposeResult: o = self.order detail_text = ( f"[bold]Order ID:[/bold] {o.order_id}\n" f"[bold]Product ID:[/bold] {o.prod_id}\n" f"[bold]Product Count:[/bold] {o.prod_count}\n" f"[bold]Product Price:[/bold] {o.prod_price:.2f}\n" f"[bold]Revenue:[/bold] {o.revenue:.2f}\n" f"[bold]State:[/bold] {o.state_text}\n" f"[bold]Order Time:[/bold] {o.order_time[:19] if o.order_time else 'N/A'}\n" f"[bold]Receive Time:[/bold] {o.receive_time[:19] if o.receive_time else 'N/A'}\n" f"[bold]Platform Order:[/bold] {o.platform_order or 'N/A'}\n" f"[bold]Version:[/bold] {o.ver}\n" f"[bold]Custom Data:[/bold] {o.custom_data or 'N/A'}" ) yield Vertical( Static(detail_text, id="detail_text"), Button("Back", id="back_btn"), classes="detail-container", ) def on_button_pressed(self, event: Button.Pressed) -> None: self.dismiss(None) class OrderQueryApp(App): """Main TUI application.""" CSS = """ .input-container { align: center middle; width: 60; height: auto; padding: 2; border: round $primary; } .label { text-align: center; margin-bottom: 1; text-style: bold; } .buttons { margin-top: 1; align: center middle; } .list-container { padding: 1; } .detail-container { align: center middle; width: 80; height: auto; padding: 2; border: round $primary; } #detail_text { margin-bottom: 1; } #back_btn { margin-top: 1; } """ BINDINGS = [("q", "quit", "Quit")] def on_mount(self) -> None: self.push_screen(InputScreen(), self.handle_user_id) def handle_user_id(self, user_id: str | None) -> None: if user_id is None: return try: config = load_config() orders = query_orders(config, user_id) except Exception as e: self.notify(str(e), severity="error") self.push_screen(InputScreen(), self.handle_user_id) return if not orders: self.notify("No orders found for this user", severity="warning") self.push_screen(InputScreen(), self.handle_user_id) return self.push_screen(OrderListScreen(orders), self.handle_order_selected) def handle_order_selected(self, order_id: str | None) -> None: if order_id is None: self.push_screen(InputScreen(), self.handle_user_id) return order = next((o for o in self._current_orders if o.order_id == order_id), None) if order: self.push_screen(OrderDetailScreen(order), lambda _: self.show_list_again()) def show_list_again(self) -> None: self.push_screen(OrderListScreen(self._current_orders), self.handle_order_selected) def handle_user_id(self, user_id: str | None) -> None: if user_id is None: return try: config = load_config() orders = query_orders(config, user_id) except Exception as e: self.notify(str(e), severity="error") self.push_screen(InputScreen(), self.handle_user_id) return self._current_orders = orders if not orders: self.notify("No orders found for this user", severity="warning") self.push_screen(InputScreen(), self.handle_user_id) return self.push_screen(OrderListScreen(orders), self.handle_order_selected) ``` - [ ] **Step 2: Add main entry point** ```python def main(): app = OrderQueryApp() app.run() ``` - [ ] **Step 3: Commit** ```bash git add order_query/main.py git commit -m "feat: implement Textual TUI with 3-screen flow" ``` --- ### Task 5: Integration test and polish - [ ] **Step 1: Test the tool runs** Run: `uv run python -m order_query.main` Expected: TUI window opens with userId input prompt - [ ] **Step 2: Verify config auto-creation** Delete `~/.order-query/config.toml` if exists, run the tool again, confirm it creates the file and exits with instructions. - [ ] **Step 3: Commit** ```bash git add -A git commit -m "chore: verify integration and polish" ```