Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
229 lines
10 KiB
Markdown
229 lines
10 KiB
Markdown
---
|
|
phase: 07-dashboard-nav-polish
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- imptune/api/pages.py
|
|
- imptune/templates/dashboard.html
|
|
- imptune/templates/packages.html
|
|
- tests/test_static.py
|
|
autonomous: true
|
|
requirements: []
|
|
must_haves:
|
|
truths:
|
|
- "GET /packages returns 200 with a list of printers that have drivers assigned"
|
|
- "Dashboard shows the 5 most recently created printers from the database"
|
|
- "Dashboard shows the 5 most recently created printers with a driver assigned (recent packages)"
|
|
- "All existing tests remain green after changes"
|
|
artifacts:
|
|
- path: "imptune/templates/packages.html"
|
|
provides: "Packages listing page template"
|
|
contains: "extends \"base.html\""
|
|
- path: "imptune/api/pages.py"
|
|
provides: "/packages route and fixed dashboard queries"
|
|
exports: ["packages_page"]
|
|
- path: "tests/test_static.py"
|
|
provides: "Integration tests for /packages and dashboard data"
|
|
contains: "test_packages_returns_200"
|
|
key_links:
|
|
- from: "imptune/api/pages.py"
|
|
to: "imptune/db/models.py"
|
|
via: "Printer.select().order_by(Printer.created_at.desc()).limit(5)"
|
|
pattern: "Printer\\.select\\(\\)"
|
|
- from: "imptune/api/pages.py"
|
|
to: "imptune/templates/packages.html"
|
|
via: "TemplateResponse name='packages.html'"
|
|
pattern: "packages\\.html"
|
|
- from: "imptune/templates/base.html"
|
|
to: "imptune/api/pages.py"
|
|
via: "nav link href='/packages' resolves to packages_page route"
|
|
pattern: "/packages"
|
|
---
|
|
|
|
<objective>
|
|
Fix the broken /packages nav link (404) and wire the dashboard to show real data from the database.
|
|
|
|
Purpose: Close two visible integration gaps -- the /packages nav link returns 404 and the dashboard always shows empty lists despite real data existing in the database.
|
|
Output: Working /packages page, dashboard with live recent printers and recent packages queries, integration tests proving all three success criteria.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
|
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/07-dashboard-nav-polish/07-RESEARCH.md
|
|
|
|
<interfaces>
|
|
<!-- Key types and contracts the executor needs. -->
|
|
|
|
From imptune/db/models.py:
|
|
```python
|
|
class Client(BaseModel):
|
|
name = CharField(unique=True)
|
|
created_at = DateTimeField(default=datetime.utcnow)
|
|
|
|
class Driver(BaseModel):
|
|
sha256 = CharField(unique=True, index=True)
|
|
original_filename = CharField()
|
|
driver_desc = CharField(null=True)
|
|
uploaded_at = DateTimeField(default=datetime.utcnow)
|
|
|
|
class Printer(BaseModel):
|
|
name = CharField()
|
|
ip_address = CharField()
|
|
port_name = CharField()
|
|
client = ForeignKeyField(Client, null=True, backref="printers")
|
|
driver = ForeignKeyField(Driver, null=True, backref="printers")
|
|
created_at = DateTimeField(default=datetime.utcnow)
|
|
```
|
|
|
|
From imptune/api/pages.py (established pattern):
|
|
```python
|
|
router = APIRouter()
|
|
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
|
|
|
|
# All routes use deferred imports inside function body
|
|
# All routes use: templates.TemplateResponse(request=request, name="...", context={...})
|
|
# All queries use: list(Model.select()...) — never Model.get()
|
|
```
|
|
|
|
From tests/conftest.py:
|
|
```python
|
|
@pytest.fixture
|
|
def client(tmp_data_dir):
|
|
from imptune.main import app
|
|
with TestClient(app) as c:
|
|
yield c
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Add integration tests for /packages route and dashboard data</name>
|
|
<files>tests/test_static.py</files>
|
|
<behavior>
|
|
- test_packages_returns_200: GET /packages returns status 200
|
|
- test_dashboard_shows_recent_printers: Create 2 Printer records in DB, GET / response body contains both printer names
|
|
- test_dashboard_shows_recent_packages: Create 2 Printer records (one with driver, one without), GET / response body contains the driver-assigned printer name in the packages section but not the driverless one
|
|
</behavior>
|
|
<action>
|
|
Add three test functions to the existing `tests/test_static.py` file. All tests use the `client` fixture from conftest.py.
|
|
|
|
For `test_packages_returns_200`: Simple GET /packages, assert status_code == 200.
|
|
|
|
For `test_dashboard_shows_recent_printers`:
|
|
1. Import Client, Driver, Printer from imptune.db.models
|
|
2. Create 2 Printer records with distinct names (e.g. "TestPrinter-Alpha", "TestPrinter-Beta") using Printer.create(name=..., ip_address="10.0.0.1", port_name="IP_10.0.0.1")
|
|
3. GET /, assert both printer names appear in response.text
|
|
4. Assert "No printers configured yet" NOT in response.text
|
|
|
|
For `test_dashboard_shows_recent_packages`:
|
|
1. Create a Driver record: Driver.create(sha256="abc123", original_filename="test.zip", size_bytes=1000, driver_desc='["TestDriver"]')
|
|
2. Create Printer with driver assigned: Printer.create(name="PkgPrinter-Assigned", ip_address="10.0.0.2", port_name="IP_10.0.0.2", driver=driver)
|
|
3. Create Printer without driver: Printer.create(name="PkgPrinter-NoDriver", ip_address="10.0.0.3", port_name="IP_10.0.0.3")
|
|
4. GET /, assert "PkgPrinter-Assigned" appears in response.text
|
|
5. Assert "No packages exported yet" NOT in response.text
|
|
|
|
These tests will FAIL initially (RED) because /packages returns 404 and dashboard hardcodes empty lists.
|
|
</action>
|
|
<verify>
|
|
<automated>cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && pytest tests/test_static.py -x 2>&1 | tail -20</automated>
|
|
</verify>
|
|
<done>Three new test functions exist in test_static.py. test_packages_returns_200 fails with 404, dashboard tests fail because response contains "No printers configured yet" / "No packages exported yet".</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Add /packages route, fix dashboard queries, create packages.html template</name>
|
|
<files>imptune/api/pages.py, imptune/templates/packages.html, imptune/templates/dashboard.html</files>
|
|
<action>
|
|
**imptune/api/pages.py** — Two changes:
|
|
|
|
1. Fix the `dashboard` function (lines 16-25). Replace hardcoded empty lists with real queries using deferred imports inside the function body:
|
|
```python
|
|
@router.get("/", response_class=HTMLResponse)
|
|
def dashboard(request: Request):
|
|
from imptune.db.models import Printer
|
|
|
|
recent_printers = list(
|
|
Printer.select().order_by(Printer.created_at.desc()).limit(5)
|
|
)
|
|
recent_packages = list(
|
|
Printer.select()
|
|
.where(Printer.driver.is_null(False))
|
|
.order_by(Printer.created_at.desc())
|
|
.limit(5)
|
|
)
|
|
return templates.TemplateResponse(
|
|
request=request,
|
|
name="dashboard.html",
|
|
context={
|
|
"recent_printers": recent_printers,
|
|
"recent_packages": recent_packages,
|
|
},
|
|
)
|
|
```
|
|
|
|
2. Add a new `/packages` route at the end of the file. Follow the exact same pattern as `printers_page` — deferred imports, LEFT_OUTER joins, list() wrapper:
|
|
```python
|
|
@router.get("/packages", response_class=HTMLResponse)
|
|
def packages_page(request: Request):
|
|
from imptune.db.models import Client, Driver, Printer
|
|
|
|
printers = list(
|
|
Printer.select(Printer, Client, Driver)
|
|
.join(Client, JOIN.LEFT_OUTER)
|
|
.switch(Printer)
|
|
.join(Driver, JOIN.LEFT_OUTER)
|
|
.where(Printer.driver.is_null(False))
|
|
.order_by(Printer.name)
|
|
)
|
|
return templates.TemplateResponse(
|
|
request=request,
|
|
name="packages.html",
|
|
context={"printers": printers},
|
|
)
|
|
```
|
|
|
|
**imptune/templates/packages.html** — Create new file extending base.html. Show a table of printers that have drivers assigned, with columns: Printer Name (linked to /printers/{id}), Client, Driver, and download links for Intune (.intunewin) and NinjaRMM (ZIP). Use the existing download URL patterns: `/printers/{id}/packages/intunewin` and `/printers/{id}/packages/ninja`. Show an empty state message if no package-ready printers exist. Use Pico CSS table styling (no custom classes needed beyond what base.html provides).
|
|
|
|
**imptune/templates/dashboard.html** — Update the printer list items to be clickable links. Change:
|
|
- `<li>{{ printer.name }} — {{ printer.ip_address }}</li>` to `<li><a href="/printers/{{ printer.id }}">{{ printer.name }}</a> — {{ printer.ip_address }}</li>`
|
|
- For recent_packages section, change `<li>{{ package }}</li>` to `<li><a href="/printers/{{ package.id }}">{{ package.name }}</a>{% if package.client_id %} — {{ package.client.name }}{% endif %}</li>` (the variable is a Printer object, not a string)
|
|
- Also update the quick-action links: "New Printer" href to "/printers", "Upload Driver" href to "/drivers", "Export Package" href to "/packages" — remove aria-disabled="true" from all three.
|
|
</action>
|
|
<verify>
|
|
<automated>cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && pytest tests/ -x 2>&1 | tail -20</automated>
|
|
</verify>
|
|
<done>GET /packages returns 200 with a table of package-ready printers. Dashboard shows real recent printers and recent packages from the database. All tests in the full suite pass including the 3 new tests from Task 1.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
1. `pytest tests/ -x` — full test suite green
|
|
2. `pytest tests/test_static.py::test_packages_returns_200 -x` — /packages route works
|
|
3. `pytest tests/test_static.py::test_dashboard_shows_recent_printers -x` — dashboard shows real printers
|
|
4. `pytest tests/test_static.py::test_dashboard_shows_recent_packages -x` — dashboard shows real packages
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- GET /packages returns 200 and renders a page listing printers with drivers assigned
|
|
- Dashboard recent_printers section shows real Printer records from the database
|
|
- Dashboard recent_packages section shows Printer records that have a driver assigned
|
|
- Full test suite (pytest tests/ -x) passes with zero failures
|
|
- No regressions in existing tests
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/07-dashboard-nav-polish/07-01-SUMMARY.md`
|
|
</output>
|