Coverage for src/lilbee/server/routes/analyze.py: 100%
28 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-11-06 08:17 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-11-06 08:17 +0000
1"""Analyze routes: the streamed run, the tip state, and hiding the tip."""
3from __future__ import annotations
5import asyncio
7from litestar import Router, get, post
8from litestar.exceptions import ValidationException
9from litestar.openapi.datastructures import ResponseSpec
10from litestar.response import Stream
11from litestar.status_codes import HTTP_200_OK
13from lilbee.app.analyze import AnalyzeRequest, server_directory, validate_request
14from lilbee.server.handlers import analyze as handlers
15from lilbee.server.handlers.sse import SSE_MEDIA_TYPE
16from lilbee.server.models import AnalyzeRequestBody, AnalyzeResponse, AnalyzeStateResponse
19def _request(data: AnalyzeRequestBody) -> AnalyzeRequest:
20 return AnalyzeRequest(
21 directory=server_directory(data.directory),
22 apply=data.apply,
23 save=data.save,
24 target=data.target,
25 )
28@post(
29 "/api/analyze",
30 media_type=SSE_MEDIA_TYPE,
31 status_code=HTTP_200_OK,
32 responses={
33 HTTP_200_OK: ResponseSpec(
34 data_container=AnalyzeResponse,
35 media_type=SSE_MEDIA_TYPE,
36 generate_examples=False,
37 description="Progress events, then a done event whose data is this report.",
38 )
39 },
40)
41async def analyze_route(data: AnalyzeRequestBody | None = None) -> Stream:
42 """Read the corpus or a folder and recommend a profile, streaming ``analyze`` progress.
44 The ``done`` event carries the report; a refused request answers 400 before any file
45 is read. A client that disconnects before the save starts cancels the run; nothing is saved.
46 """
47 try:
48 request = _request(data if data is not None else AnalyzeRequestBody())
49 await asyncio.to_thread(validate_request, request)
50 except ValueError as exc:
51 raise ValidationException(str(exc)) from exc
52 return Stream(handlers.analyze_stream(request), media_type=SSE_MEDIA_TYPE)
55@get("/api/analyze/state")
56async def analyze_state_route() -> AnalyzeStateResponse:
57 """Whether the project was analyzed, hid the tip, and would see the tip now."""
58 return await handlers.analyze_state()
61@post("/api/analyze/dismiss", status_code=HTTP_200_OK)
62async def analyze_dismiss_route() -> AnalyzeStateResponse:
63 """Hide the analyze tip for this project."""
64 return await handlers.dismiss_tip()
67analyze_router = Router(
68 path="/",
69 route_handlers=[analyze_route, analyze_state_route, analyze_dismiss_route],
70)