Coverage for app/views/routes/search.py: 0%
34 statements
« prev ^ index » next coverage.py v7.6.10, created at 2026-06-04 22:29 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2026-06-04 22:29 +0000
1from typing import Annotated
3from fastapi import APIRouter, Header, Request
4from fastapi.responses import HTMLResponse
5from sqlalchemy import text
7from app.core.db import read_engine
8from app.core.logger import logger
9from app.core.templates import templates
10from app.normalization import NormalizedPackageName
11from app.utils import parse_query_params, validate_package
13router: APIRouter = APIRouter()
16@router.get("/package-search-input", response_class=HTMLResponse)
17def get_search_input(
18 request: Request,
19 package_name: NormalizedPackageName,
20 url: Annotated[str, Header(alias="HX-Current-URL")],
21) -> HTMLResponse:
22 query_params = parse_query_params(url)
24 if query_params.error:
25 logger.warning(query_params.error)
26 return HTMLResponse(status_code=422, content=query_params.error)
28 error_message = ""
29 is_valid_submission = True
31 if package_name != "":
32 if package_name in query_params.packages:
33 is_valid_submission = False
34 error_message = f"'{package_name}' already selected"
35 elif not validate_package(package_name):
36 is_valid_submission = False
37 error_message = f"'{package_name}' not found on PyPI"
39 return templates.TemplateResponse(
40 request=request,
41 name="fragments/package_search_input.html",
42 context={
43 "package_name": package_name,
44 "is_valid_submission": is_valid_submission,
45 "error_message": error_message,
46 },
47 )
50@router.get("/package-search-results", response_class=HTMLResponse)
51def get_search_results(
52 request: Request,
53 package_name: NormalizedPackageName,
54) -> HTMLResponse:
55 logger.info(f"Searching for packages matching: '{package_name}'")
56 with read_engine.connect() as conn:
57 result = conn.execute(
58 text("""
59 select
60 package_name,
61 package_summary
62 from
63 pypi_packages
64 where
65 package_name like :package_name
66 order by
67 package_name limit 10
68 """),
69 {"package_name": package_name + "%"},
70 )
71 packages = result.fetchall()
73 logger.info(f"Found {len(packages)} results for search term '{package_name}'")
75 return templates.TemplateResponse(
76 "pages/package_search_results.html",
77 {"request": request, "packages": packages, "package_name": package_name},
78 )